Floating point comparison inconsistencies of the geometric types
There are those macros defined for the built-in geometric types:
#define EPSILON 1.0E-06
#define FPzero(A) (fabs(A) <= EPSILON)
#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
#define FPlt(A,B) ((B) - (A) > EPSILON)
#define FPle(A,B) ((A) - (B) <= EPSILON)
#define FPgt(A,B) ((A) - (B) > EPSILON)
#define FPge(A,B) ((B) - (A) <= EPSILON)
with this warning:
* XXX These routines were not written by a numerical analyst.
Most of the geometric operators use those macros for comparison, but
those do not:
* polygon << polygon
* polygon &< polygon
* polygon &> polygon
* polygon >> polygon
* polygon <<| polygon
* polygon &<| polygon
* polygon |&> polygon
* polygon |>> polygon
* box @> point
* point <@ box
* lseg <@ box
* circle @> point
* point <@ circle
This is really a bug that needs to be fixed one way or another. I think
that it is better to fix it by removing the macros all together. I
am not sure how useful they are in practice. I haven't seen anyone
complaining about the above operators not using the macros. Though,
people often complain about the ones using the macros and the problems
caused by them.
The hackers evidently don't like the macros, either. That should be
why they are not used on the new operators. What annoys me most about
this situation is the inconsistency blocks demonstrating our indexes.
Because of this, we had to rip out point type support from BRIN
inclusion operator class which could be useful to PostGIS.
Fixing it has been discussed many times before [1]/messages/by-id/D90A5A6C612A39408103E6ECDD77B8290FD4E3@voyager.corporate.connx.com[2]/messages/by-id/4A7C2C4B.5020508@netspace.net.au[3]/messages/by-id/12549.1346111029@sss.pgh.pa.us[4]/messages/by-id/20150512181307.GJ2523@alvh.no-ip.org with
no result. Here is my plan to fix the situation covering the problems
around it:
1) Reimplement some operators to avoid divisions
The attached patch does it on some of the line operators.
2) Use exact comparison on everywhere except the operators fuzzy
comparison is certainly required
The attach patch changes all operators except some "lseg" operators.
"lseg" stores two points on a line. Most of the operations done on it
are lossy. I don't see a problem treating them differently, those
operators are very unlikely to be index supported.
3) Check numbers for underflow and overflow
I am thinking to use CHECKFLOATVAL on utils/adt/float.c, but it is not
exposed outside at the moment. Would it be okay to create a new header
file utils/float.h to increase code sharing between float and geometric
types?
4) Implement relative fuzzy comparison for the remaining operators
It is better to completely get rid of those macros while we are on it.
I think we can get away by implementing just a single function for fuzzy
equality, not for other comparisons. I am inclined to put it to
utils/adt/float.c. Is this a good idea?
Tom Lane commented on the function posted to the list [1]/messages/by-id/D90A5A6C612A39408103E6ECDD77B8290FD4E3@voyager.corporate.connx.com on 2002:
Not like that. Perhaps use a fraction of the absolute value of the
one with larger absolute value. As-is it's hard to tell how FLT_EPSILON
is measured.
I cannot image how the function would look like. I would appreciate
any guidance.
5) Implement default hash operator class for all geometric types
This would solve most complained problem of those types allowing them
to used with DISTINCT and GROUP BY.
6) Implement default btree operator class at least for the point type
This would let the point type to be used with ORDER BY. It is
relatively straight forward to implement it for the point type. Is it
a good idea to somehow implement it for other types?
7) Add GiST index support for missing cross type operators
Currently only contained by operators are supported by an out-of-range
strategy number. I think we can make the operator classes much nicer
by allowing really cross type operator families.
Comments?
[1]: /messages/by-id/D90A5A6C612A39408103E6ECDD77B8290FD4E3@voyager.corporate.connx.com
[2]: /messages/by-id/4A7C2C4B.5020508@netspace.net.au
[3]: /messages/by-id/12549.1346111029@sss.pgh.pa.us
[4]: /messages/by-id/20150512181307.GJ2523@alvh.no-ip.org
Attachments:
0001-geo-ops-fpcomp-v01.patchtext/x-diff; charset=utf-8; name=0001-geo-ops-fpcomp-v01.patchDownload
From aa79e331595860489cdbbdce2d5f35a7d1f33783 Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Wed, 25 May 2016 17:53:19 +0200
Subject: [PATCH] Stop using FP macros on geo_ops.c
---
src/backend/access/gist/gistproc.c | 17 +-
src/backend/access/spgist/spgkdtreeproc.c | 24 +--
src/backend/utils/adt/geo_ops.c | 312 +++++++++++++++---------------
src/backend/utils/adt/geo_spgist.c | 24 +--
src/include/utils/geo_decls.h | 16 --
src/test/regress/expected/point.out | 8 +-
6 files changed, 193 insertions(+), 208 deletions(-)
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index e8213e2..7fb4437 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -1295,44 +1295,41 @@ computeDistance(bool isLeaf, BOX *box, Point *point)
static bool
gist_point_consistent_internal(StrategyNumber strategy,
bool isLeaf, BOX *key, Point *query)
{
bool result = false;
switch (strategy)
{
case RTLeftStrategyNumber:
- result = FPlt(key->low.x, query->x);
+ result = key->low.x < query->x;
break;
case RTRightStrategyNumber:
- result = FPgt(key->high.x, query->x);
+ result = key->high.x > query->x;
break;
case RTAboveStrategyNumber:
- result = FPgt(key->high.y, query->y);
+ result = key->high.y > query->y;
break;
case RTBelowStrategyNumber:
- result = FPlt(key->low.y, query->y);
+ result = key->low.y < query->y;
break;
case RTSameStrategyNumber:
if (isLeaf)
{
/* key.high must equal key.low, so we can disregard it */
- result = (FPeq(key->low.x, query->x) &&
- FPeq(key->low.y, query->y));
+ result = key->low.x == query->x && key->low.y == query->y;
}
else
{
- result = (FPle(query->x, key->high.x) &&
- FPge(query->x, key->low.x) &&
- FPle(query->y, key->high.y) &&
- FPge(query->y, key->low.y));
+ result = query->x <= key->high.x && query->x >= key->low.x &&
+ query->y <= key->high.y && query->y >= key->low.y;
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
result = false; /* keep compiler quiet */
break;
}
return result;
}
diff --git a/src/backend/access/spgist/spgkdtreeproc.c b/src/backend/access/spgist/spgkdtreeproc.c
index 1ab9335..4b3be5d 100644
--- a/src/backend/access/spgist/spgkdtreeproc.c
+++ b/src/backend/access/spgist/spgkdtreeproc.c
@@ -175,72 +175,72 @@ spg_kd_inner_consistent(PG_FUNCTION_ARGS)
which = (1 << 1) | (1 << 2);
for (i = 0; i < in->nkeys; i++)
{
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
BOX *boxQuery;
switch (in->scankeys[i].sk_strategy)
{
case RTLeftStrategyNumber:
- if ((in->level % 2) != 0 && FPlt(query->x, coord))
+ if ((in->level % 2) != 0 && query->x < coord)
which &= (1 << 1);
break;
case RTRightStrategyNumber:
- if ((in->level % 2) != 0 && FPgt(query->x, coord))
+ if ((in->level % 2) != 0 && query->x > coord)
which &= (1 << 2);
break;
case RTSameStrategyNumber:
if ((in->level % 2) != 0)
{
- if (FPlt(query->x, coord))
+ if (query->x < coord)
which &= (1 << 1);
- else if (FPgt(query->x, coord))
+ else if (query->x > coord)
which &= (1 << 2);
}
else
{
- if (FPlt(query->y, coord))
+ if (query->y < coord)
which &= (1 << 1);
- else if (FPgt(query->y, coord))
+ else if (query->y > coord)
which &= (1 << 2);
}
break;
case RTBelowStrategyNumber:
- if ((in->level % 2) == 0 && FPlt(query->y, coord))
+ if ((in->level % 2) == 0 && query->y < coord)
which &= (1 << 1);
break;
case RTAboveStrategyNumber:
- if ((in->level % 2) == 0 && FPgt(query->y, coord))
+ if ((in->level % 2) == 0 && query->y > coord)
which &= (1 << 2);
break;
case RTContainedByStrategyNumber:
/*
* For this operator, the query is a box not a point. We
* cheat to the extent of assuming that DatumGetPointP won't
* do anything that would be bad for a pointer-to-box.
*/
boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
if ((in->level % 2) != 0)
{
- if (FPlt(boxQuery->high.x, coord))
+ if (boxQuery->high.x < coord)
which &= (1 << 1);
- else if (FPgt(boxQuery->low.x, coord))
+ else if (boxQuery->low.x > coord)
which &= (1 << 2);
}
else
{
- if (FPlt(boxQuery->high.y, coord))
+ if (boxQuery->high.y < coord)
which &= (1 << 1);
- else if (FPgt(boxQuery->low.y, coord))
+ else if (boxQuery->low.y > coord)
which &= (1 << 2);
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d",
in->scankeys[i].sk_strategy);
break;
}
if (which == 0)
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 657bcee..7e553e6 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -499,246 +499,246 @@ box_copy(BOX *box)
*---------------------------------------------------------*/
/* box_same - are two boxes identical?
*/
Datum
box_same(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box1->high.x, box2->high.x) &&
- FPeq(box1->low.x, box2->low.x) &&
- FPeq(box1->high.y, box2->high.y) &&
- FPeq(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(box1->high.x == box2->high.x &&
+ box1->low.x == box2->low.x &&
+ box1->high.y == box2->high.y &&
+ box1->low.y == box2->low.y);
}
/* box_overlap - does box1 overlap box2?
*/
Datum
box_overlap(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
PG_RETURN_BOOL(box_ov(box1, box2));
}
static bool
box_ov(BOX *box1, BOX *box2)
{
- return (FPle(box1->low.x, box2->high.x) &&
- FPle(box2->low.x, box1->high.x) &&
- FPle(box1->low.y, box2->high.y) &&
- FPle(box2->low.y, box1->high.y));
+ return (box1->low.x <= box2->high.x &&
+ box2->low.x <= box1->high.x &&
+ box1->low.y <= box2->high.y &&
+ box2->low.y <= box1->high.y);
}
/* box_left - is box1 strictly left of box2?
*/
Datum
box_left(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
+ PG_RETURN_BOOL(box1->high.x < box2->low.x);
}
/* box_overleft - is the right edge of box1 at or left of
* the right edge of box2?
*
* This is "less than or equal" for the end of a time range,
* when time ranges are stored as rectangles.
*/
Datum
box_overleft(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
+ PG_RETURN_BOOL(box1->high.x <= box2->high.x);
}
/* box_right - is box1 strictly right of box2?
*/
Datum
box_right(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
+ PG_RETURN_BOOL(box1->low.x > box2->high.x);
}
/* box_overright - is the left edge of box1 at or right of
* the left edge of box2?
*
* This is "greater than or equal" for time ranges, when time ranges
* are stored as rectangles.
*/
Datum
box_overright(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
+ PG_RETURN_BOOL(box1->low.x >= box2->low.x);
}
/* box_below - is box1 strictly below box2?
*/
Datum
box_below(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(box1->high.y < box2->low.y);
}
/* box_overbelow - is the upper edge of box1 at or below
* the upper edge of box2?
*/
Datum
box_overbelow(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
+ PG_RETURN_BOOL(box1->high.y <= box2->high.y);
}
/* box_above - is box1 strictly above box2?
*/
Datum
box_above(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(box1->low.y > box2->high.y);
}
/* box_overabove - is the lower edge of box1 at or above
* the lower edge of box2?
*/
Datum
box_overabove(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(box1->low.y >= box2->low.y);
}
/* box_contained - is box1 contained by box2?
*/
Datum
box_contained(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x) &&
- FPge(box1->low.x, box2->low.x) &&
- FPle(box1->high.y, box2->high.y) &&
- FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(box1->high.x <= box2->high.x &&
+ box1->low.x >= box2->low.x &&
+ box1->high.y <= box2->high.y &&
+ box1->low.y >= box2->low.y);
}
/* box_contain - does box1 contain box2?
*/
Datum
box_contain(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(box1->high.x >= box2->high.x &&
+ box1->low.x <= box2->low.x &&
+ box1->high.y >= box2->high.y &&
+ box1->low.y <= box2->low.y);
}
/* box_positionop -
* is box1 entirely {above,below} box2?
*
* box_below_eq and box_above_eq are obsolete versions that (probably
* erroneously) accept the equal-boundaries case. Since these are not
* in sync with the box_left and box_right code, they are deprecated and
* not supported in the PG 8.1 rtree operator class extension.
*/
Datum
box_below_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(box1->high.y <= box2->low.y);
}
Datum
box_above_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(box1->low.y >= box2->high.y);
}
/* box_relop - is area(box1) relop area(box2), within
* our accuracy constraint?
*/
Datum
box_lt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(box_ar(box1) < box_ar(box2));
}
Datum
box_gt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(box_ar(box1) > box_ar(box2));
}
Datum
box_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(box_ar(box1) == box_ar(box2));
}
Datum
box_le(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(box_ar(box1) <= box_ar(box2));
}
Datum
box_ge(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(box_ar(box1) >= box_ar(box2));
}
/*----------------------------------------------------------
* "Arithmetic" operators on boxes.
*---------------------------------------------------------*/
/* box_area - returns the area of the box.
*/
Datum
@@ -927,29 +927,29 @@ line_in(PG_FUNCTION_ARGS)
s = str;
while (isspace((unsigned char) *s))
s++;
if (*s == '{')
{
if (!line_decode(s + 1, str, line))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"line", str)));
- if (FPzero(line->A) && FPzero(line->B))
+ if (line->A == 0 && line->B == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: A and B cannot both be zero")));
}
else
{
path_decode(s, true, 2, &(lseg.p[0]), &isopen, NULL, "line", str);
- if (FPeq(lseg.p[0].x, lseg.p[1].x) && FPeq(lseg.p[0].y, lseg.p[1].y))
+ if (lseg.p[0].x == lseg.p[1].x && lseg.p[0].y == lseg.p[1].y)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: must be two distinct points")));
line_construct_pts(line, &lseg.p[0], &lseg.p[1]);
}
PG_RETURN_LINE_P(line);
}
@@ -1029,31 +1029,31 @@ line_construct_pm(Point *pt, double m)
return result;
}
/*
* Fill already-allocated LINE struct from two points on the line
*/
static void
line_construct_pts(LINE *line, Point *pt1, Point *pt2)
{
- if (FPeq(pt1->x, pt2->x))
+ if (pt1->x == pt2->x)
{ /* vertical */
/* use "x = C" */
line->A = -1;
line->B = 0;
line->C = pt1->x;
#ifdef GEODEBUG
printf("line_construct_pts- line is vertical\n");
#endif
}
- else if (FPeq(pt1->y, pt2->y))
+ else if (pt1->y == pt2->y)
{ /* horizontal */
/* use "y = C" */
line->A = 0;
line->B = -1;
line->C = pt1->y;
#ifdef GEODEBUG
printf("line_construct_pts- line is horizontal\n");
#endif
}
else
@@ -1101,75 +1101,77 @@ line_intersect(PG_FUNCTION_ARGS)
LinePGetDatum(l1),
LinePGetDatum(l2))));
}
Datum
line_parallel(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->B));
+ if (l1->B == 0)
+ PG_RETURN_BOOL(l2->B == 0);
- PG_RETURN_BOOL(FPeq(l2->A, l1->A * (l2->B / l1->B)));
+ PG_RETURN_BOOL(l2->A * l1->B == l1->A * l2->B);
}
Datum
line_perp(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- if (FPzero(l1->A))
- PG_RETURN_BOOL(FPzero(l2->B));
- else if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->A));
+ if (l1->A == 0)
+ PG_RETURN_BOOL(l2->B == 0);
+ else if (l1->B == 0)
+ PG_RETURN_BOOL(l2->A == 0);
- PG_RETURN_BOOL(FPeq(((l1->A * l2->B) / (l1->B * l2->A)), -1.0));
+ PG_RETURN_BOOL(l1->A * l2->B == l1->B * l2->A * -1.0);
}
Datum
line_vertical(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->B));
+ PG_RETURN_BOOL(line->B == 0);
}
Datum
line_horizontal(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->A));
+ PG_RETURN_BOOL(line->A == 0);
}
Datum
line_eq(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
double k;
- if (!FPzero(l2->A))
- k = l1->A / l2->A;
- else if (!FPzero(l2->B))
- k = l1->B / l2->B;
- else if (!FPzero(l2->C))
- k = l1->C / l2->C;
- else
- k = 1.0;
+ if (l1->A == l2->A)
+ PG_RETURN_BOOL(l1->B == l2->B && l1->C == l2->C);
- PG_RETURN_BOOL(FPeq(l1->A, k * l2->A) &&
- FPeq(l1->B, k * l2->B) &&
- FPeq(l1->C, k * l2->C));
+ if (l1->A != 0)
+ PG_RETURN_BOOL(l1->A * l2->B == l2->A * l1->B &&
+ l1->A * l2->C == l2->A * l1->C);
+ if (l2->B != 0)
+ PG_RETURN_BOOL(l1->B * l2->A == l2->B * l1->A &&
+ l1->B * l2->C == l2->B * l1->C);
+ if (l2->C != 0)
+ PG_RETURN_BOOL(l1->C * l2->A == l2->C * l1->A &&
+ l1->C * l2->B == l2->C * l1->B);
+
+ PG_RETURN_BOOL(false);
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* line_distance()
* Distance between two lines.
*/
@@ -1178,21 +1180,21 @@ line_distance(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
float8 result;
Point *tmp;
if (!DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))))
PG_RETURN_FLOAT8(0.0);
- if (FPzero(l1->B)) /* vertical? */
+ if (l1->B == 0) /* vertical? */
PG_RETURN_FLOAT8(fabs(l1->C - l2->C));
tmp = point_construct(0.0, l1->C);
result = dist_pl_internal(tmp, l2);
PG_RETURN_FLOAT8(result);
}
/* line_interpt()
* Point where two lines l1, l2 intersect (if any)
*/
Datum
@@ -1224,26 +1226,26 @@ line_interpt_internal(LINE *l1, LINE *l2)
/*
* NOTE: if the lines are identical then we will find they are parallel
* and report "no intersection". This is a little weird, but since
* there's no *unique* intersection, maybe it's appropriate behavior.
*/
if (DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))))
return NULL;
- if (FPzero(l1->B)) /* l1 vertical? */
+ if (l1->B == 0) /* l1 vertical? */
{
x = l1->C;
y = (l2->A * x + l2->C);
}
- else if (FPzero(l2->B)) /* l2 vertical? */
+ else if (l2->B == 0) /* l2 vertical? */
{
x = l2->C;
y = (l1->A * x + l1->C);
}
else
{
x = (l1->C - l2->C) / (l2->A - l1->A);
y = (l1->A * x + l1->C);
}
result = point_construct(x, y);
@@ -1816,84 +1818,84 @@ point_copy(Point *pt)
* that results may, strictly speaking, be a lie (unless
* EPSILON = 0.0).
*---------------------------------------------------------*/
Datum
point_left(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(pt1->x < pt2->x);
}
Datum
point_right(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(pt1->x > pt2->x);
}
Datum
point_above(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(pt1->y > pt2->y);
}
Datum
point_below(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(pt1->y < pt2->y);
}
Datum
point_vert(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
+ PG_RETURN_BOOL(pt1->x == pt2->x);
}
Datum
point_horiz(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(pt1->y == pt2->y);
}
Datum
point_eq(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(pt1->x == pt2->x && pt1->y == pt2->y);
}
Datum
point_ne(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPne(pt1->x, pt2->x) || FPne(pt1->y, pt2->y));
+ PG_RETURN_BOOL(pt1->x != pt2->x || pt1->y != pt2->y);
}
/*----------------------------------------------------------
* "Arithmetic" operators on points.
*---------------------------------------------------------*/
Datum
point_distance(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
@@ -1918,21 +1920,21 @@ point_slope(PG_FUNCTION_ARGS)
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
PG_RETURN_FLOAT8(point_sl(pt1, pt2));
}
double
point_sl(Point *pt1, Point *pt2)
{
- return (FPeq(pt1->x, pt2->x)
+ return (pt1->x == pt2->x
? (double) DBL_MAX
: (pt1->y - pt2->y) / (pt1->x - pt2->x));
}
/***********************************************************************
**
** Routines for 2D line segments.
**
***********************************************************************/
@@ -2072,20 +2074,21 @@ lseg_intersect_internal(LSEG *l1, LSEG *l2)
retval = false;
return retval;
}
Datum
lseg_parallel(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
+ /* The slopes are lossy. We cannot exact match them. */
PG_RETURN_BOOL(FPeq(point_sl(&l1->p[0], &l1->p[1]),
point_sl(&l2->p[0], &l2->p[1])));
}
/* lseg_perp()
* Determine if two line segments are perpendicular.
*
* This code did not get the correct answer for
* '((0,0),(0,1))'::lseg ?-| '((0,0),(1,0))'::lseg
* So, modified it to check explicitly for slope of vertical line
@@ -2099,107 +2102,108 @@ lseg_perp(PG_FUNCTION_ARGS)
LSEG *l2 = PG_GETARG_LSEG_P(1);
double m1,
m2;
m1 = point_sl(&(l1->p[0]), &(l1->p[1]));
m2 = point_sl(&(l2->p[0]), &(l2->p[1]));
#ifdef GEODEBUG
printf("lseg_perp- slopes are %g and %g\n", m1, m2);
#endif
- if (FPzero(m1))
- PG_RETURN_BOOL(FPeq(m2, DBL_MAX));
- else if (FPzero(m2))
- PG_RETURN_BOOL(FPeq(m1, DBL_MAX));
+ if (m1 == 0)
+ PG_RETURN_BOOL(m2 == DBL_MAX);
+ else if (m2 == 0)
+ PG_RETURN_BOOL(m1 == DBL_MAX);
- PG_RETURN_BOOL(FPeq(m1 / m2, -1.0));
+ /* The slopes are lossy. We cannot exact match them. */
+ PG_RETURN_BOOL(FPeq(m1, m2 * -1.0));
}
Datum
lseg_vertical(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
+ PG_RETURN_BOOL(lseg->p[0].x == lseg->p[1].x);
}
Datum
lseg_horizontal(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
+ PG_RETURN_BOOL(lseg->p[0].y == lseg->p[1].y);
}
Datum
lseg_eq(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(l1->p[0].x, l2->p[0].x) &&
- FPeq(l1->p[0].y, l2->p[0].y) &&
- FPeq(l1->p[1].x, l2->p[1].x) &&
- FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(l1->p[0].x == l2->p[0].x &&
+ l1->p[0].y == l2->p[0].y &&
+ l1->p[1].x == l2->p[1].x &&
+ l1->p[1].y == l2->p[1].y);
}
Datum
lseg_ne(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(!FPeq(l1->p[0].x, l2->p[0].x) ||
- !FPeq(l1->p[0].y, l2->p[0].y) ||
- !FPeq(l1->p[1].x, l2->p[1].x) ||
- !FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(l1->p[0].x != l2->p[0].x ||
+ l1->p[0].y != l2->p[0].y ||
+ l1->p[1].x != l2->p[1].x ||
+ l1->p[1].y != l2->p[1].y);
}
Datum
lseg_lt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(point_dt(&l1->p[0], &l1->p[1]) <
+ point_dt(&l2->p[0], &l2->p[1]));
}
Datum
lseg_le(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(point_dt(&l1->p[0], &l1->p[1]) <=
+ point_dt(&l2->p[0], &l2->p[1]));
}
Datum
lseg_gt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(point_dt(&l1->p[0], &l1->p[1]) >
+ point_dt(&l2->p[0], &l2->p[1]));
}
Datum
lseg_ge(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(point_dt(&l1->p[0], &l1->p[1]) >=
+ point_dt(&l2->p[0], &l2->p[1]));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* lseg_distance -
* If two segments don't intersect, then the closest
* point will be from one of the endpoints to the other
@@ -2278,21 +2282,22 @@ lseg_interpt_internal(LSEG *l1, LSEG *l2)
if (!on_ps_internal(result, l1) ||
!on_ps_internal(result, l2))
{
pfree(result);
return NULL;
}
/*
* If there is an intersection, then check explicitly for matching
* endpoints since there may be rounding effects with annoying lsb
- * residue. - tgl 1997-07-09
+ * residue. We are fuzzy matching them to be compatible with
+ * on_ps_internal().
*/
if ((FPeq(l1->p[0].x, l2->p[0].x) && FPeq(l1->p[0].y, l2->p[0].y)) ||
(FPeq(l1->p[0].x, l2->p[1].x) && FPeq(l1->p[0].y, l2->p[1].y)))
{
result->x = l1->p[0].x;
result->y = l1->p[0].y;
}
else if ((FPeq(l1->p[1].x, l2->p[0].x) && FPeq(l1->p[1].y, l2->p[0].y)) ||
(FPeq(l1->p[1].x, l2->p[1].x) && FPeq(l1->p[1].y, l2->p[1].y)))
{
@@ -2714,27 +2719,27 @@ Datum
close_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
Point *result;
LINE *tmp;
double invm;
result = (Point *) palloc(sizeof(Point));
- if (FPzero(line->B)) /* vertical? */
+ if (line->B == 0) /* vertical? */
{
result->x = line->C;
result->y = pt->y;
PG_RETURN_POINT_P(result);
}
- if (FPzero(line->A)) /* horizontal? */
+ if (line->A == 0) /* horizontal? */
{
result->x = pt->x;
result->y = line->C;
PG_RETURN_POINT_P(result);
}
/* drop a perpendicular and find the intersection point */
/* invert and flip the sign on the slope to get a perpendicular */
invm = line->B / line->A;
tmp = line_construct_pm(pt, invm);
@@ -2759,51 +2764,51 @@ close_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
Point *result = NULL;
LINE *tmp;
double invm;
int xh,
yh;
#ifdef GEODEBUG
- printf("close_sp:pt->x %f pt->y %f\nlseg(0).x %f lseg(0).y %f lseg(1).x %f lseg(1).y %f\n",
+ printf("close_ps:spt->x %f pt->y %f\nlseg(0).x %f lseg(0).y %f lseg(1).x %f lseg(1).y %f\n",
pt->x, pt->y, lseg->p[0].x, lseg->p[0].y,
lseg->p[1].x, lseg->p[1].y);
#endif
/* xh (or yh) is the index of upper x( or y) end point of lseg */
/* !xh (or !yh) is the index of lower x( or y) end point of lseg */
xh = lseg->p[0].x < lseg->p[1].x;
yh = lseg->p[0].y < lseg->p[1].y;
- if (FPeq(lseg->p[0].x, lseg->p[1].x)) /* vertical? */
+ if (lseg->p[0].x == lseg->p[1].x) /* vertical? */
{
#ifdef GEODEBUG
printf("close_ps- segment is vertical\n");
#endif
/* first check if point is below or above the entire lseg. */
if (pt->y < lseg->p[!yh].y)
result = point_copy(&lseg->p[!yh]); /* below the lseg */
else if (pt->y > lseg->p[yh].y)
result = point_copy(&lseg->p[yh]); /* above the lseg */
if (result != NULL)
PG_RETURN_POINT_P(result);
/* point lines along (to left or right) of the vertical lseg. */
result = (Point *) palloc(sizeof(Point));
result->x = lseg->p[0].x;
result->y = pt->y;
PG_RETURN_POINT_P(result);
}
- else if (FPeq(lseg->p[0].y, lseg->p[1].y)) /* horizontal? */
+ else if (lseg->p[0].y == lseg->p[1].y) /* horizontal? */
{
#ifdef GEODEBUG
printf("close_ps- segment is horizontal\n");
#endif
/* first check if point is left or right of the entire lseg. */
if (pt->x < lseg->p[!xh].x)
result = point_copy(&lseg->p[!xh]); /* left of the lseg */
else if (pt->x > lseg->p[xh].x)
result = point_copy(&lseg->p[xh]); /* right of the lseg */
if (result != NULL)
@@ -3118,40 +3123,41 @@ close_lb(PG_FUNCTION_ARGS)
/* on_pl -
* Does the point satisfy the equation?
*/
Datum
on_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
- PG_RETURN_BOOL(FPzero(line->A * pt->x + line->B * pt->y + line->C));
+ PG_RETURN_BOOL(line->A * pt->x + line->B * pt->y + line->C == 0);
}
/* on_ps -
- * Determine colinearity by detecting a triangle inequality.
+ * Determine collinearity by detecting a triangle inequality.
* This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
*/
Datum
on_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
PG_RETURN_BOOL(on_ps_internal(pt, lseg));
}
static bool
on_ps_internal(Point *pt, LSEG *lseg)
{
+ /* The distances are lossy. We cannot exact match them. */
return FPeq(point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1]),
point_dt(&lseg->p[0], &lseg->p[1]));
}
Datum
on_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
BOX *box = PG_GETARG_BOX_P(1);
@@ -3191,22 +3197,21 @@ on_ppath(PG_FUNCTION_ARGS)
b;
/*-- OPEN --*/
if (!path->closed)
{
n = path->npts - 1;
a = point_dt(pt, &path->p[0]);
for (i = 0; i < n; i++)
{
b = point_dt(pt, &path->p[i + 1]);
- if (FPeq(a + b,
- point_dt(&path->p[i], &path->p[i + 1])))
+ if (a + b == point_dt(&path->p[i], &path->p[i + 1]))
PG_RETURN_BOOL(true);
a = b;
}
PG_RETURN_BOOL(false);
}
/*-- CLOSED --*/
PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
}
@@ -3810,21 +3815,21 @@ poly_overlap(PG_FUNCTION_ARGS)
static bool
touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
{
/* point a is on s, b is not */
LSEG t;
t.p[0] = *a;
t.p[1] = *b;
-#define POINTEQ(pt1, pt2) (FPeq((pt1)->x, (pt2)->x) && FPeq((pt1)->y, (pt2)->y))
+#define POINTEQ(pt1, pt2) ((pt1)->x == (pt2)->x && (pt1)->y == (pt2)->y)
if (POINTEQ(a, s->p))
{
if (on_ps_internal(s->p + 1, &t))
return lseg_inside_poly(b, s->p + 1, poly, start);
}
else if (POINTEQ(a, s->p + 1))
{
if (on_ps_internal(s->p, &t))
return lseg_inside_poly(b, s->p, poly, start);
}
@@ -4648,216 +4653,218 @@ circle_send(PG_FUNCTION_ARGS)
*---------------------------------------------------------*/
/* circles identical?
*/
Datum
circle_same(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle1->radius, circle2->radius) &&
- FPeq(circle1->center.x, circle2->center.x) &&
- FPeq(circle1->center.y, circle2->center.y));
+ PG_RETURN_BOOL(circle1->radius == circle2->radius &&
+ circle1->center.x == circle2->center.x &&
+ circle1->center.y == circle2->center.y);
}
/* circle_overlap - does circle1 overlap circle2?
*/
Datum
circle_overlap(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
- circle1->radius + circle2->radius));
+ PG_RETURN_BOOL(point_dt(&circle1->center, &circle2->center) <=
+ circle1->radius + circle2->radius);
}
/* circle_overleft - is the right edge of circle1 at or left of
* the right edge of circle2?
*/
Datum
circle_overleft(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((circle1->center.x + circle1->radius),
- (circle2->center.x + circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.x + circle1->radius <=
+ circle2->center.x + circle2->radius);
}
/* circle_left - is circle1 strictly left of circle2?
*/
Datum
circle_left(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt((circle1->center.x + circle1->radius),
- (circle2->center.x - circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.x + circle1->radius <
+ circle2->center.x - circle2->radius);
}
/* circle_right - is circle1 strictly right of circle2?
*/
Datum
circle_right(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt((circle1->center.x - circle1->radius),
- (circle2->center.x + circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.x - circle1->radius >
+ circle2->center.x + circle2->radius);
}
/* circle_overright - is the left edge of circle1 at or right of
* the left edge of circle2?
*/
Datum
circle_overright(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge((circle1->center.x - circle1->radius),
- (circle2->center.x - circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.x - circle1->radius >=
+ circle2->center.x - circle2->radius);
}
/* circle_contained - is circle1 contained by circle2?
*/
Datum
circle_contained(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle1->radius), circle2->radius));
+ PG_RETURN_BOOL(point_dt(&circle1->center, &circle2->center) +
+ circle1->radius <= circle2->radius);
}
/* circle_contain - does circle1 contain circle2?
*/
Datum
circle_contain(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle2->radius), circle1->radius));
+ PG_RETURN_BOOL(point_dt(&circle1->center, &circle2->center) +
+ circle2->radius <= circle1->radius);
}
/* circle_below - is circle1 strictly below circle2?
*/
Datum
circle_below(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt((circle1->center.y + circle1->radius),
- (circle2->center.y - circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.y + circle1->radius <
+ circle2->center.y - circle2->radius);
}
/* circle_above - is circle1 strictly above circle2?
*/
Datum
circle_above(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt((circle1->center.y - circle1->radius),
- (circle2->center.y + circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.y - circle1->radius >
+ circle2->center.y + circle2->radius);
}
/* circle_overbelow - is the upper edge of circle1 at or below
* the upper edge of circle2?
*/
Datum
circle_overbelow(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((circle1->center.y + circle1->radius),
- (circle2->center.y + circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.y + circle1->radius <=
+ circle2->center.y + circle2->radius);
}
/* circle_overabove - is the lower edge of circle1 at or above
* the lower edge of circle2?
*/
Datum
circle_overabove(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge((circle1->center.y - circle1->radius),
- (circle2->center.y - circle2->radius)));
+ PG_RETURN_BOOL(circle1->center.y - circle1->radius >=
+ circle2->center.y - circle2->radius);
}
/* circle_relop - is area(circle1) relop area(circle2), within
* our accuracy constraint?
*/
Datum
circle_eq(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(circle_ar(circle1) == circle_ar(circle2));
}
Datum
circle_ne(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(circle_ar(circle1) != circle_ar(circle2));
}
Datum
circle_lt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(circle_ar(circle1) < circle_ar(circle2));
}
Datum
circle_gt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(circle_ar(circle1) > circle_ar(circle2));
}
Datum
circle_le(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(circle_ar(circle1) <= circle_ar(circle2));
}
Datum
circle_ge(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(circle_ar(circle1) >= circle_ar(circle2));
}
/*----------------------------------------------------------
* "Arithmetic" operators on circles.
*---------------------------------------------------------*/
static CIRCLE *
circle_copy(CIRCLE *circle)
{
@@ -5146,21 +5153,21 @@ circle_poly(PG_FUNCTION_ARGS)
{
int32 npts = PG_GETARG_INT32(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
POLYGON *poly;
int base_size,
size;
int i;
double angle;
double anglestep;
- if (FPzero(circle->radius))
+ if (circle->radius == 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert circle with radius zero to polygon")));
if (npts < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must request at least 2 points")));
base_size = sizeof(poly->p[0]) * npts;
@@ -5305,110 +5312,107 @@ point_inside(Point *p, int npts, Point *plist)
* Wow, that is one confusing API, but it is used above, and when summed,
* can tell is if a point is in a polygon.
*/
static int
lseg_crossing(double x, double y, double prev_x, double prev_y)
{
double z;
int y_sign;
- if (FPzero(y))
+ if (y == 0)
{ /* y == 0, on X axis */
- if (FPzero(x)) /* (x,y) is (0,0)? */
+ if (x == 0) /* (x,y) is (0,0)? */
return POINT_ON_POLYGON;
- else if (FPgt(x, 0))
+ else if (x > 0)
{ /* x > 0 */
- if (FPzero(prev_y)) /* y and prev_y are zero */
+ if (prev_y == 0) /* y and prev_y are zero */
/* prev_x > 0? */
- return FPgt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
- return FPlt(prev_y, 0) ? 1 : -1;
+ return prev_x > 0 ? 0 : POINT_ON_POLYGON;
+ return prev_y < 0 ? 1 : -1;
}
else
{ /* x < 0, x not on positive X axis */
- if (FPzero(prev_y))
+ if (prev_y == 0)
/* prev_x < 0? */
- return FPlt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
+ return prev_x < 0 ? 0 : POINT_ON_POLYGON;
return 0;
}
}
else
{ /* y != 0 */
/* compute y crossing direction from previous point */
- y_sign = FPgt(y, 0) ? 1 : -1;
+ y_sign = y > 0 ? 1 : -1;
- if (FPzero(prev_y))
+ if (prev_y == 0)
/* previous point was on X axis, so new point is either off or on */
- return FPlt(prev_x, 0) ? 0 : y_sign;
- else if (FPgt(y_sign * prev_y, 0))
+ return prev_x < 0 ? 0 : y_sign;
+ else if (y_sign * prev_y > 0)
/* both above or below X axis */
return 0; /* same sign */
else
{ /* y and prev_y cross X-axis */
- if (FPge(x, 0) && FPgt(prev_x, 0))
+ if (x >= 0 && prev_x > 0)
/* both non-negative so cross positive X-axis */
return 2 * y_sign;
- if (FPlt(x, 0) && FPle(prev_x, 0))
+ if (x < 0 && prev_x <= 0)
/* both non-positive so do not cross positive X-axis */
return 0;
/* x and y cross axises, see URL above point_inside() */
z = (x - prev_x) * y - (y - prev_y) * x;
- if (FPzero(z))
+ if (z == 0)
return POINT_ON_POLYGON;
- return FPgt((y_sign * z), 0) ? 0 : 2 * y_sign;
+ return y_sign * z > 0 ? 0 : 2 * y_sign;
}
}
}
static bool
plist_same(int npts, Point *p1, Point *p2)
{
int i,
ii,
j;
/* find match for first point */
for (i = 0; i < npts; i++)
{
- if ((FPeq(p2[i].x, p1[0].x))
- && (FPeq(p2[i].y, p1[0].y)))
+ if (p2[i].x == p1[0].x && p2[i].y == p1[0].y)
{
/* match found? then look forward through remaining points */
for (ii = 1, j = i + 1; ii < npts; ii++, j++)
{
if (j >= npts)
j = 0;
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (p2[j].x != p1[ii].x || p2[j].y != p1[ii].y)
{
#ifdef GEODEBUG
printf("plist_same- %d failed forward match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after forward match\n", ii, npts);
#endif
if (ii == npts)
return TRUE;
/* match not found forwards? then look backwards */
for (ii = 1, j = i - 1; ii < npts; ii++, j--)
{
if (j < 0)
j = (npts - 1);
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (p2[j].x != p1[ii].x || p2[j].y != p1[ii].y)
{
#ifdef GEODEBUG
printf("plist_same- %d failed reverse match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after reverse match\n", ii, npts);
#endif
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 83d509e..4b82f89 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -225,80 +225,80 @@ nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant)
else
next_rect_box->range_box_y.right.high = centroid->right.high;
return next_rect_box;
}
/* Can any range from range_box overlap with this argument? */
static bool
overlap2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->low) &&
- FPle(range_box->left.low, query->high);
+ return range_box->right.high >= query->low &&
+ range_box->left.low <= query->high;
}
/* Can any rectangle from rect_box overlap with this argument? */
static bool
overlap4D(RectBox *rect_box, RangeBox *query)
{
return overlap2D(&rect_box->range_box_x, &query->left) &&
overlap2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box contain this argument? */
static bool
contain2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->high) &&
- FPle(range_box->left.low, query->low);
+ return range_box->right.high >= query->high &&
+ range_box->left.low <= query->low;
}
/* Can any rectangle from rect_box contain this argument? */
static bool
contain4D(RectBox *rect_box, RangeBox * query)
{
return contain2D(&rect_box->range_box_x, &query->left) &&
contain2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box be contained by this argument? */
static bool
contained2D(RangeBox *range_box, Range *query)
{
- return FPle(range_box->left.low, query->high) &&
- FPge(range_box->left.high, query->low) &&
- FPle(range_box->right.low, query->high) &&
- FPge(range_box->right.high, query->low);
+ return range_box->left.low <= query->high &&
+ range_box->left.high >= query->low &&
+ range_box->right.low <= query->high &&
+ range_box->right.high >= query->low;
}
/* Can any rectangle from rect_box be contained by this argument? */
static bool
contained4D(RectBox *rect_box, RangeBox *query)
{
return contained2D(&rect_box->range_box_x, &query->left) &&
contained2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box to be lower than this argument? */
static bool
lower2D(RangeBox *range_box, Range *query)
{
- return FPlt(range_box->left.low, query->low) &&
- FPlt(range_box->right.low, query->low);
+ return range_box->left.low < query->low &&
+ range_box->right.low < query->low;
}
/* Can any range from range_box to be higher than this argument? */
static bool
higher2D(RangeBox *range_box, Range *query)
{
- return FPgt(range_box->left.high, query->high) &&
- FPgt(range_box->right.high, query->high);
+ return range_box->left.high > query->high &&
+ range_box->right.high > query->high;
}
/* Can any rectangle from rect_box be left of this argument? */
static bool
left4D(RectBox *rect_box, RangeBox *query)
{
return lower2D(&rect_box->range_box_x, &query->left);
}
/* Can any rectangle from rect_box does not extend the right of this argument? */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index acf3202..163bc57 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -25,37 +25,21 @@
#include "fmgr.h"
/*--------------------------------------------------------------------
* Useful floating point utilities and constants.
*-------------------------------------------------------------------*/
#define EPSILON 1.0E-06
-#ifdef EPSILON
-#define FPzero(A) (fabs(A) <= EPSILON)
#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
-#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
-#define FPlt(A,B) ((B) - (A) > EPSILON)
-#define FPle(A,B) ((A) - (B) <= EPSILON)
-#define FPgt(A,B) ((A) - (B) > EPSILON)
-#define FPge(A,B) ((B) - (A) <= EPSILON)
-#else
-#define FPzero(A) ((A) == 0)
-#define FPeq(A,B) ((A) == (B))
-#define FPne(A,B) ((A) != (B))
-#define FPlt(A,B) ((A) < (B))
-#define FPle(A,B) ((A) <= (B))
-#define FPgt(A,B) ((A) > (B))
-#define FPge(A,B) ((A) >= (B))
-#endif
#define HYPOT(A, B) pg_hypot(A, B)
/*---------------------------------------------------------------------
* Point - (x,y)
*-------------------------------------------------------------------*/
typedef struct
{
double x,
y;
diff --git a/src/test/regress/expected/point.out b/src/test/regress/expected/point.out
index bfc0962..6319652 100644
--- a/src/test/regress/expected/point.out
+++ b/src/test/regress/expected/point.out
@@ -249,49 +249,49 @@ SELECT '' AS three, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS dista
CREATE TEMP TABLE point_gist_tbl(f1 point);
INSERT INTO point_gist_tbl SELECT '(0,0)' FROM generate_series(0,1000);
CREATE INDEX point_gist_tbl_index ON point_gist_tbl USING gist (f1);
INSERT INTO point_gist_tbl VALUES ('(0.0000009,0.0000009)');
SET enable_seqscan TO true;
SET enable_indexscan TO false;
SET enable_bitmapscan TO false;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
SET enable_seqscan TO false;
SET enable_indexscan TO true;
SET enable_bitmapscan TO true;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
2.7.4 (Apple Git-66)
On Fri, May 27, 2016 at 6:43 AM, Emre Hasegeli <emre@hasegeli.com> wrote:
There are those macros defined for the built-in geometric types:
#define EPSILON 1.0E-06
#define FPzero(A) (fabs(A) <= EPSILON)
#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
#define FPlt(A,B) ((B) - (A) > EPSILON)
#define FPle(A,B) ((A) - (B) <= EPSILON)
#define FPgt(A,B) ((A) - (B) > EPSILON)
#define FPge(A,B) ((B) - (A) <= EPSILON)with this warning:
* XXX These routines were not written by a numerical analyst.
I agree that those macros looks like a pile of suck. It's unclear to
me what purpose they're trying to accomplish, but regardless of what
it is, it's hard for me to believe that they are accomplishing it.
Whether 1.0E-06 is a correct fuzz factor presumably depends greatly on
the scale of the number; e.g. if the values are all like "3" it's
probably fine but if they are like "37142816124856" it's probably not
enough fuzz and if they are all like ".00000004" it's probably way too
much fuzz.
Figuring out what to do about it is harder. Your proposal seems to be
to remove them except where we need the fuzzy behavior, which doesn't
sound unreasonable, but I don't personally understand why we need it
in some places and not others. It would be good if some of the people
who are more numerically inclined than I am (and hate floats less, but
then that's everyone) could jump in here.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jun 1, 2016 at 8:08 AM, Robert Haas <robertmhaas@gmail.com> wrote:
On Fri, May 27, 2016 at 6:43 AM, Emre Hasegeli <emre@hasegeli.com> wrote:
There are those macros defined for the built-in geometric types:
#define EPSILON 1.0E-06
#define FPzero(A) (fabs(A) <= EPSILON)
#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
#define FPlt(A,B) ((B) - (A) > EPSILON)
#define FPle(A,B) ((A) - (B) <= EPSILON)
#define FPgt(A,B) ((A) - (B) > EPSILON)
#define FPge(A,B) ((B) - (A) <= EPSILON)with this warning:
* XXX These routines were not written by a numerical analyst.
I agree that those macros looks like a pile of suck.
+1
It's unclear to
me what purpose they're trying to accomplish, but regardless of what
it is, it's hard for me to believe that they are accomplishing it.
Whether 1.0E-06 is a correct fuzz factor presumably depends greatly on
the scale of the number; e.g. if the values are all like "3" it's
probably fine but if they are like "37142816124856" it's probably not
enough fuzz and if they are all like ".00000004" it's probably way too
much fuzz.
Also, it's a pretty lame heuristic. It doesn't really eliminate
*any* problems; it just aims to make them less frequent by shifting
the edges around. In doing so, it creates whole new classes of
problems:
test=# \set A '''(1.9999996,1.9999996),(1,1)''::box'
test=# \set B '''(2,2),(1,1)''::box'
test=# \set C '''(2.0000004,2.0000004),(1,1)''::box'
test=# select :A = :B, :B = :C, :A = :C;
?column? | ?column? | ?column?
----------+----------+----------
t | t | f
(1 row)
Is the benefit we get from the macros worth destroying the
transitive property of the comparison operators on these types?
Figuring out what to do about it is harder. Your proposal seems to be
to remove them except where we need the fuzzy behavior, which doesn't
sound unreasonable, but I don't personally understand why we need it
in some places and not others.
+1
My first inclination is to remove those macros in version 10, but
it would be good to hear from some people using these types on what
the impact of that would be.
--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Kevin Grittner <kgrittn@gmail.com> writes:
On Wed, Jun 1, 2016 at 8:08 AM, Robert Haas <robertmhaas@gmail.com> wrote:
Figuring out what to do about it is harder. Your proposal seems to be
to remove them except where we need the fuzzy behavior, which doesn't
sound unreasonable, but I don't personally understand why we need it
in some places and not others.
+1
My first inclination is to remove those macros in version 10, but
it would be good to hear from some people using these types on what
the impact of that would be.
As I understand it, the key problem is that tests like "is point on line"
would basically never succeed except in the most trivial cases, because of
roundoff error. That's not very nice, and it might cascade to larger
problems like object-containment tests failing unexpectedly. We would
need to go through all the geometric operations and figure out where that
kind of gotcha is significant and what we can do about it. Seems like a
fair amount of work :-(. If somebody's willing to do that kind of
investigation, then sure, but I don't think just blindly removing these
macros is going to lead to anything good.
Also, I suppose this means that Robert promises not to make any of his
usual complaints about breaking compatibility? Because we certainly
would be.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 6/1/16 9:27 AM, Tom Lane wrote:
Kevin Grittner <kgrittn@gmail.com> writes:
On Wed, Jun 1, 2016 at 8:08 AM, Robert Haas <robertmhaas@gmail.com> wrote:
Figuring out what to do about it is harder. Your proposal seems to be
to remove them except where we need the fuzzy behavior, which doesn't
sound unreasonable, but I don't personally understand why we need it
in some places and not others.+1
My first inclination is to remove those macros in version 10, but
it would be good to hear from some people using these types on what
the impact of that would be.As I understand it, the key problem is that tests like "is point on line"
would basically never succeed except in the most trivial cases, because of
roundoff error. That's not very nice, and it might cascade to larger
problems like object-containment tests failing unexpectedly. We would
need to go through all the geometric operations and figure out where that
kind of gotcha is significant and what we can do about it. Seems like a
fair amount of work :-(. If somebody's willing to do that kind of
investigation, then sure, but I don't think just blindly removing these
macros is going to lead to anything good.
I suspect another wrinkle here is that in the GIS world a single point
can be represented it multiple reference/coordinate systems, and it
would have different values in each of them. AIUI the transforms between
those systems can be rather complicated if different projection methods
are involved. I don't know if PostGIS depends on what these macros are
doing or not. If it doesn't, perhaps it would be sufficient to lop of
the last few bits of the significand. ISTM that'd be much better than
what the macros currently do.
BTW, I suspect the macro values were chosen specifically for dealing
with LAT/LONG.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jun 1, 2016 at 7:52 AM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
I suspect another wrinkle here is that in the GIS world a single point can
be represented it multiple reference/coordinate systems, and it would have
different values in each of them. AIUI the transforms between those systems
can be rather complicated if different projection methods are involved. I
don't know if PostGIS depends on what these macros are doing or not. If it
doesn't, perhaps it would be sufficient to lop of the last few bits of the
significand. ISTM that'd be much better than what the macros currently do.
We don't depend on these, we have our own :/
The real answer for a GIS system is to have an explicit tolerance
parameter for calculations like distance/touching/containment, but
unfortunately we didn't do that so now we have our own
compatibility/boil the ocean problem if we ever wanted/were funded to
add one.
P.
BTW, I suspect the macro values were chosen specifically for dealing with
LAT/LONG.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 06/01/2016 07:52 AM, Jim Nasby wrote:
On 6/1/16 9:27 AM, Tom Lane wrote:
Kevin Grittner <kgrittn@gmail.com> writes:
On Wed, Jun 1, 2016 at 8:08 AM, Robert Haas <robertmhaas@gmail.com>
wrote:
Figuring out what to do about it is harder. Your proposal seems
to be
to remove them except where we need the fuzzy behavior, which
doesn't
sound unreasonable, but I don't personally understand why we need it
in some places and not others.+1
My first inclination is to remove those macros in version 10, but
it would be good to hear from some people using these types on what
the impact of that would be.As I understand it, the key problem is that tests like "is point on line"
would basically never succeed except in the most trivial cases,
because of
roundoff error. That's not very nice, and it might cascade to larger
problems like object-containment tests failing unexpectedly. We would
need to go through all the geometric operations and figure out where that
kind of gotcha is significant and what we can do about it. Seems like a
fair amount of work :-(. If somebody's willing to do that kind of
investigation, then sure, but I don't think just blindly removing these
macros is going to lead to anything good.I suspect another wrinkle here is that in the GIS world a single point
can be represented it multiple reference/coordinate systems, and it
would have different values in each of them. AIUI the transforms between
those systems can be rather complicated if different projection methods
are involved. I don't know if PostGIS depends on what these macros are
doing or not. If it doesn't, perhaps it would be sufficient to lop of
the last few bits of the significand. ISTM that'd be much better than
what the macros currently do.
IIRC PostGIS uses a function from libgeos to do things like "point
equals" (ST_Equals). I've never looked at that source, but would be
unsurprised to find that it does something similar to this although
probably also more sophisticated.
(looks) yes -- ST_Equals calls GEOSEquals() after some sanity checking...
BTW, I suspect the macro values were chosen specifically for dealing
with LAT/LONG.
I think that is it exactly.
Joe
--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
On 6/1/16 10:03 AM, Paul Ramsey wrote:
We don't depend on these, we have our own :/
The real answer for a GIS system is to have an explicit tolerance
parameter for calculations like distance/touching/containment, but
unfortunately we didn't do that so now we have our own
compatibility/boil the ocean problem if we ever wanted/were funded to
add one.
Well it sounds like what's currently happening in Postgres is probably
going to change, so how might we structure that to help PostGIS? Would
simply lopping off the last few bits of the significand/mantissa work,
or is that not enough when different GRSes are involved?
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jun 1, 2016 at 8:59 AM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
On 6/1/16 10:03 AM, Paul Ramsey wrote:
We don't depend on these, we have our own :/
The real answer for a GIS system is to have an explicit tolerance
parameter for calculations like distance/touching/containment, but
unfortunately we didn't do that so now we have our own
compatibility/boil the ocean problem if we ever wanted/were funded to
add one.Well it sounds like what's currently happening in Postgres is probably going
to change, so how might we structure that to help PostGIS? Would simply
lopping off the last few bits of the significand/mantissa work, or is that
not enough when different GRSes are involved?
PostGIS doesn't look at all at what the PgSQL geotypes do, so go
forward w/o fear. Tolerance in geo world is more than vertex rounding
though, it's things like saying that when distance(pt,line) < epsilon
then distance(pt,line) == 0, or similarly for shape touching, etc. One
of the things people find annoying about postgis is that
ST_Intersects(ST_Intersection(a, b), a) can come out as false (a
derived point at a crossing of lines may not exactly intersect either
of the input lines), which is a direct result of our use of exact math
for the boolean intersects test.
Anyways, go forth and do whatever makes sense for PgSQL
P
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Paul Ramsey <pramsey@cleverelephant.ca> writes:
One of the things people find annoying about postgis is that
ST_Intersects(ST_Intersection(a, b), a) can come out as false (a
derived point at a crossing of lines may not exactly intersect either
of the input lines), which is a direct result of our use of exact math
for the boolean intersects test.
That's an interesting comment, because it's more or less exactly the
type of failure that we could expect to get if we remove fuzzy comparisons
from the built-in types. How much of a problem is it in practice for
PostGIS users? Do you have any plans to change it?
Anyways, go forth and do whatever makes sense for PgSQL
I think we're trying to figure out what that is ...
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jun 1, 2016 at 10:27 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
As I understand it, the key problem is that tests like "is point on line"
would basically never succeed except in the most trivial cases, because of
roundoff error. That's not very nice, and it might cascade to larger
problems like object-containment tests failing unexpectedly. We would
need to go through all the geometric operations and figure out where that
kind of gotcha is significant and what we can do about it. Seems like a
fair amount of work :-(. If somebody's willing to do that kind of
investigation, then sure, but I don't think just blindly removing these
macros is going to lead to anything good.
Yeah, it does seem to need some research.
Also, I suppose this means that Robert promises not to make any of his
usual complaints about breaking compatibility? Because we certainly
would be.
Pot, meet Mr. Kettle!
Obviously, the inconvenience caused by any backward incompatibility
has to be balanced against the fact that the new behavior is
presumably better. But I stridently object to the accusation that of
the two of us I'm the one more concerned with backward-compatibility.
There may be some instances where I've had a more conservative
judgement than you about breaking user-facing stuff, but you've
blocked dozens of changes to the C API that would have enabled
meaningful extension development on the grounds that somebody might
complain when a future release changes the API! I think behavior
changes that users will notice are of vastly greater significance than
those which will only be observed by developers.
In this particular case, I think that the current behavior is pretty
stupid, and that the built-in geometric types are barely used,
possibly because they have stupid behavior. So I would be willing to
bet on a well-thought-out change in this area coming out to a net
positive.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
My progress so far is attached as 2 patches. First one introduces an
header file for adt/float.c. Second one refactors the geometric
operations.
I have removed the fuzzy comparison macros all together. It is very
hard to keep some of them and maintain consistency. Many operators
that would benefit from the fuzzy comparison is quite broken at the
moment [1]/messages/by-id/CAE2gYzw_-z=V2kh8QqFjenu=8MJXzOP44wRW=AzzeamrmTT1=Q@mail.gmail.com, anyway. I have also made the comparisons safe against NaN
[2]: /messages/by-id/28685.1468246504@sss.pgh.pa.us
[1]: /messages/by-id/CAE2gYzw_-z=V2kh8QqFjenu=8MJXzOP44wRW=AzzeamrmTT1=Q@mail.gmail.com
[2]: /messages/by-id/28685.1468246504@sss.pgh.pa.us
Attachments:
0001-float-header-v01.patchapplication/octet-stream; name=0001-float-header-v01.patchDownload
From a4f68bd1a925f001030097a8daebcbef68415a20 Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Sat, 28 May 2016 18:16:05 +0200
Subject: [PATCH 1/2] float-header-v01
---
src/backend/access/gist/gistget.c | 2 +-
src/backend/access/gist/gistproc.c | 56 ++---
src/backend/access/gist/gistutil.c | 2 +-
src/backend/utils/adt/float.c | 313 +++++++-------------------
src/backend/utils/adt/formatting.c | 8 +-
src/backend/utils/adt/geo_ops.c | 6 +-
src/backend/utils/adt/geo_spgist.c | 2 +-
src/backend/utils/adt/numeric.c | 1 +
src/backend/utils/adt/rangetypes_gist.c | 2 +-
src/backend/utils/adt/rangetypes_selfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_typanalyze.c | 2 +-
src/backend/utils/adt/timestamp.c | 1 +
src/backend/utils/misc/guc.c | 1 +
src/include/utils/builtins.h | 13 --
src/include/utils/float.h | 130 +++++++++++
15 files changed, 246 insertions(+), 295 deletions(-)
create mode 100644 src/include/utils/float.h
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 5ba7d0a..eda48ef 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -13,21 +13,21 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist_private.h"
#include "access/relscan.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "lib/pairingheap.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/memutils.h"
#include "utils/rel.h"
/*
* gistkillitems() -- set LP_DEAD state for items an indexscan caller has
* told us were killed.
*
* We re-read page here, so it's important to check page LSN. If the page
* has been modified since the last read (as determined by LSN), we cannot
* flag any entries because it is possible that the old entry was vacuumed
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index d47211a..c7ac033 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -15,73 +15,65 @@
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <math.h>
#include "access/gist.h"
#include "access/stratnum.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
static bool gist_box_leaf_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
static bool rtree_internal_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
/* Minimum accepted ratio of split */
#define LIMIT_RATIO 0.3
-/* Convenience macros for NaN-aware comparisons */
-#define FLOAT8_EQ(a,b) (float8_cmp_internal(a, b) == 0)
-#define FLOAT8_LT(a,b) (float8_cmp_internal(a, b) < 0)
-#define FLOAT8_LE(a,b) (float8_cmp_internal(a, b) <= 0)
-#define FLOAT8_GT(a,b) (float8_cmp_internal(a, b) > 0)
-#define FLOAT8_GE(a,b) (float8_cmp_internal(a, b) >= 0)
-#define FLOAT8_MAX(a,b) (FLOAT8_GT(a, b) ? (a) : (b))
-#define FLOAT8_MIN(a,b) (FLOAT8_LT(a, b) ? (a) : (b))
-
/**************************************************
* Box ops
**************************************************/
/*
* Calculates union of two boxes, a and b. The result is stored in *n.
*/
static void
rt_box_union(BOX *n, const BOX *a, const BOX *b)
{
- n->high.x = FLOAT8_MAX(a->high.x, b->high.x);
- n->high.y = FLOAT8_MAX(a->high.y, b->high.y);
- n->low.x = FLOAT8_MIN(a->low.x, b->low.x);
- n->low.y = FLOAT8_MIN(a->low.y, b->low.y);
+ n->high.x = FLOAT_MAX(a->high.x, b->high.x);
+ n->high.y = FLOAT_MAX(a->high.y, b->high.y);
+ n->low.x = FLOAT_MIN(a->low.x, b->low.x);
+ n->low.y = FLOAT_MIN(a->low.y, b->low.y);
}
/*
* Size of a BOX for penalty-calculation purposes.
* The result can be +Infinity, but not NaN.
*/
static double
size_box(const BOX *box)
{
/*
* Check for zero-width cases. Note that we define the size of a zero-
* by-infinity box as zero. It's important to special-case this somehow,
* as naively multiplying infinity by zero will produce NaN.
*
* The less-than cases should not happen, but if they do, say "zero".
*/
- if (FLOAT8_LE(box->high.x, box->low.x) ||
- FLOAT8_LE(box->high.y, box->low.y))
+ if (FLOAT_LE(box->high.x, box->low.x) ||
+ FLOAT_LE(box->high.y, box->low.y))
return 0.0;
/*
* We treat NaN as larger than +Infinity, so any distance involving a NaN
* and a non-NaN is infinite. Note the previous check eliminated the
* possibility that the low fields are NaNs.
*/
if (isnan(box->high.x) || isnan(box->high.y))
return get_float8_infinity();
return (box->high.x - box->low.x) * (box->high.y - box->low.y);
@@ -136,27 +128,27 @@ gist_box_consistent(PG_FUNCTION_ARGS)
query,
strategy));
}
/*
* Increase BOX b to include addon.
*/
static void
adjustBox(BOX *b, const BOX *addon)
{
- if (FLOAT8_LT(b->high.x, addon->high.x))
+ if (FLOAT_LT(b->high.x, addon->high.x))
b->high.x = addon->high.x;
- if (FLOAT8_GT(b->low.x, addon->low.x))
+ if (FLOAT_GT(b->low.x, addon->low.x))
b->low.x = addon->low.x;
- if (FLOAT8_LT(b->high.y, addon->high.y))
+ if (FLOAT_LT(b->high.y, addon->high.y))
b->high.y = addon->high.y;
- if (FLOAT8_GT(b->low.y, addon->low.y))
+ if (FLOAT_GT(b->low.y, addon->low.y))
b->low.y = addon->low.y;
}
/*
* The GiST Union method for boxes
*
* returns the minimal bounding box that encloses all the entries in entryvec
*/
Datum
gist_box_union(PG_FUNCTION_ARGS)
@@ -636,36 +628,36 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
i1 = 0;
i2 = 0;
rightLower = intervalsLower[i1].lower;
leftUpper = intervalsUpper[i2].lower;
while (true)
{
/*
* Find next lower bound of right group.
*/
while (i1 < nentries &&
- FLOAT8_EQ(rightLower, intervalsLower[i1].lower))
+ FLOAT_EQ(rightLower, intervalsLower[i1].lower))
{
- if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper))
+ if (FLOAT_LT(leftUpper, intervalsLower[i1].upper))
leftUpper = intervalsLower[i1].upper;
i1++;
}
if (i1 >= nentries)
break;
rightLower = intervalsLower[i1].lower;
/*
* Find count of intervals which anyway should be placed to the
* left group.
*/
while (i2 < nentries &&
- FLOAT8_LE(intervalsUpper[i2].upper, leftUpper))
+ FLOAT_LE(intervalsUpper[i2].upper, leftUpper))
i2++;
/*
* Consider found split.
*/
g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
}
/*
* Iterate over upper bound of left group finding greatest possible
@@ -673,35 +665,35 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
*/
i1 = nentries - 1;
i2 = nentries - 1;
rightLower = intervalsLower[i1].upper;
leftUpper = intervalsUpper[i2].upper;
while (true)
{
/*
* Find next upper bound of left group.
*/
- while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper))
+ while (i2 >= 0 && FLOAT_EQ(leftUpper, intervalsUpper[i2].upper))
{
- if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower))
+ if (FLOAT_GT(rightLower, intervalsUpper[i2].lower))
rightLower = intervalsUpper[i2].lower;
i2--;
}
if (i2 < 0)
break;
leftUpper = intervalsUpper[i2].upper;
/*
* Find count of intervals which anyway should be placed to the
* right group.
*/
- while (i1 >= 0 && FLOAT8_GE(intervalsLower[i1].lower, rightLower))
+ while (i1 >= 0 && FLOAT_GE(intervalsLower[i1].lower, rightLower))
i1--;
/*
* Consider found split.
*/
g_box_consider_split(&context, dim,
rightLower, i1 + 1, leftUpper, i2 + 1);
}
}
@@ -775,42 +767,42 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
{
lower = box->low.x;
upper = box->high.x;
}
else
{
lower = box->low.y;
upper = box->high.y;
}
- if (FLOAT8_LE(upper, context.leftUpper))
+ if (FLOAT_LE(upper, context.leftUpper))
{
/* Fits to the left group */
- if (FLOAT8_GE(lower, context.rightLower))
+ if (FLOAT_GE(lower, context.rightLower))
{
/* Fits also to the right group, so "common entry" */
commonEntries[commonEntriesCount++].index = i;
}
else
{
/* Doesn't fit to the right group, so join to the left group */
PLACE_LEFT(box, i);
}
}
else
{
/*
* Each entry should fit on either left or right group. Since this
* entry didn't fit on the left group, it better fit in the right
* group.
*/
- Assert(FLOAT8_GE(lower, context.rightLower));
+ Assert(FLOAT_GE(lower, context.rightLower));
/* Doesn't fit to the left group, so join to the right group */
PLACE_RIGHT(box, i);
}
}
/*
* Distribute "common entries", if any.
*/
if (commonEntriesCount > 0)
@@ -880,24 +872,24 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
* equivalent to box_same().
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
- *result = (FLOAT8_EQ(b1->low.x, b2->low.x) &&
- FLOAT8_EQ(b1->low.y, b2->low.y) &&
- FLOAT8_EQ(b1->high.x, b2->high.x) &&
- FLOAT8_EQ(b1->high.y, b2->high.y));
+ *result = (FLOAT_EQ(b1->low.x, b2->low.x) &&
+ FLOAT_EQ(b1->low.y, b2->low.y) &&
+ FLOAT_EQ(b1->high.x, b2->high.x) &&
+ FLOAT_EQ(b1->high.y, b2->high.y));
else
*result = (b1 == NULL && b2 == NULL);
PG_RETURN_POINTER(result);
}
/*
* Leaf-level consistency for boxes: just apply the query operator
*/
static bool
gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index fac166d..88f26d0 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -12,21 +12,21 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <math.h>
#include "access/gist_private.h"
#include "access/reloptions.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
/*
* Write itup vector to page, has no control of free space.
*/
void
gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
{
OffsetNumber l = InvalidOffsetNumber;
int i;
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 8aa17e1..5b69221 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -16,61 +16,24 @@
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <limits.h>
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/sortsupport.h"
-#ifndef M_PI
-/* from my RH5.2 gcc math.h file - thomas 2000-04-03 */
-#define M_PI 3.14159265358979323846
-#endif
-
-/* Radians per degree, a.k.a. PI / 180 */
-#define RADIANS_PER_DEGREE 0.0174532925199432957692
-
-/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
- * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
- */
-#if defined(WIN32) && !defined(NAN)
-static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
-
-#define NAN (*(const double *) nan)
-#endif
-
-/* not sure what the following should be, but better to make it over-sufficient */
-#define MAXFLOATWIDTH 64
-#define MAXDOUBLEWIDTH 128
-
-/*
- * check to see if a float4/8 val has underflowed or overflowed
- */
-#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \
-do { \
- if (isinf(val) && !(inf_is_valid)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
- errmsg("value out of range: overflow"))); \
- \
- if ((val) == 0.0 && !(zero_is_valid)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
- errmsg("value out of range: underflow"))); \
-} while(0)
-
-
/* Configurable GUC parameter */
int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */
/* Cached constants for degree-based trig functions */
static bool degree_consts_set = false;
static float8 sin_30 = 0;
static float8 one_minus_cos_60 = 0;
static float8 asin_0_5 = 0;
static float8 acos_0_5 = 0;
static float8 atan_1_0 = 0;
@@ -689,35 +652,35 @@ float4up(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT4(arg);
}
Datum
float4larger(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (float4_cmp_internal(arg1, arg2) > 0)
+ if (FLOAT_GT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT4(result);
}
Datum
float4smaller(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (float4_cmp_internal(arg1, arg2) < 0)
+ if (FLOAT_LT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT4(result);
}
/*
* ======================
* FLOAT8 BASE OPERATIONS
* ======================
@@ -756,35 +719,35 @@ float8up(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(arg);
}
Datum
float8larger(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (float8_cmp_internal(arg1, arg2) > 0)
+ if (FLOAT_GT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT8(result);
}
Datum
float8smaller(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (float8_cmp_internal(arg1, arg2) < 0)
+ if (FLOAT_LT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* ARITHMETIC OPERATORS
@@ -797,232 +760,188 @@ float8smaller(PG_FUNCTION_ARGS)
* float4mul - returns arg1 * arg2
* float4div - returns arg1 / arg2
*/
Datum
float4pl(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- result = arg1 + arg2;
+ FLOAT_PL(result, arg1, arg2);
- /*
- * There isn't any way to check for underflow of addition/subtraction
- * because numbers near the underflow value have already been rounded to
- * the point where we can't detect that the two values were originally
- * different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
- * 1.4013e-45.
- */
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT4(result);
}
Datum
float4mi(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- result = arg1 - arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+ FLOAT_MI(result, arg1, arg2);
+
PG_RETURN_FLOAT4(result);
}
Datum
float4mul(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- result = arg1 * arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ FLOAT_MUL(result, arg1, arg2);
+
PG_RETURN_FLOAT4(result);
}
Datum
float4div(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT4(result);
}
/*
* float8pl - returns arg1 + arg2
* float8mi - returns arg1 - arg2
* float8mul - returns arg1 * arg2
* float8div - returns arg1 / arg2
*/
Datum
float8pl(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 + arg2;
+ FLOAT_PL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float8mi(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 - arg2;
+ FLOAT_MI(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float8mul(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 * arg2;
+ FLOAT_MUL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
Datum
float8div(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* COMPARISON OPERATORS
* ====================
*/
/*
* float4{eq,ne,lt,le,gt,ge} - float4/float4 comparison operations
*/
int
float4_cmp_internal(float4 a, float4 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
- if (isnan(a))
- {
- if (isnan(b))
- return 0; /* NAN = NAN */
- else
- return 1; /* NAN > non-NAN */
- }
- else if (isnan(b))
- {
- return -1; /* non-NAN < NAN */
- }
+ if (FLOAT_GT(a, b))
+ return 1;
+ else if (FLOAT_LT(a, b))
+ return -1;
else
- {
- if (a > b)
- return 1;
- else if (a < b)
- return -1;
- else
- return 0;
- }
+ return 0;
}
Datum
float4eq(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float4ne(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float4lt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float4le(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float4gt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float4ge(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
Datum
btfloat4cmp(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
}
@@ -1044,99 +963,80 @@ btfloat4sortsupport(PG_FUNCTION_ARGS)
ssup->comparator = btfloat4fastcmp;
PG_RETURN_VOID();
}
/*
* float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations
*/
int
float8_cmp_internal(float8 a, float8 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
- if (isnan(a))
- {
- if (isnan(b))
- return 0; /* NAN = NAN */
- else
- return 1; /* NAN > non-NAN */
- }
- else if (isnan(b))
- {
- return -1; /* non-NAN < NAN */
- }
+ if (FLOAT_GT(a, b))
+ return 1;
+ else if (FLOAT_LT(a, b))
+ return -1;
else
- {
- if (a > b)
- return 1;
- else if (a < b)
- return -1;
- else
- return 0;
- }
+ return 0;
}
Datum
float8eq(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float8ne(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float8lt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float8le(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float8gt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float8ge(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
Datum
btfloat8cmp(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
}
@@ -2369,23 +2269,22 @@ dtand(PG_FUNCTION_ARGS)
/*
* degrees - returns degrees converted from radians
*/
Datum
degrees(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
- result = arg1 / RADIANS_PER_DEGREE;
+ FLOAT_DIV(result, arg1, RADIANS_PER_DEGREE);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dpi - returns the constant PI
*/
Datum
dpi(PG_FUNCTION_ARGS)
{
@@ -2395,23 +2294,22 @@ dpi(PG_FUNCTION_ARGS)
/*
* radians - returns radians converted from degrees
*/
Datum
radians(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
- result = arg1 * RADIANS_PER_DEGREE;
+ FLOAT_MUL(result, arg1, RADIANS_PER_DEGREE);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* drandom - returns a random number
*/
Datum
drandom(PG_FUNCTION_ARGS)
{
@@ -2498,61 +2396,49 @@ float8_combine(PG_FUNCTION_ARGS)
float8 *transvalues1;
float8 *transvalues2;
float8 N,
sumX,
sumX2;
if (!AggCheckCallContext(fcinfo, NULL))
elog(ERROR, "aggregate function called in non-aggregate context");
transvalues1 = check_float8_array(transarray1, "float8_combine", 3);
- N = transvalues1[0];
- sumX = transvalues1[1];
- sumX2 = transvalues1[2];
-
transvalues2 = check_float8_array(transarray2, "float8_combine", 3);
- N += transvalues2[0];
- sumX += transvalues2[1];
- CHECKFLOATVAL(sumX, isinf(transvalues1[1]) || isinf(transvalues2[1]),
- true);
- sumX2 += transvalues2[2];
- CHECKFLOATVAL(sumX2, isinf(transvalues1[2]) || isinf(transvalues2[2]),
- true);
+ N = transvalues1[0] + transvalues2[0];
+ FLOAT_PL(sumX, transvalues1[1], transvalues2[1]);
+ FLOAT_PL(sumX2, transvalues1[2], transvalues2[2]);
transvalues1[0] = N;
transvalues1[1] = sumX;
transvalues1[2] = sumX2;
PG_RETURN_ARRAYTYPE_P(transarray1);
}
Datum
float8_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
float8 newval = PG_GETARG_FLOAT8(1);
float8 *transvalues;
float8 N,
+ newval2,
sumX,
sumX2;
transvalues = check_float8_array(transarray, "float8_accum", 3);
- N = transvalues[0];
- sumX = transvalues[1];
- sumX2 = transvalues[2];
-
- N += 1.0;
- sumX += newval;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true);
- sumX2 += newval * newval;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true);
+ N = transvalues[0] + 1.0;
+ FLOAT_PL(sumX, transvalues[1], newval);
+ FLOAT_MUL(newval2, newval, newval);
+ FLOAT_PL(sumX2, transvalues[2], newval2);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = sumX;
@@ -2579,33 +2465,29 @@ float8_accum(PG_FUNCTION_ARGS)
Datum
float4_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
/* do computations as float8 */
float8 newval = PG_GETARG_FLOAT4(1);
float8 *transvalues;
float8 N,
+ newval2,
sumX,
sumX2;
transvalues = check_float8_array(transarray, "float4_accum", 3);
- N = transvalues[0];
- sumX = transvalues[1];
- sumX2 = transvalues[2];
-
- N += 1.0;
- sumX += newval;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true);
- sumX2 += newval * newval;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true);
+ N = transvalues[0] + 1.0;
+ FLOAT_PL(sumX, transvalues[1], newval);
+ FLOAT_MUL(newval2, newval, newval);
+ FLOAT_PL(sumX2, transvalues[2], newval2);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = sumX;
@@ -2872,45 +2754,28 @@ float8_regr_combine(PG_FUNCTION_ARGS)
sumX,
sumX2,
sumY,
sumY2,
sumXY;
if (!AggCheckCallContext(fcinfo, NULL))
elog(ERROR, "aggregate function called in non-aggregate context");
transvalues1 = check_float8_array(transarray1, "float8_regr_combine", 6);
- N = transvalues1[0];
- sumX = transvalues1[1];
- sumX2 = transvalues1[2];
- sumY = transvalues1[3];
- sumY2 = transvalues1[4];
- sumXY = transvalues1[5];
-
transvalues2 = check_float8_array(transarray2, "float8_regr_combine", 6);
- N += transvalues2[0];
- sumX += transvalues2[1];
- CHECKFLOATVAL(sumX, isinf(transvalues1[1]) || isinf(transvalues2[1]),
- true);
- sumX2 += transvalues2[2];
- CHECKFLOATVAL(sumX2, isinf(transvalues1[2]) || isinf(transvalues2[2]),
- true);
- sumY += transvalues2[3];
- CHECKFLOATVAL(sumY, isinf(transvalues1[3]) || isinf(transvalues2[3]),
- true);
- sumY2 += transvalues2[4];
- CHECKFLOATVAL(sumY2, isinf(transvalues1[4]) || isinf(transvalues2[4]),
- true);
- sumXY += transvalues2[5];
- CHECKFLOATVAL(sumXY, isinf(transvalues1[5]) || isinf(transvalues2[5]),
- true);
+ N = transvalues1[0] + transvalues2[0];
+ FLOAT_PL(sumX, transvalues1[1], transvalues2[1]);
+ FLOAT_PL(sumX2, transvalues1[2], transvalues2[2]);
+ FLOAT_PL(sumY, transvalues1[3], transvalues2[3]);
+ FLOAT_PL(sumY2, transvalues1[4], transvalues2[4]);
+ FLOAT_PL(sumXY, transvalues1[5], transvalues2[5]);
transvalues1[0] = N;
transvalues1[1] = sumX;
transvalues1[2] = sumX2;
transvalues1[3] = sumY;
transvalues1[4] = sumY2;
transvalues1[5] = sumXY;
PG_RETURN_ARRAYTYPE_P(transarray1);
}
@@ -3265,249 +3130,233 @@ float8_regr_intercept(PG_FUNCTION_ARGS)
* float48mul - returns arg1 * arg2
* float48div - returns arg1 / arg2
*/
Datum
float48pl(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 + arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+ FLOAT_PL(result, arg1, arg2);
+
PG_RETURN_FLOAT8(result);
}
Datum
float48mi(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 - arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+ FLOAT_MI(result, arg1, arg2);
+
PG_RETURN_FLOAT8(result);
}
Datum
float48mul(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 * arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ FLOAT_MUL(result, arg1, arg2);
+
PG_RETURN_FLOAT8(result);
}
Datum
float48div(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* float84pl - returns arg1 + arg2
* float84mi - returns arg1 - arg2
* float84mul - returns arg1 * arg2
* float84div - returns arg1 / arg2
*/
Datum
float84pl(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- result = arg1 + arg2;
+ FLOAT_PL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float84mi(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- result = arg1 - arg2;
+ FLOAT_MI(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float84mul(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- result = arg1 * arg2;
+ FLOAT_MUL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
Datum
float84div(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* COMPARISON OPERATORS
* ====================
*/
/*
* float48{eq,ne,lt,le,gt,ge} - float4/float8 comparison operations
*/
Datum
float48eq(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float48ne(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float48lt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float48le(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float48gt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float48ge(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
/*
* float84{eq,ne,lt,le,gt,ge} - float8/float4 comparison operations
*/
Datum
float84eq(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float84ne(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float84lt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float84le(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float84gt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float84ge(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
/*
* Implements the float8 version of the width_bucket() function
* defined by SQL2003. See also width_bucket_numeric().
*
* 'bound1' and 'bound2' are the lower and upper bounds of the
* histogram's range, respectively. 'count' is the number of buckets
* in the histogram. width_bucket() returns an integer indicating the
* bucket number that 'operand' belongs to in an equiwidth histogram
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index ae93fe0..fde2749 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -80,20 +80,21 @@
#endif
#ifdef HAVE_WCTYPE_H
#include <wctype.h>
#endif
#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
+#include "utils/float.h"
#include "utils/formatting.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"
/* ----------
* Routines type
* ----------
*/
#define DCH_TYPE 1 /* DATE-TIME version */
@@ -106,27 +107,20 @@
#define KeyWord_INDEX_SIZE ('~' - ' ')
#define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
/* ----------
* Maximal length of one node
* ----------
*/
#define DCH_MAX_ITEM_SIZ 12 /* max localized day name */
#define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
-/* ----------
- * More is in float.c
- * ----------
- */
-#define MAXFLOATWIDTH 60
-#define MAXDOUBLEWIDTH 500
-
/* ----------
* Format parser structs
* ----------
*/
typedef struct
{
char *name; /* suffix string */
int len, /* suffix length */
id, /* used in node->suffix */
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 0dff40d..efc81ab 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -14,27 +14,23 @@
*/
#include "postgres.h"
#include <math.h>
#include <limits.h>
#include <float.h>
#include <ctype.h>
#include "libpq/pqformat.h"
#include "miscadmin.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
/*
* Internal routines
*/
enum path_delim
{
PATH_NONE, PATH_OPEN, PATH_CLOSED
};
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 0190156..565a034 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -69,21 +69,21 @@
* src/backend/utils/adt/geo_spgist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
/*
* Comparator for qsort
*
* We don't need to use the floating point macros in here, because this
* is going only going to be used in a place to effect the performance
* of the index, not the correctness.
*/
static int
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 2fbdfe0..6442374 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -28,20 +28,21 @@
#include "access/hash.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "lib/hyperloglog.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/guc.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/sortsupport.h"
/* ----------
* Uncomment the following to enable compilation of dump_numeric()
* and dump_var() and to get a dump of any result produced by make_result().
* ----------
#define NUMERIC_DEBUG
diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c
index 01cd234..71dc12e 100644
--- a/src/backend/utils/adt/rangetypes_gist.c
+++ b/src/backend/utils/adt/rangetypes_gist.c
@@ -9,21 +9,21 @@
*
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_gist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist.h"
#include "access/stratnum.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/datum.h"
#include "utils/rangetypes.h"
/*
* Range class properties used to segregate different classes of ranges in
* GiST. Each unique combination of properties is a class. CLS_EMPTY cannot
* be combined with anything else.
*/
#define CLS_NORMAL 0 /* Ordinary finite range (no bits set) */
diff --git a/src/backend/utils/adt/rangetypes_selfuncs.c b/src/backend/utils/adt/rangetypes_selfuncs.c
index 8595d41..2e79ec7 100644
--- a/src/backend/utils/adt/rangetypes_selfuncs.c
+++ b/src/backend/utils/adt/rangetypes_selfuncs.c
@@ -13,21 +13,21 @@
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_selfuncs.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/selfuncs.h"
#include "utils/typcache.h"
static double calc_rangesel(TypeCacheEntry *typcache, VariableStatData *vardata,
RangeType *constval, Oid operator);
static double default_range_selectivity(Oid operator);
static double calc_hist_selectivity(TypeCacheEntry *typcache,
VariableStatData *vardata, RangeType *constval,
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index fcb71d3..eca3807 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -19,21 +19,21 @@
*
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_typanalyze.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "commands/vacuum.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
static int float8_qsort_cmp(const void *a1, const void *a2);
static int range_bound_qsort_cmp(const void *a1, const void *a2, void *arg);
static void compute_range_stats(VacAttrStats *stats,
AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows);
/*
* range_typanalyze -- typanalyze function for range columns
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index d7ee865..062e041 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -26,20 +26,21 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/scansup.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
+#include "utils/float.h"
/*
* gcc's -ffast-math switch breaks routines that expect exact results from
* expressions like timeval / SECS_PER_HOUR, where timeval is double.
*/
#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6ac5184..46bbf3b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -67,20 +67,21 @@
#include "storage/standby.h"
#include "storage/fd.h"
#include "storage/pg_shmem.h"
#include "storage/proc.h"
#include "storage/predicate.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/guc_tables.h"
+#include "utils/float.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/plancache.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/tzparser.h"
#include "utils/xml.h"
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 8cebc86..159fc09 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -329,33 +329,20 @@ extern Datum bttextsortsupport(PG_FUNCTION_ARGS);
*/
extern Datum btint2sortsupport(PG_FUNCTION_ARGS);
extern Datum btint4sortsupport(PG_FUNCTION_ARGS);
extern Datum btint8sortsupport(PG_FUNCTION_ARGS);
extern Datum btfloat4sortsupport(PG_FUNCTION_ARGS);
extern Datum btfloat8sortsupport(PG_FUNCTION_ARGS);
extern Datum btoidsortsupport(PG_FUNCTION_ARGS);
extern Datum btnamesortsupport(PG_FUNCTION_ARGS);
/* float.c */
-extern PGDLLIMPORT int extra_float_digits;
-
-extern double get_float8_infinity(void);
-extern float get_float4_infinity(void);
-extern double get_float8_nan(void);
-extern float get_float4_nan(void);
-extern int is_infinite(double val);
-extern double float8in_internal(char *num, char **endptr_p,
- const char *type_name, const char *orig_string);
-extern char *float8out_internal(double num);
-extern int float4_cmp_internal(float4 a, float4 b);
-extern int float8_cmp_internal(float8 a, float8 b);
-
extern Datum float4in(PG_FUNCTION_ARGS);
extern Datum float4out(PG_FUNCTION_ARGS);
extern Datum float4recv(PG_FUNCTION_ARGS);
extern Datum float4send(PG_FUNCTION_ARGS);
extern Datum float8in(PG_FUNCTION_ARGS);
extern Datum float8out(PG_FUNCTION_ARGS);
extern Datum float8recv(PG_FUNCTION_ARGS);
extern Datum float8send(PG_FUNCTION_ARGS);
extern Datum float4abs(PG_FUNCTION_ARGS);
extern Datum float4um(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
new file mode 100644
index 0000000..4eacd56
--- /dev/null
+++ b/src/include/utils/float.h
@@ -0,0 +1,130 @@
+/*-------------------------------------------------------------------------
+ *
+ * float.h
+ * Definitions for the built-in floating-point types
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/include/utils/float.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FLOAT_H
+#define FLOAT_H
+
+#ifndef M_PI
+/* From my RH5.2 gcc math.h file - thomas 2000-04-03 */
+#define M_PI 3.14159265358979323846
+#endif
+
+/* Radians per degree, a.k.a. PI / 180 */
+#define RADIANS_PER_DEGREE 0.0174532925199432957692
+
+/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
+ */
+#if defined(WIN32) && !defined(NAN)
+static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
+
+#define NAN (*(const double *) nan)
+#endif
+
+/*
+ * We are not sure what the following should be, but better to make it
+ * over-sufficient.
+ */
+#define MAXFLOATWIDTH 64
+#define MAXDOUBLEWIDTH 128
+
+/*
+ * Check to see if a float4/8 val has underflowed or overflowed
+ */
+#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \
+do { \
+ if (isinf(val) && !(inf_is_valid)) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
+ errmsg("value out of range: overflow"))); \
+ \
+ if ((val) == 0.0 && !(zero_is_valid)) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
+ errmsg("value out of range: underflow"))); \
+} while(0)
+
+/*
+ * Convenience macros for operations with checking
+ *
+ * There isn't any way to check for underflow of addition/subtraction
+ * because numbers near the underflow value have already been rounded to
+ * the point where we can't detect that the two values were originally
+ * different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
+ * 1.4013e-45.
+ */
+#define FLOAT_PL(result, val1, val2) \
+do { \
+ result = (val1) + (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true); \
+} while(0)
+
+#define FLOAT_MI(result, val1, val2) \
+do { \
+ result = (val1) - (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true); \
+} while(0)
+
+#define FLOAT_MUL(result, val1, val2) \
+do { \
+ result = (val1) * (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), \
+ (val1) == 0.0 || (val2) == 0.0); \
+} while(0)
+
+#define FLOAT_DIV(result, val1, val2) \
+do { \
+ if (val2 == 0.0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_DIVISION_BY_ZERO), \
+ errmsg("division by zero"))); \
+ \
+ result = (val1) / (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), \
+ (val1) == 0.0); \
+} while(0)
+
+/*
+ * Convenience macros for NaN-aware comparisons
+ *
+ * We consider all NANs to be equal and larger than any non-NAN. This is
+ * somewhat arbitrary; the important thing is to have a consistent sort
+ * order.
+ */
+#define FLOAT_EQ(a, b) (isnan(a) ? isnan(b) : !isnan(b) && (a) == (b))
+#define FLOAT_NE(a, b) (isnan(a) ? !isnan(b) : isnan(b) || (a) != (b))
+#define FLOAT_LT(a, b) (!isnan(a) && (isnan(b) || (a) < (b)))
+#define FLOAT_LE(a, b) (isnan(b) || (!isnan(a) && (a) <= (b)))
+#define FLOAT_GT(a, b) (!isnan(b) && (isnan(a) || (a) > (b)))
+#define FLOAT_GE(a, b) (isnan(a) || (!isnan(b) && (a) >= (b)))
+#define FLOAT_MAX(a, b) (FLOAT_GT(a, b) ? (a) : (b))
+#define FLOAT_MIN(a, b) (FLOAT_LT(a, b) ? (a) : (b))
+
+extern PGDLLIMPORT int extra_float_digits;
+
+/*
+ * Utility functions in float.c
+ */
+extern float get_float4_infinity(void);
+extern double get_float8_infinity(void);
+extern float get_float4_nan(void);
+extern double get_float8_nan(void);
+extern int is_infinite(double val);
+extern double float8in_internal(char *num, char **endptr_p,
+ const char *type_name, const char *orig_string);
+extern char *float8out_internal(double num);
+extern int float4_cmp_internal(float4 a, float4 b);
+extern int float8_cmp_internal(float8 a, float8 b);
+
+#endif /* FLOAT_H */
--
2.7.4 (Apple Git-66)
0002-geo-ops-fpcomp-v02.patchapplication/octet-stream; name=0002-geo-ops-fpcomp-v02.patchDownload
From 616ea9bb08c98f86375f9851e7e6e1d0792b7c11 Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Mon, 20 Jun 2016 10:24:32 +0200
Subject: [PATCH 2/2] geo-ops-fpcomp-v02
---
src/backend/access/gist/gistproc.c | 102 +-
src/backend/access/spgist/spgkdtreeproc.c | 25 +-
src/backend/utils/adt/geo_ops.c | 1497 ++++++++++++++++------------
src/backend/utils/adt/geo_spgist.c | 26 +-
src/include/utils/geo_decls.h | 35 +-
src/test/regress/expected/line.out | 30 +-
src/test/regress/expected/point.out | 8 +-
src/test/regress/expected/select_views.out | 335 +------
src/test/regress/regress.c | 11 +-
9 files changed, 944 insertions(+), 1125 deletions(-)
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index c7ac033..7cd55c0 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -859,24 +859,20 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
v->spl_ldatum = PointerGetDatum(leftBox);
v->spl_rdatum = PointerGetDatum(rightBox);
PG_RETURN_POINTER(v);
}
/*
* Equality method
*
* This is used for boxes, points, circles, and polygons, all of which store
* boxes as GiST index entries.
- *
- * Returns true only when boxes are exactly the same. We can't use fuzzy
- * comparisons here without breaking index consistency; therefore, this isn't
- * equivalent to box_same().
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
*result = (FLOAT_EQ(b1->low.x, b2->low.x) &&
@@ -1128,24 +1124,24 @@ gist_circle_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
CIRCLE *in = DatumGetCircleP(entry->key);
BOX *r;
r = (BOX *) palloc(sizeof(BOX));
- r->high.x = in->center.x + in->radius;
- r->low.x = in->center.x - in->radius;
- r->high.y = in->center.y + in->radius;
- r->low.y = in->center.y - in->radius;
+ FLOAT_PL(r->high.x, in->center.x, in->radius);
+ FLOAT_MI(r->low.x, in->center.x, in->radius);
+ FLOAT_PL(r->high.y, in->center.y, in->radius);
+ FLOAT_MI(r->low.y, in->center.y, in->radius);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, FALSE);
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
@@ -1169,24 +1165,24 @@ gist_circle_consistent(PG_FUNCTION_ARGS)
*recheck = true;
if (DatumGetBoxP(entry->key) == NULL || query == NULL)
PG_RETURN_BOOL(FALSE);
/*
* Since the operators require recheck anyway, we can just use
* rtree_internal_consistent even at leaf nodes. (This works in part
* because the index entries are bounding boxes not circles.)
*/
- bbox.high.x = query->center.x + query->radius;
- bbox.low.x = query->center.x - query->radius;
- bbox.high.y = query->center.y + query->radius;
- bbox.low.y = query->center.y - query->radius;
+ FLOAT_PL(bbox.high.x, query->center.x, query->radius);
+ FLOAT_MI(bbox.low.x, query->center.x, query->radius);
+ FLOAT_PL(bbox.high.y, query->center.y, query->radius);
+ FLOAT_MI(bbox.low.y, query->center.y, query->radius);
result = rtree_internal_consistent(DatumGetBoxP(entry->key),
&bbox, strategy);
PG_RETURN_BOOL(result);
}
/**************************************************
* Point ops
**************************************************/
@@ -1247,109 +1243,113 @@ gist_point_fetch(PG_FUNCTION_ARGS)
static double
computeDistance(bool isLeaf, BOX *box, Point *point)
{
double result = 0.0;
if (isLeaf)
{
/* simple point to point distance */
result = point_point_distance(point, &box->low);
}
- else if (point->x <= box->high.x && point->x >= box->low.x &&
- point->y <= box->high.y && point->y >= box->low.y)
+ else if (FLOAT_LE(point->x, box->high.x) &&
+ FLOAT_GE(point->x, box->low.x) &&
+ FLOAT_LE(point->y, box->high.y) &&
+ FLOAT_GE(point->y, box->low.y))
{
/* point inside the box */
result = 0.0;
}
- else if (point->x <= box->high.x && point->x >= box->low.x)
+ else if (FLOAT_LE(point->x, box->high.x) &&
+ FLOAT_GE(point->x, box->low.x))
{
/* point is over or below box */
- Assert(box->low.y <= box->high.y);
- if (point->y > box->high.y)
- result = point->y - box->high.y;
- else if (point->y < box->low.y)
- result = box->low.y - point->y;
+ Assert(FLOAT_LE(box->low.y, box->high.y));
+ if (FLOAT_GT(point->y, box->high.y))
+ FLOAT_MI(result, point->y, box->high.y);
+ else if (FLOAT_LT(point->y, box->low.y))
+ FLOAT_MI(result, box->low.y, point->y);
else
elog(ERROR, "inconsistent point values");
}
- else if (point->y <= box->high.y && point->y >= box->low.y)
+ else if (FLOAT_LE(point->y, box->high.y) &&
+ FLOAT_GE(point->y, box->low.y))
{
/* point is to left or right of box */
- Assert(box->low.x <= box->high.x);
- if (point->x > box->high.x)
- result = point->x - box->high.x;
- else if (point->x < box->low.x)
- result = box->low.x - point->x;
+ Assert(FLOAT_LE(box->low.x, box->high.x));
+ if (FLOAT_GT(point->x, box->high.x))
+ FLOAT_MI(result, point->x, box->high.x);
+ else if (FLOAT_LT(point->x, box->low.x))
+ FLOAT_MI(result, box->low.x, point->x);
else
elog(ERROR, "inconsistent point values");
}
else
{
/* closest point will be a vertex */
Point p;
double subresult;
result = point_point_distance(point, &box->low);
subresult = point_point_distance(point, &box->high);
- if (result > subresult)
+ if (FLOAT_GT(result, subresult))
result = subresult;
p.x = box->low.x;
p.y = box->high.y;
subresult = point_point_distance(point, &p);
- if (result > subresult)
+ if (FLOAT_GT(result, subresult))
result = subresult;
p.x = box->high.x;
p.y = box->low.y;
subresult = point_point_distance(point, &p);
- if (result > subresult)
+ if (FLOAT_GT(result, subresult))
result = subresult;
}
return result;
}
static bool
gist_point_consistent_internal(StrategyNumber strategy,
bool isLeaf, BOX *key, Point *query)
{
bool result = false;
switch (strategy)
{
case RTLeftStrategyNumber:
- result = FPlt(key->low.x, query->x);
+ result = FLOAT_LT(key->low.x, query->x);
break;
case RTRightStrategyNumber:
- result = FPgt(key->high.x, query->x);
+ result = FLOAT_GT(key->high.x, query->x);
break;
case RTAboveStrategyNumber:
- result = FPgt(key->high.y, query->y);
+ result = FLOAT_GT(key->high.y, query->y);
break;
case RTBelowStrategyNumber:
- result = FPlt(key->low.y, query->y);
+ result = FLOAT_LT(key->low.y, query->y);
break;
case RTSameStrategyNumber:
if (isLeaf)
{
/* key.high must equal key.low, so we can disregard it */
- result = (FPeq(key->low.x, query->x) &&
- FPeq(key->low.y, query->y));
+ result = (FLOAT_EQ(key->low.x, query->x) &&
+ FLOAT_EQ(key->low.y, query->y));
}
else
{
- result = (FPle(query->x, key->high.x) &&
- FPge(query->x, key->low.x) &&
- FPle(query->y, key->high.y) &&
- FPge(query->y, key->low.y));
+ result = (FLOAT_LE(query->x, key->high.x) &&
+ FLOAT_GE(query->x, key->low.x) &&
+ FLOAT_LE(query->y, key->high.y) &&
+ FLOAT_GE(query->y, key->low.y));
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
result = false; /* keep compiler quiet */
break;
}
return result;
}
@@ -1376,39 +1376,31 @@ gist_point_consistent(PG_FUNCTION_ARGS)
GIST_LEAF(entry),
DatumGetBoxP(entry->key),
PG_GETARG_POINT_P(1));
*recheck = false;
break;
case BoxStrategyNumberGroup:
{
/*
* The only operator in this group is point <@ box (on_pb), so
* we needn't examine strategy again.
- *
- * For historical reasons, on_pb uses exact rather than fuzzy
- * comparisons. We could use box_overlap when at an internal
- * page, but that would lead to possibly visiting child pages
- * uselessly, because box_overlap uses fuzzy comparisons.
- * Instead we write a non-fuzzy overlap test. The same code
- * will also serve for leaf-page tests, since leaf keys have
- * high == low.
*/
BOX *query,
*key;
query = PG_GETARG_BOX_P(1);
key = DatumGetBoxP(entry->key);
- result = (key->high.x >= query->low.x &&
- key->low.x <= query->high.x &&
- key->high.y >= query->low.y &&
- key->low.y <= query->high.y);
+ result = (FLOAT_GE(key->high.x, query->low.x) &&
+ FLOAT_LE(key->low.x, query->high.x) &&
+ FLOAT_GE(key->high.y, query->low.y) &&
+ FLOAT_LE(key->low.y, query->high.y));
*recheck = false;
}
break;
case PolygonStrategyNumberGroup:
{
POLYGON *query = PG_GETARG_POLYGON_P(1);
result = DatumGetBool(DirectFunctionCall5(
gist_poly_consistent,
PointerGetDatum(entry),
@@ -1417,22 +1409,22 @@ gist_point_consistent(PG_FUNCTION_ARGS)
0, PointerGetDatum(recheck)));
if (GIST_LEAF(entry) && result)
{
/*
* We are on leaf page and quick check shows overlapping
* of polygon's bounding box and point
*/
BOX *box = DatumGetBoxP(entry->key);
- Assert(box->high.x == box->low.x
- && box->high.y == box->low.y);
+ Assert(FLOAT_EQ(box->high.x, box->low.x) &&
+ FLOAT_EQ(box->high.y, box->low.y));
result = DatumGetBool(DirectFunctionCall2(
poly_contain_pt,
PolygonPGetDatum(query),
PointPGetDatum(&box->high)));
*recheck = false;
}
}
break;
case CircleStrategyNumberGroup:
{
@@ -1446,22 +1438,22 @@ gist_point_consistent(PG_FUNCTION_ARGS)
0, PointerGetDatum(recheck)));
if (GIST_LEAF(entry) && result)
{
/*
* We are on leaf page and quick check shows overlapping
* of polygon's bounding box and point
*/
BOX *box = DatumGetBoxP(entry->key);
- Assert(box->high.x == box->low.x
- && box->high.y == box->low.y);
+ Assert(FLOAT_EQ(box->high.x, box->low.x) &&
+ FLOAT_EQ(box->high.y, box->low.y));
result = DatumGetBool(DirectFunctionCall2(
circle_contain_pt,
CirclePGetDatum(query),
PointPGetDatum(&box->high)));
*recheck = false;
}
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
diff --git a/src/backend/access/spgist/spgkdtreeproc.c b/src/backend/access/spgist/spgkdtreeproc.c
index 1ab9335..10a08ed 100644
--- a/src/backend/access/spgist/spgkdtreeproc.c
+++ b/src/backend/access/spgist/spgkdtreeproc.c
@@ -12,20 +12,21 @@
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
Datum
spg_kd_config(PG_FUNCTION_ARGS)
{
/* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */
spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
cfg->prefixType = FLOAT8OID;
@@ -175,72 +176,72 @@ spg_kd_inner_consistent(PG_FUNCTION_ARGS)
which = (1 << 1) | (1 << 2);
for (i = 0; i < in->nkeys; i++)
{
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
BOX *boxQuery;
switch (in->scankeys[i].sk_strategy)
{
case RTLeftStrategyNumber:
- if ((in->level % 2) != 0 && FPlt(query->x, coord))
+ if ((in->level % 2) != 0 && FLOAT_LT(query->x, coord))
which &= (1 << 1);
break;
case RTRightStrategyNumber:
- if ((in->level % 2) != 0 && FPgt(query->x, coord))
+ if ((in->level % 2) != 0 && FLOAT_GT(query->x, coord))
which &= (1 << 2);
break;
case RTSameStrategyNumber:
if ((in->level % 2) != 0)
{
- if (FPlt(query->x, coord))
+ if (FLOAT_LT(query->x, coord))
which &= (1 << 1);
- else if (FPgt(query->x, coord))
+ else if (FLOAT_GT(query->x, coord))
which &= (1 << 2);
}
else
{
- if (FPlt(query->y, coord))
+ if (FLOAT_LT(query->y, coord))
which &= (1 << 1);
- else if (FPgt(query->y, coord))
+ else if (FLOAT_GT(query->y, coord))
which &= (1 << 2);
}
break;
case RTBelowStrategyNumber:
- if ((in->level % 2) == 0 && FPlt(query->y, coord))
+ if ((in->level % 2) == 0 && FLOAT_LT(query->y, coord))
which &= (1 << 1);
break;
case RTAboveStrategyNumber:
- if ((in->level % 2) == 0 && FPgt(query->y, coord))
+ if ((in->level % 2) == 0 && FLOAT_GT(query->y, coord))
which &= (1 << 2);
break;
case RTContainedByStrategyNumber:
/*
* For this operator, the query is a box not a point. We
* cheat to the extent of assuming that DatumGetPointP won't
* do anything that would be bad for a pointer-to-box.
*/
boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
if ((in->level % 2) != 0)
{
- if (FPlt(boxQuery->high.x, coord))
+ if (FLOAT_LT(boxQuery->high.x, coord))
which &= (1 << 1);
- else if (FPgt(boxQuery->low.x, coord))
+ else if (FLOAT_GT(boxQuery->low.x, coord))
which &= (1 << 2);
}
else
{
- if (FPlt(boxQuery->high.y, coord))
+ if (FLOAT_LT(boxQuery->high.y, coord))
which &= (1 << 1);
- else if (FPgt(boxQuery->low.y, coord))
+ else if (FLOAT_GT(boxQuery->low.y, coord))
which &= (1 << 2);
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d",
in->scankeys[i].sk_strategy);
break;
}
if (which == 0)
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index efc81ab..3855c8e 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -36,50 +36,56 @@ enum path_delim
static int point_inside(Point *p, int npts, Point *plist);
static int lseg_crossing(double x, double y, double px, double py);
static BOX *box_construct(double x1, double x2, double y1, double y2);
static BOX *box_copy(BOX *box);
static BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2);
static bool box_ov(BOX *box1, BOX *box2);
static double box_ht(BOX *box);
static double box_wd(BOX *box);
static double circle_ar(CIRCLE *circle);
-static CIRCLE *circle_copy(CIRCLE *circle);
+static CIRCLE *circle_construct(double x, double y, double radius);
static LINE *line_construct_pm(Point *pt, double m);
-static void line_construct_pts(LINE *line, Point *pt1, Point *pt2);
+static LINE *line_construct_pts(Point *pt1, Point *pt2);
static bool lseg_intersect_internal(LSEG *l1, LSEG *l2);
static double lseg_dt(LSEG *l1, LSEG *l2);
static bool on_ps_internal(Point *pt, LSEG *lseg);
+static double point_on_line_value(Point *pt, LINE *line);
+static int point_lseg_cmp(Point *pt, LSEG *lseg);
static void make_bound_box(POLYGON *poly);
static bool plist_same(int npts, Point *p1, Point *p2);
static Point *point_construct(double x, double y);
static Point *point_copy(Point *pt);
+static double point_dt(Point *pt1, Point *pt2);
+static double slope(double x1, double x2, double y1, double y2);
static double single_decode(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
static void single_encode(float8 x, StringInfo str);
static void pair_decode(char *str, double *x, double *y, char **endptr_p,
const char *type_name, const char *orig_string);
static void pair_encode(float8 x, float8 y, StringInfo str);
static int pair_count(char *s, char delim);
static void path_decode(char *str, bool opentype, int npts, Point *p,
bool *isopen, char **endptr_p,
const char *type_name, const char *orig_string);
static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
static void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
static double box_ar(BOX *box);
static void box_cn(Point *center, BOX *box);
static Point *interpt_sl(LSEG *lseg, LINE *line);
static bool has_interpt_sl(LSEG *lseg, LINE *line);
+static Point *close_pl_internal(Point *pt, LINE *line);
static double dist_pl_internal(Point *pt, LINE *line);
static double dist_ps_internal(Point *pt, LSEG *lseg);
static Point *line_interpt_internal(LINE *l1, LINE *l2);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
+static Point *lseg_center_internal(LSEG *lseg);
static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2);
static double dist_ppoly_internal(Point *pt, POLYGON *poly);
/*
* Delimiters for input and output strings.
* LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively.
* LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints.
*/
@@ -349,27 +355,27 @@ box_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
BOX *box = (BOX *) palloc(sizeof(BOX));
bool isopen;
double x,
y;
path_decode(str, false, 2, &(box->high), &isopen, NULL, "box", str);
/* reorder corners if necessary... */
- if (box->high.x < box->low.x)
+ if (FLOAT_LT(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
- if (box->high.y < box->low.y)
+ if (FLOAT_LT(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/* box_out - convert a box to external form.
@@ -394,27 +400,27 @@ box_recv(PG_FUNCTION_ARGS)
y;
box = (BOX *) palloc(sizeof(BOX));
box->high.x = pq_getmsgfloat8(buf);
box->high.y = pq_getmsgfloat8(buf);
box->low.x = pq_getmsgfloat8(buf);
box->low.y = pq_getmsgfloat8(buf);
/* reorder corners if necessary... */
- if (box->high.x < box->low.x)
+ if (FLOAT_LT(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
- if (box->high.y < box->low.y)
+ if (FLOAT_LT(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/*
@@ -444,31 +450,31 @@ box_construct(double x1, double x2, double y1, double y2)
return box_fill(result, x1, x2, y1, y2);
}
/* box_fill - fill in a given box struct
*/
static BOX *
box_fill(BOX *result, double x1, double x2, double y1, double y2)
{
- if (x1 > x2)
+ if (FLOAT_GT(x1, x2))
{
result->high.x = x1;
result->low.x = x2;
}
else
{
result->high.x = x2;
result->low.x = x1;
}
- if (y1 > y2)
+ if (FLOAT_GT(y1, y2))
{
result->high.y = y1;
result->low.y = y2;
}
else
{
result->high.y = y2;
result->low.y = y1;
}
@@ -495,246 +501,246 @@ box_copy(BOX *box)
*---------------------------------------------------------*/
/* box_same - are two boxes identical?
*/
Datum
box_same(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box1->high.x, box2->high.x) &&
- FPeq(box1->low.x, box2->low.x) &&
- FPeq(box1->high.y, box2->high.y) &&
- FPeq(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_EQ(box1->high.x, box2->high.x) &&
+ FLOAT_EQ(box1->low.x, box2->low.x) &&
+ FLOAT_EQ(box1->high.y, box2->high.y) &&
+ FLOAT_EQ(box1->low.y, box2->low.y));
}
/* box_overlap - does box1 overlap box2?
*/
Datum
box_overlap(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
PG_RETURN_BOOL(box_ov(box1, box2));
}
static bool
box_ov(BOX *box1, BOX *box2)
{
- return (FPle(box1->low.x, box2->high.x) &&
- FPle(box2->low.x, box1->high.x) &&
- FPle(box1->low.y, box2->high.y) &&
- FPle(box2->low.y, box1->high.y));
+ return (FLOAT_LE(box1->low.x, box2->high.x) &&
+ FLOAT_LE(box2->low.x, box1->high.x) &&
+ FLOAT_LE(box1->low.y, box2->high.y) &&
+ FLOAT_LE(box2->low.y, box1->high.y));
}
/* box_left - is box1 strictly left of box2?
*/
Datum
box_left(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
+ PG_RETURN_BOOL(FLOAT_LT(box1->high.x, box2->low.x));
}
/* box_overleft - is the right edge of box1 at or left of
* the right edge of box2?
*
* This is "less than or equal" for the end of a time range,
* when time ranges are stored as rectangles.
*/
Datum
box_overleft(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.x, box2->high.x));
}
/* box_right - is box1 strictly right of box2?
*/
Datum
box_right(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
+ PG_RETURN_BOOL(FLOAT_GT(box1->low.x, box2->high.x));
}
/* box_overright - is the left edge of box1 at or right of
* the left edge of box2?
*
* This is "greater than or equal" for time ranges, when time ranges
* are stored as rectangles.
*/
Datum
box_overright(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
+ PG_RETURN_BOOL(FLOAT_GE(box1->low.x, box2->low.x));
}
/* box_below - is box1 strictly below box2?
*/
Datum
box_below(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_LT(box1->high.y, box2->low.y));
}
/* box_overbelow - is the upper edge of box1 at or below
* the upper edge of box2?
*/
Datum
box_overbelow(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.y, box2->high.y));
}
/* box_above - is box1 strictly above box2?
*/
Datum
box_above(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(FLOAT_GT(box1->low.y, box2->high.y));
}
/* box_overabove - is the lower edge of box1 at or above
* the lower edge of box2?
*/
Datum
box_overabove(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_GE(box1->low.y, box2->low.y));
}
/* box_contained - is box1 contained by box2?
*/
Datum
box_contained(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x) &&
- FPge(box1->low.x, box2->low.x) &&
- FPle(box1->high.y, box2->high.y) &&
- FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.x, box2->high.x) &&
+ FLOAT_GE(box1->low.x, box2->low.x) &&
+ FLOAT_LE(box1->high.y, box2->high.y) &&
+ FLOAT_GE(box1->low.y, box2->low.y));
}
/* box_contain - does box1 contain box2?
*/
Datum
box_contain(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_GE(box1->high.x, box2->high.x) &&
+ FLOAT_LE(box1->low.x, box2->low.x) &&
+ FLOAT_GE(box1->high.y, box2->high.y) &&
+ FLOAT_LE(box1->low.y, box2->low.y));
}
/* box_positionop -
* is box1 entirely {above,below} box2?
*
* box_below_eq and box_above_eq are obsolete versions that (probably
* erroneously) accept the equal-boundaries case. Since these are not
* in sync with the box_left and box_right code, they are deprecated and
* not supported in the PG 8.1 rtree operator class extension.
*/
Datum
box_below_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.y, box2->low.y));
}
Datum
box_above_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(FLOAT_GE(box1->low.y, box2->high.y));
}
/* box_relop - is area(box1) relop area(box2), within
* our accuracy constraint?
*/
Datum
box_lt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_LT(box_ar(box1), box_ar(box2)));
}
Datum
box_gt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_GT(box_ar(box1), box_ar(box2)));
}
Datum
box_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_EQ(box_ar(box1), box_ar(box2)));
}
Datum
box_le(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_LE(box_ar(box1), box_ar(box2)));
}
Datum
box_ge(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_GE(box_ar(box1), box_ar(box2)));
}
/*----------------------------------------------------------
* "Arithmetic" operators on boxes.
*---------------------------------------------------------*/
/* box_area - returns the area of the box.
*/
Datum
@@ -777,21 +783,21 @@ Datum
box_distance(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
Point a,
b;
box_cn(&a, box1);
box_cn(&b, box2);
- PG_RETURN_FLOAT8(HYPOT(a.x - b.x, a.y - b.y));
+ PG_RETURN_FLOAT8(point_dt(&a, &b));
}
/* box_center - returns the center point of the box.
*/
Datum
box_center(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *result = (Point *) palloc(sizeof(Point));
@@ -809,22 +815,28 @@ box_ar(BOX *box)
{
return box_wd(box) * box_ht(box);
}
/* box_cn - stores the centerpoint of the box into *center.
*/
static void
box_cn(Point *center, BOX *box)
{
- center->x = (box->high.x + box->low.x) / 2.0;
- center->y = (box->high.y + box->low.y) / 2.0;
+ double sumx;
+ double sumy;
+
+ FLOAT_PL(sumx, box->high.x, box->low.x);
+ FLOAT_DIV(center->x, sumx, 2.0);
+
+ FLOAT_PL(sumy, box->high.y, box->low.y);
+ FLOAT_DIV(center->y, sumy, 2.0);
}
/* box_wd - returns the width (length) of the box
* (horizontal magnitude).
*/
static double
box_wd(BOX *box)
{
return box->high.x - box->low.x;
@@ -854,24 +866,24 @@ box_intersect(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
BOX *result;
if (!box_ov(box1, box2))
PG_RETURN_NULL();
result = (BOX *) palloc(sizeof(BOX));
- result->high.x = Min(box1->high.x, box2->high.x);
- result->low.x = Max(box1->low.x, box2->low.x);
- result->high.y = Min(box1->high.y, box2->high.y);
- result->low.y = Max(box1->low.y, box2->low.y);
+ result->high.x = FLOAT_MIN(box1->high.x, box2->high.x);
+ result->low.x = FLOAT_MAX(box1->low.x, box2->low.x);
+ result->high.y = FLOAT_MIN(box1->high.y, box2->high.y);
+ result->low.y = FLOAT_MAX(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(result);
}
/* box_diagonal -
* returns a line segment which happens to be the
* positive-slope diagonal of "box".
*/
Datum
@@ -908,48 +920,51 @@ line_decode(char *s, const char *str, LINE *line)
s++;
if (*s != '\0')
return false;
return true;
}
Datum
line_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- LINE *line = (LINE *) palloc(sizeof(LINE));
+ LINE *line;
LSEG lseg;
bool isopen;
char *s;
s = str;
while (isspace((unsigned char) *s))
s++;
if (*s == '{')
{
+ line = (LINE *) palloc(sizeof(LINE));
+
if (!line_decode(s + 1, str, line))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"line", str)));
- if (FPzero(line->A) && FPzero(line->B))
+ if (FLOAT_EQ(line->A, 0.0) && FLOAT_EQ(line->B, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: A and B cannot both be zero")));
}
else
{
path_decode(s, true, 2, &(lseg.p[0]), &isopen, NULL, "line", str);
- if (FPeq(lseg.p[0].x, lseg.p[1].x) && FPeq(lseg.p[0].y, lseg.p[1].y))
+ if (FLOAT_EQ(lseg.p[0].x, lseg.p[1].x) &&
+ FLOAT_EQ(lseg.p[0].y, lseg.p[1].y))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: must be two distinct points")));
- line_construct_pts(line, &lseg.p[0], &lseg.p[1]);
+ line = line_construct_pts(&lseg.p[0], &lseg.p[1]);
}
PG_RETURN_LINE_P(line);
}
Datum
line_out(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
@@ -968,20 +983,25 @@ line_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
LINE *line;
line = (LINE *) palloc(sizeof(LINE));
line->A = pq_getmsgfloat8(buf);
line->B = pq_getmsgfloat8(buf);
line->C = pq_getmsgfloat8(buf);
+ if (FLOAT_EQ(line->A, 0.0) && FLOAT_EQ(line->B, 0.0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid line specification: A and B cannot both be zero")));
+
PG_RETURN_LINE_P(line);
}
/*
* line_send - converts line to binary format
*/
Datum
line_send(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
@@ -1000,93 +1020,101 @@ line_send(PG_FUNCTION_ARGS)
* Internal form: Ax+By+C=0
*---------------------------------------------------------*/
/* line_construct_pm()
* point-slope
*/
static LINE *
line_construct_pm(Point *pt, double m)
{
LINE *result = (LINE *) palloc(sizeof(LINE));
+ double val;
- if (m == DBL_MAX)
+ if (FLOAT_EQ(m, DBL_MAX))
{
/* vertical - use "x = C" */
- result->A = -1;
- result->B = 0;
+ result->A = -1.0;
+ result->B = 0.0;
result->C = pt->x;
}
else
{
- /* use "mx - y + yinter = 0" */
result->A = m;
result->B = -1.0;
- result->C = pt->y - m * pt->x;
+ FLOAT_MUL(val, m, pt->x);
+ FLOAT_MI(result->C, pt->y, val);
}
return result;
}
/*
* Fill already-allocated LINE struct from two points on the line
*/
-static void
-line_construct_pts(LINE *line, Point *pt1, Point *pt2)
+static LINE *
+line_construct_pts(Point *pt1, Point *pt2)
{
- if (FPeq(pt1->x, pt2->x))
+ LINE *result = (LINE *) palloc(sizeof(LINE));
+
+ if (FLOAT_EQ(pt1->x, pt2->x))
{ /* vertical */
/* use "x = C" */
- line->A = -1;
- line->B = 0;
- line->C = pt1->x;
+ result->A = -1.0;
+ result->B = 0.0;
+ result->C = pt1->x;
#ifdef GEODEBUG
printf("line_construct_pts- line is vertical\n");
#endif
}
- else if (FPeq(pt1->y, pt2->y))
+ else if (FLOAT_EQ(pt1->y, pt2->y))
{ /* horizontal */
/* use "y = C" */
- line->A = 0;
- line->B = -1;
- line->C = pt1->y;
+ result->A = 0.0;
+ result->B = -1.0;
+ result->C = pt1->y;
#ifdef GEODEBUG
printf("line_construct_pts- line is horizontal\n");
#endif
}
else
{
- /* use "mx - y + yinter = 0" */
- line->A = (pt2->y - pt1->y) / (pt2->x - pt1->x);
- line->B = -1.0;
- line->C = pt1->y - line->A * pt1->x;
- /* on some platforms, the preceding expression tends to produce -0 */
- if (line->C == 0.0)
- line->C = 0.0;
+ double mul_x;
+ double mul_y;
+
+ /* The equation is optimized to avoid losing precision by division. */
+ FLOAT_MI(result->A, pt2->y, pt1->y);
+ FLOAT_MI(result->B, pt1->x, pt2->x);
+ FLOAT_MUL(mul_x, pt1->x, result->A);
+ FLOAT_MUL(mul_y, pt1->y, result->B);
+ FLOAT_PL(result->C, mul_x, mul_y);
+ if (FLOAT_NE(result->C, 0.0)) /* We don't want to get -0. */
+ result->C = -result->C;
+
#ifdef GEODEBUG
printf("line_construct_pts- line is neither vertical nor horizontal (diffs x=%.*g, y=%.*g\n",
DBL_DIG, (pt2->x - pt1->x), DBL_DIG, (pt2->y - pt1->y));
#endif
}
+
+ return result;
}
/* line_construct_pp()
* two points
*/
Datum
line_construct_pp(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- LINE *result = (LINE *) palloc(sizeof(LINE));
- line_construct_pts(result, pt1, pt2);
- PG_RETURN_LINE_P(result);
+ PG_RETURN_LINE_P(line_construct_pts(pt1, pt2));
}
/*----------------------------------------------------------
* Relative position routines.
*---------------------------------------------------------*/
Datum
line_intersect(PG_FUNCTION_ARGS)
{
@@ -1096,102 +1124,139 @@ line_intersect(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(!DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))));
}
Datum
line_parallel(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
+ double mul12;
+ double mul21;
- if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->B));
+ if (FLOAT_EQ(l1->A, 0.0)) /* horizontal? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->A, 0.0));
+ if (FLOAT_EQ(l1->B, 0.0)) /* vertical? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->B, 0.0));
- PG_RETURN_BOOL(FPeq(l2->A, l1->A * (l2->B / l1->B)));
+ if (FLOAT_EQ(l2->A, 0.0) || FLOAT_EQ(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(mul12, l1->A, l2->B);
+ FLOAT_MUL(mul21, l2->A, l1->B);
+
+ PG_RETURN_BOOL(FLOAT_EQ(mul12, mul21));
}
Datum
line_perp(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
+ double val1;
+ double val2;
- if (FPzero(l1->A))
- PG_RETURN_BOOL(FPzero(l2->B));
- else if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->A));
+ if (FLOAT_EQ(l1->A, 0.0)) /* horizontal? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->B, 0.0));
+ if (FLOAT_EQ(l1->B, 0.0)) /* vertical? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->A, 0.0));
- PG_RETURN_BOOL(FPeq(((l1->A * l2->B) / (l1->B * l2->A)), -1.0));
+ if (FLOAT_EQ(l2->A, 0.0) || FLOAT_EQ(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(val1, l1->A, l2->B);
+ FLOAT_MUL(val2, l2->A, l1->B);
+
+ PG_RETURN_BOOL(FLOAT_EQ(val1, -val2));
}
Datum
line_vertical(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->B));
+ PG_RETURN_BOOL(FLOAT_EQ(line->B, 0.0));
}
Datum
line_horizontal(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->A));
+ PG_RETURN_BOOL(FLOAT_EQ(line->A, 0.0));
}
Datum
line_eq(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- double k;
+ double mul12;
+ double mul21;
- if (!FPzero(l2->A))
- k = l1->A / l2->A;
- else if (!FPzero(l2->B))
- k = l1->B / l2->B;
- else if (!FPzero(l2->C))
- k = l1->C / l2->C;
- else
- k = 1.0;
+ FLOAT_MUL(mul12, l1->A, l2->B);
+ FLOAT_MUL(mul21, l2->A, l1->B);
+ if (FLOAT_NE(mul12, mul21))
+ PG_RETURN_BOOL(false);
- PG_RETURN_BOOL(FPeq(l1->A, k * l2->A) &&
- FPeq(l1->B, k * l2->B) &&
- FPeq(l1->C, k * l2->C));
+ if (FLOAT_NE(l1->A, 0.0))
+ {
+ if (FLOAT_EQ(l2->A, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(mul12, l1->A, l2->C);
+ FLOAT_MUL(mul21, l2->A, l1->C);
+ }
+ else /* (l1->A == 0.0 => l1->B != 0.0 => l2->A == 0.0) */
+ {
+ if (FLOAT_EQ(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(mul12, l1->B, l2->C);
+ FLOAT_MUL(mul21, l2->B, l1->C);
+ }
+
+ PG_RETURN_BOOL(FLOAT_EQ(mul12, mul21));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* line_distance()
* Distance between two lines.
*/
Datum
line_distance(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- float8 result;
- Point *tmp;
+ double result;
+ double y;
if (!DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))))
PG_RETURN_FLOAT8(0.0);
- if (FPzero(l1->B)) /* vertical? */
- PG_RETURN_FLOAT8(fabs(l1->C - l2->C));
- tmp = point_construct(0.0, l1->C);
- result = dist_pl_internal(tmp, l2);
+
+ if (FLOAT_EQ(l1->A, 0.0) || FLOAT_EQ(l1->B, 0.0)) /* horizontal or vertical? */
+ {
+ FLOAT_MI(result, l1->C, l2->C);
+ }
+ else
+ {
+ FLOAT_DIV(y, -l1->C, l1->B);
+ result = dist_pl_internal(point_construct(0.0, y), l2);
+ }
+
PG_RETURN_FLOAT8(result);
}
/* line_interpt()
* Point where two lines l1, l2 intersect (if any)
*/
Datum
line_interpt(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
@@ -1207,48 +1272,69 @@ line_interpt(PG_FUNCTION_ARGS)
/*
* Internal version of line_interpt
*
* returns a NULL pointer if no intersection point
*/
static Point *
line_interpt_internal(LINE *l1, LINE *l2)
{
Point *result;
+ double val1;
+ double val2;
double x,
y;
/*
* NOTE: if the lines are identical then we will find they are parallel
* and report "no intersection". This is a little weird, but since
* there's no *unique* intersection, maybe it's appropriate behavior.
*/
if (DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))))
return NULL;
- if (FPzero(l1->B)) /* l1 vertical? */
+ if (FLOAT_EQ(l1->B, 0.0)) /* l1 vertical? */
{
- x = l1->C;
- y = (l2->A * x + l2->C);
- }
- else if (FPzero(l2->B)) /* l2 vertical? */
- {
- x = l2->C;
- y = (l1->A * x + l1->C);
+ FLOAT_DIV(x, -l1->C, l1->A);
+ FLOAT_MUL(val1, l2->A, x);
+ FLOAT_PL(val2, val1, l2->C);
+ FLOAT_DIV(y, -val2, l2->B);
}
else
{
- x = (l1->C - l2->C) / (l2->A - l1->A);
- y = (l1->A * x + l1->C);
+ if (FLOAT_EQ(l2->B, 0.0)) /* l2 vertical? */
+ {
+ FLOAT_DIV(x, -l2->C, l2->A);
+ }
+ else
+ {
+ double mul12;
+ double mul21;
+
+ FLOAT_MUL(mul12, l1->B, l2->C);
+ FLOAT_MUL(mul21, l2->B, l1->C);
+ FLOAT_MI(val1, mul12, mul21);
+
+ FLOAT_MUL(mul12, l1->A, l2->B);
+ FLOAT_MUL(mul21, l2->A, l1->B);
+ FLOAT_MI(val2, mul12, mul21);
+
+ FLOAT_DIV(x, val1, val2);
+ }
+
+ FLOAT_MUL(val1, l1->A, x);
+ FLOAT_PL(val2, val1, l1->C);
+ FLOAT_DIV(y, -val2, l1->B);
}
+
result = point_construct(x, y);
#ifdef GEODEBUG
printf("line_interpt- lines are A=%.*g, B=%.*g, C=%.*g, A=%.*g, B=%.*g, C=%.*g\n",
DBL_DIG, l1->A, DBL_DIG, l1->B, DBL_DIG, l1->C, DBL_DIG, l2->A, DBL_DIG, l2->B, DBL_DIG, l2->C);
printf("line_interpt- lines intersect at (%.*g,%.*g)\n", DBL_DIG, x, DBL_DIG, y);
#endif
return result;
}
@@ -1274,35 +1360,40 @@ line_interpt_internal(LINE *l1, LINE *l2)
* "[xcoord, ycoord,... ]"
* Also support older format:
* "(closed, npts, xcoord, ycoord,... )"
*---------------------------------------------------------*/
Datum
path_area(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0);
double area = 0.0;
+ double mulxy;
+ double mulyx;
+ double new_area;
int i,
j;
if (!path->closed)
PG_RETURN_NULL();
for (i = 0; i < path->npts; i++)
{
j = (i + 1) % path->npts;
- area += path->p[i].x * path->p[j].y;
- area -= path->p[i].y * path->p[j].x;
+ FLOAT_MUL(mulxy, path->p[i].x, path->p[j].y);
+ FLOAT_MUL(mulyx, path->p[i].y, path->p[j].x);
+ FLOAT_PL(new_area, area, mulxy);
+ FLOAT_MI(area, new_area, mulyx);
}
- area *= 0.5;
- PG_RETURN_FLOAT8(area < 0.0 ? -area : area);
+ FLOAT_MUL(new_area, area, 0.5);
+ PG_RETURN_FLOAT8(fabs(new_area));
}
Datum
path_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
PATH *path;
bool isopen;
char *s;
@@ -1322,21 +1413,21 @@ path_in(PG_FUNCTION_ARGS)
s++;
/* skip single leading paren */
if ((*s == LDELIM) && (strrchr(s, LDELIM) == s))
{
s++;
depth++;
}
base_size = sizeof(path->p[0]) * npts;
- size = offsetof(PATH, p) +base_size;
+ size = offsetof(PATH, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
@@ -1392,21 +1483,21 @@ path_recv(PG_FUNCTION_ARGS)
int32 i;
int size;
closed = pq_getmsgbyte(buf);
npts = pq_getmsgint(buf, sizeof(int32));
if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(PATH, p)) / sizeof(Point)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of points in external \"path\" value")));
- size = offsetof(PATH, p) +sizeof(path->p[0]) * npts;
+ size = offsetof(PATH, p) + sizeof(path->p[0]) * npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = npts;
path->closed = (closed ? 1 : 0);
/* prevent instability in unused pad bytes */
path->dummy = 0;
for (i = 0; i < npts; i++)
{
@@ -1519,31 +1610,31 @@ path_npoints(PG_FUNCTION_ARGS)
PG_RETURN_INT32(path->npts);
}
Datum
path_close(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
- path->closed = TRUE;
+ path->closed = true;
PG_RETURN_PATH_P(path);
}
Datum
path_open(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
- path->closed = FALSE;
+ path->closed = false;
PG_RETURN_PATH_P(path);
}
/* path_inter -
* Does p1 intersect p2 at any point?
* Use bounding boxes for a quick (O(n)) check, then do a
* O(n^2) iterative edge check.
*/
@@ -1559,33 +1650,33 @@ path_inter(PG_FUNCTION_ARGS)
LSEG seg1,
seg2;
if (p1->npts <= 0 || p2->npts <= 0)
PG_RETURN_BOOL(false);
b1.high.x = b1.low.x = p1->p[0].x;
b1.high.y = b1.low.y = p1->p[0].y;
for (i = 1; i < p1->npts; i++)
{
- b1.high.x = Max(p1->p[i].x, b1.high.x);
- b1.high.y = Max(p1->p[i].y, b1.high.y);
- b1.low.x = Min(p1->p[i].x, b1.low.x);
- b1.low.y = Min(p1->p[i].y, b1.low.y);
+ b1.high.x = FLOAT_MAX(p1->p[i].x, b1.high.x);
+ b1.high.y = FLOAT_MAX(p1->p[i].y, b1.high.y);
+ b1.low.x = FLOAT_MIN(p1->p[i].x, b1.low.x);
+ b1.low.y = FLOAT_MIN(p1->p[i].y, b1.low.y);
}
b2.high.x = b2.low.x = p2->p[0].x;
b2.high.y = b2.low.y = p2->p[0].y;
for (i = 1; i < p2->npts; i++)
{
- b2.high.x = Max(p2->p[i].x, b2.high.x);
- b2.high.y = Max(p2->p[i].y, b2.high.y);
- b2.low.x = Min(p2->p[i].x, b2.low.x);
- b2.low.y = Min(p2->p[i].y, b2.low.y);
+ b2.high.x = FLOAT_MAX(p2->p[i].x, b2.high.x);
+ b2.high.y = FLOAT_MAX(p2->p[i].y, b2.high.y);
+ b2.low.x = FLOAT_MIN(p2->p[i].x, b2.low.x);
+ b2.low.y = FLOAT_MIN(p2->p[i].y, b2.low.y);
}
if (!box_ov(&b1, &b2))
PG_RETURN_BOOL(false);
/* pairwise check lseg intersections */
for (i = 0; i < p1->npts; i++)
{
int iprev;
if (i > 0)
@@ -1663,21 +1754,21 @@ path_distance(PG_FUNCTION_ARGS)
continue;
jprev = p2->npts - 1; /* include the closure segment */
}
statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
tmp = DatumGetFloat8(DirectFunctionCall2(lseg_distance,
LsegPGetDatum(&seg1),
LsegPGetDatum(&seg2)));
- if (!have_min || tmp < min)
+ if (!have_min || FLOAT_LT(tmp, min))
{
min = tmp;
have_min = true;
}
}
}
if (!have_min)
PG_RETURN_NULL();
@@ -1686,38 +1777,43 @@ path_distance(PG_FUNCTION_ARGS)
/*----------------------------------------------------------
* "Arithmetic" operations.
*---------------------------------------------------------*/
Datum
path_length(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0);
+ bool has_inf = false;
+ float8 distance;
float8 result = 0.0;
int i;
for (i = 0; i < path->npts; i++)
{
int iprev;
if (i > 0)
iprev = i - 1;
else
{
if (!path->closed)
continue;
iprev = path->npts - 1; /* include the closure segment */
}
- result += point_dt(&path->p[iprev], &path->p[i]);
+ distance = point_dt(&path->p[iprev], &path->p[i]);
+ has_inf |= isinf(distance);
+ result += distance;
}
+ CHECKFLOATVAL(result, has_inf, true);
PG_RETURN_FLOAT8(result);
}
/***********************************************************************
**
** Routines for 2D points.
**
***********************************************************************/
@@ -1746,26 +1842,23 @@ point_out(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(path_encode(PATH_NONE, 1, pt));
}
/*
* point_recv - converts external binary format to point
*/
Datum
point_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- Point *point;
- point = (Point *) palloc(sizeof(Point));
- point->x = pq_getmsgfloat8(buf);
- point->y = pq_getmsgfloat8(buf);
- PG_RETURN_POINT_P(point);
+ PG_RETURN_POINT_P(point_construct(pq_getmsgfloat8(buf),
+ pq_getmsgfloat8(buf)));
}
/*
* point_send - converts point to binary format
*/
Datum
point_send(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
StringInfoData buf;
@@ -1784,153 +1877,162 @@ point_construct(double x, double y)
result->x = x;
result->y = y;
return result;
}
static Point *
point_copy(Point *pt)
{
- Point *result;
-
- if (!PointerIsValid(pt))
- return NULL;
-
- result = (Point *) palloc(sizeof(Point));
-
- result->x = pt->x;
- result->y = pt->y;
- return result;
+ return point_construct(pt->x, pt->y);
}
/*----------------------------------------------------------
* Relational operators for Points.
* Since we do have a sense of coordinates being
* "equal" to a given accuracy (point_vert, point_horiz),
* the other ops must preserve that sense. This means
* that results may, strictly speaking, be a lie (unless
* EPSILON = 0.0).
*---------------------------------------------------------*/
Datum
point_left(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(FLOAT_LT(pt1->x, pt2->x));
}
Datum
point_right(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(FLOAT_GT(pt1->x, pt2->x));
}
Datum
point_above(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_GT(pt1->y, pt2->y));
}
Datum
point_below(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_LT(pt1->y, pt2->y));
}
Datum
point_vert(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
+ PG_RETURN_BOOL(FLOAT_EQ(pt1->x, pt2->x));
}
Datum
point_horiz(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_EQ(pt1->y, pt2->y));
}
Datum
point_eq(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_EQ(pt1->x, pt2->x) && FLOAT_EQ(pt1->y, pt2->y));
}
Datum
point_ne(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPne(pt1->x, pt2->x) || FPne(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_NE(pt1->x, pt2->x) || FLOAT_NE(pt1->y, pt2->y));
}
/*----------------------------------------------------------
* "Arithmetic" operators on points.
*---------------------------------------------------------*/
Datum
point_distance(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_FLOAT8(HYPOT(pt1->x - pt2->x, pt1->y - pt2->y));
+ PG_RETURN_FLOAT8(point_dt(pt1, pt2));
}
-double
+static double
point_dt(Point *pt1, Point *pt2)
{
+ double xdiff,
+ ydiff,
+ result;
+
+ FLOAT_MI(xdiff, pt1->x, pt2->x);
+ FLOAT_MI(ydiff, pt1->y, pt2->y);
+ FLOAT_HYPOT(result, xdiff, ydiff);
+
#ifdef GEODEBUG
printf("point_dt- segment (%f,%f),(%f,%f) length is %f\n",
- pt1->x, pt1->y, pt2->x, pt2->y, HYPOT(pt1->x - pt2->x, pt1->y - pt2->y));
+ pt1->x, pt1->y, pt2->x, pt2->y, result);
#endif
- return HYPOT(pt1->x - pt2->x, pt1->y - pt2->y);
+
+ return result;
}
Datum
point_slope(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_FLOAT8(point_sl(pt1, pt2));
+ PG_RETURN_FLOAT8(slope(pt1->x, pt2->x, pt1->y, pt2->y));
}
double
-point_sl(Point *pt1, Point *pt2)
+slope(double x1, double x2, double y1, double y2)
{
- return (FPeq(pt1->x, pt2->x)
- ? (double) DBL_MAX
- : (pt1->y - pt2->y) / (pt1->x - pt2->x));
+ double xdiff;
+ double ydiff;
+ double result;
+
+ if (FLOAT_EQ(x1, x2))
+ return DBL_MAX;
+
+ FLOAT_MI(xdiff, x1, x2);
+ FLOAT_MI(ydiff, y1, y2);
+ FLOAT_DIV(result, ydiff, xdiff);
+
+ return result;
}
/***********************************************************************
**
** Routines for 2D line segments.
**
***********************************************************************/
/*----------------------------------------------------------
@@ -2002,24 +2104,21 @@ lseg_send(PG_FUNCTION_ARGS)
/* lseg_construct -
* form a LSEG from two Points.
*/
Datum
lseg_construct(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
LSEG *result = (LSEG *) palloc(sizeof(LSEG));
- result->p[0].x = pt1->x;
- result->p[0].y = pt1->y;
- result->p[1].x = pt2->x;
- result->p[1].y = pt2->y;
+ statlseg_construct(result, pt1, pt2);
PG_RETURN_LSEG_P(result);
}
/* like lseg_construct, but assume space already allocated */
static void
statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
{
lseg->p[0].x = pt1->x;
lseg->p[0].y = pt1->y;
@@ -2048,154 +2147,146 @@ lseg_intersect(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
PG_RETURN_BOOL(lseg_intersect_internal(l1, l2));
}
static bool
lseg_intersect_internal(LSEG *l1, LSEG *l2)
{
- LINE ln;
Point *interpt;
- bool retval;
- line_construct_pts(&ln, &l2->p[0], &l2->p[1]);
- interpt = interpt_sl(l1, &ln);
+ interpt = line_interpt_internal(line_construct_pts(&l1->p[0], &l1->p[1]),
+ line_construct_pts(&l2->p[0], &l2->p[1]));
- if (interpt != NULL && on_ps_internal(interpt, l2))
- retval = true; /* interpt on l1 and l2 */
- else
- retval = false;
- return retval;
+ return (interpt != NULL &&
+ point_lseg_cmp(interpt, l1) == 0 &&
+ point_lseg_cmp(interpt, l2) == 0);
}
Datum
lseg_parallel(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(point_sl(&l1->p[0], &l1->p[1]),
- point_sl(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_EQ(slope(l1->p[0].x, l1->p[1].x, l1->p[0].y, l1->p[1].y),
+ slope(l2->p[0].x, l2->p[1].x, l2->p[0].y, l2->p[1].y)));
}
/* lseg_perp()
* Determine if two line segments are perpendicular.
*
* This code did not get the correct answer for
* '((0,0),(0,1))'::lseg ?-| '((0,0),(1,0))'::lseg
* So, modified it to check explicitly for slope of vertical line
- * returned by point_sl() and the results seem better.
+ * returned by slope() and the results seem better.
* - thomas 1998-01-31
*/
Datum
lseg_perp(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
double m1,
m2;
- m1 = point_sl(&(l1->p[0]), &(l1->p[1]));
- m2 = point_sl(&(l2->p[0]), &(l2->p[1]));
+ m1 = slope(l1->p[0].x, l1->p[1].x, l1->p[0].y, l1->p[1].y);
+ m2 = slope(l2->p[0].x, l2->p[1].x, l2->p[0].y, l2->p[1].y);
#ifdef GEODEBUG
printf("lseg_perp- slopes are %g and %g\n", m1, m2);
#endif
- if (FPzero(m1))
- PG_RETURN_BOOL(FPeq(m2, DBL_MAX));
- else if (FPzero(m2))
- PG_RETURN_BOOL(FPeq(m1, DBL_MAX));
- PG_RETURN_BOOL(FPeq(m1 / m2, -1.0));
+ PG_RETURN_BOOL(FLOAT_EQ(m1, -m2));
}
Datum
lseg_vertical(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
+ PG_RETURN_BOOL(FLOAT_EQ(lseg->p[0].x, lseg->p[1].x));
}
Datum
lseg_horizontal(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
+ PG_RETURN_BOOL(FLOAT_EQ(lseg->p[0].y, lseg->p[1].y));
}
Datum
lseg_eq(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(l1->p[0].x, l2->p[0].x) &&
- FPeq(l1->p[0].y, l2->p[0].y) &&
- FPeq(l1->p[1].x, l2->p[1].x) &&
- FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(FLOAT_EQ(l1->p[0].x, l2->p[0].x) &&
+ FLOAT_EQ(l1->p[0].y, l2->p[0].y) &&
+ FLOAT_EQ(l1->p[1].x, l2->p[1].x) &&
+ FLOAT_EQ(l1->p[1].y, l2->p[1].y));
}
Datum
lseg_ne(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(!FPeq(l1->p[0].x, l2->p[0].x) ||
- !FPeq(l1->p[0].y, l2->p[0].y) ||
- !FPeq(l1->p[1].x, l2->p[1].x) ||
- !FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(FLOAT_NE(l1->p[0].x, l2->p[0].x) ||
+ FLOAT_NE(l1->p[0].y, l2->p[0].y) ||
+ FLOAT_NE(l1->p[1].x, l2->p[1].x) ||
+ FLOAT_NE(l1->p[1].y, l2->p[1].y));
}
Datum
lseg_lt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_LT(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_le(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_gt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_GT(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_ge(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_GE(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* lseg_distance -
* If two segments don't intersect, then the closest
* point will be from one of the endpoints to the other
@@ -2217,92 +2308,78 @@ lseg_distance(PG_FUNCTION_ARGS)
*/
static double
lseg_dt(LSEG *l1, LSEG *l2)
{
double result,
d;
if (lseg_intersect_internal(l1, l2))
return 0.0;
- d = dist_ps_internal(&l1->p[0], l2);
- result = d;
+ result = dist_ps_internal(&l1->p[0], l2);
d = dist_ps_internal(&l1->p[1], l2);
- result = Min(result, d);
+ if (FLOAT_LT(d, result))
+ result = d;
d = dist_ps_internal(&l2->p[0], l1);
- result = Min(result, d);
+ if (FLOAT_LT(d, result))
+ result = d;
d = dist_ps_internal(&l2->p[1], l1);
- result = Min(result, d);
+ if (FLOAT_LT(d, result))
+ result = d;
return result;
}
Datum
lseg_center(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- Point *result;
- result = (Point *) palloc(sizeof(Point));
+ PG_RETURN_POINT_P(lseg_center_internal(lseg));
+}
- result->x = (lseg->p[0].x + lseg->p[1].x) / 2.0;
- result->y = (lseg->p[0].y + lseg->p[1].y) / 2.0;
+static Point *
+lseg_center_internal(LSEG *lseg)
+{
+ double xsum;
+ double ysum;
+ double x;
+ double y;
- PG_RETURN_POINT_P(result);
+ FLOAT_PL(xsum, lseg->p[0].x, lseg->p[1].x);
+ FLOAT_PL(ysum, lseg->p[0].y, lseg->p[1].y);
+ FLOAT_DIV(x, xsum, 2.0);
+ FLOAT_DIV(y, ysum, 2.0);
+
+ return point_construct(x, y);
}
static Point *
lseg_interpt_internal(LSEG *l1, LSEG *l2)
{
Point *result;
- LINE tmp1,
- tmp2;
- /*
- * Find the intersection of the appropriate lines, if any.
- */
- line_construct_pts(&tmp1, &l1->p[0], &l1->p[1]);
- line_construct_pts(&tmp2, &l2->p[0], &l2->p[1]);
- result = line_interpt_internal(&tmp1, &tmp2);
+ /* Find the intersection of the appropriate lines, if any */
+ result = line_interpt_internal(line_construct_pts(&l1->p[0], &l1->p[1]),
+ line_construct_pts(&l2->p[0], &l2->p[1]));
if (!PointerIsValid(result))
return NULL;
- /*
- * If the line intersection point isn't within l1 (or equivalently l2),
- * there is no valid segment intersection point at all.
- */
- if (!on_ps_internal(result, l1) ||
- !on_ps_internal(result, l2))
+ /* Test if the point extend the segments of the lines */
+ if (point_lseg_cmp(result, l1) != 0 ||
+ point_lseg_cmp(result, l2) != 0)
{
pfree(result);
return NULL;
}
- /*
- * If there is an intersection, then check explicitly for matching
- * endpoints since there may be rounding effects with annoying lsb
- * residue. - tgl 1997-07-09
- */
- if ((FPeq(l1->p[0].x, l2->p[0].x) && FPeq(l1->p[0].y, l2->p[0].y)) ||
- (FPeq(l1->p[0].x, l2->p[1].x) && FPeq(l1->p[0].y, l2->p[1].y)))
- {
- result->x = l1->p[0].x;
- result->y = l1->p[0].y;
- }
- else if ((FPeq(l1->p[1].x, l2->p[0].x) && FPeq(l1->p[1].y, l2->p[0].y)) ||
- (FPeq(l1->p[1].x, l2->p[1].x) && FPeq(l1->p[1].y, l2->p[1].y)))
- {
- result->x = l1->p[1].x;
- result->y = l1->p[1].y;
- }
-
return result;
}
/* lseg_interpt -
* Find the intersection point of two segments (if any).
*/
Datum
lseg_interpt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
@@ -2336,22 +2413,29 @@ dist_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
PG_RETURN_FLOAT8(dist_pl_internal(pt, line));
}
static double
dist_pl_internal(Point *pt, LINE *line)
{
- return fabs((line->A * pt->x + line->B * pt->y + line->C) /
- HYPOT(line->A, line->B));
+ double val;
+ double hyp;
+ double result;
+
+ val = point_on_line_value(pt, line);
+ FLOAT_HYPOT(hyp, line->A, line->B);
+ FLOAT_DIV(result, val, hyp);
+
+ return fabs(result);
}
/*
* Distance from a point to a lseg
*/
Datum
dist_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
@@ -2365,69 +2449,65 @@ dist_ps_internal(Point *pt, LSEG *lseg)
double m; /* slope of perp. */
LINE *ln;
double result,
tmpdist;
Point *ip;
/*
* Construct a line perpendicular to the input segment and through the
* input point
*/
- if (lseg->p[1].x == lseg->p[0].x)
- m = 0;
- else if (lseg->p[1].y == lseg->p[0].y)
- m = (double) DBL_MAX; /* slope is infinite */
- else
- m = (lseg->p[0].x - lseg->p[1].x) / (lseg->p[1].y - lseg->p[0].y);
+ m = slope(lseg->p[1].y, lseg->p[0].y, lseg->p[0].x, lseg->p[1].x);
ln = line_construct_pm(pt, m);
#ifdef GEODEBUG
printf("dist_ps- line is A=%g B=%g C=%g from (point) slope (%f,%f) %g\n",
ln->A, ln->B, ln->C, pt->x, pt->y, m);
#endif
/*
* Calculate distance to the line segment or to the nearest endpoint of
* the segment.
*/
/* intersection is on the line segment? */
- if ((ip = interpt_sl(lseg, ln)) != NULL)
+ ip = interpt_sl(lseg, ln);
+ if (ip != NULL)
{
/* yes, so use distance to the intersection point */
result = point_dt(pt, ip);
#ifdef GEODEBUG
printf("dist_ps- distance is %f to intersection point is (%f,%f)\n",
result, ip->x, ip->y);
#endif
}
else
{
/* no, so use distance to the nearer endpoint */
result = point_dt(pt, &lseg->p[0]);
tmpdist = point_dt(pt, &lseg->p[1]);
- if (tmpdist < result)
+ if (FLOAT_LT(tmpdist, result))
result = tmpdist;
}
return result;
}
/*
* Distance from a point to a path
*/
Datum
dist_ppath(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
PATH *path = PG_GETARG_PATH_P(1);
- float8 result = 0.0; /* keep compiler quiet */
+ float8 result;
bool have_min = false;
float8 tmp;
int i;
LSEG lseg;
switch (path->npts)
{
case 0:
/* no points in path? then result is undefined... */
PG_RETURN_NULL();
@@ -2436,36 +2516,37 @@ dist_ppath(PG_FUNCTION_ARGS)
result = point_dt(pt, &path->p[0]);
break;
default:
/* make sure the path makes sense... */
Assert(path->npts > 1);
/*
* the distance from a point to a path is the smallest distance
* from the point to any of its constituent segments.
*/
+ result = 0.0;
for (i = 0; i < path->npts; i++)
{
int iprev;
if (i > 0)
iprev = i - 1;
else
{
if (!path->closed)
continue;
iprev = path->npts - 1; /* include the closure segment */
}
statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
tmp = dist_ps_internal(pt, &lseg);
- if (!have_min || tmp < result)
+ if (!have_min || FLOAT_LT(tmp, result))
{
result = tmp;
have_min = true;
}
}
break;
}
PG_RETURN_FLOAT8(result);
}
@@ -2498,22 +2579,21 @@ dist_sl(PG_FUNCTION_ARGS)
LINE *line = PG_GETARG_LINE_P(1);
float8 result,
d2;
if (has_interpt_sl(lseg, line))
result = 0.0;
else
{
result = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- /* XXX shouldn't we take the min not max? */
- if (d2 > result)
+ if (FLOAT_LT(d2, result))
result = d2;
}
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a lseg to a box
*/
Datum
@@ -2554,28 +2634,28 @@ dist_lb(PG_FUNCTION_ARGS)
}
/*
* Distance from a circle to a polygon
*/
Datum
dist_cpoly(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
POLYGON *poly = PG_GETARG_POLYGON_P(1);
+ float8 dist;
float8 result;
/* calculate distance to center, and subtract radius */
- result = dist_ppoly_internal(&circle->center, poly);
-
- result -= circle->radius;
- if (result < 0)
- result = 0;
+ dist = dist_ppoly_internal(&circle->center, poly);
+ FLOAT_MI(result, dist, circle->radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a point to a polygon
*/
Datum
dist_ppoly(PG_FUNCTION_ARGS)
{
@@ -2630,56 +2710,54 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
for (i = 0; (i < poly->npts - 1); i++)
{
seg.p[0].x = poly->p[i].x;
seg.p[0].y = poly->p[i].y;
seg.p[1].x = poly->p[i + 1].x;
seg.p[1].y = poly->p[i + 1].y;
d = dist_ps_internal(pt, &seg);
#ifdef GEODEBUG
printf("dist_ppoly_internal- segment %d distance is %f\n", (i + 1), d);
#endif
- if (d < result)
+ if (FLOAT_LT(d, result))
result = d;
}
return result;
}
/*---------------------------------------------------------------------
* interpt_
* Intersection point of objects.
* We choose to ignore the "point" of intersection between
* lines and boxes, since there are typically two.
*-------------------------------------------------------------------*/
/* Get intersection point of lseg and line; returns NULL if no intersection */
static Point *
interpt_sl(LSEG *lseg, LINE *line)
{
- LINE tmp;
Point *p;
- line_construct_pts(&tmp, &lseg->p[0], &lseg->p[1]);
- p = line_interpt_internal(&tmp, line);
+ line_construct_pts(&lseg->p[0], &lseg->p[1]);
+ p = line_interpt_internal(line_construct_pts(&lseg->p[0], &lseg->p[1]),
+ line);
#ifdef GEODEBUG
printf("interpt_sl- segment is (%.*g %.*g) (%.*g %.*g)\n",
DBL_DIG, lseg->p[0].x, DBL_DIG, lseg->p[0].y, DBL_DIG, lseg->p[1].x, DBL_DIG, lseg->p[1].y);
- printf("interpt_sl- segment becomes line A=%.*g B=%.*g C=%.*g\n",
- DBL_DIG, tmp.A, DBL_DIG, tmp.B, DBL_DIG, tmp.C);
#endif
if (PointerIsValid(p))
{
#ifdef GEODEBUG
printf("interpt_sl- intersection point is (%.*g %.*g)\n", DBL_DIG, p->x, DBL_DIG, p->y);
#endif
- if (on_ps_internal(p, lseg))
+ if (point_lseg_cmp(p, lseg) == 0)
{
#ifdef GEODEBUG
printf("interpt_sl- intersection point is on segment\n");
#endif
}
else
p = NULL;
}
return p;
@@ -2704,174 +2782,81 @@ has_interpt_sl(LSEG *lseg, LINE *line)
/* close_pl -
* The intersection point of a perpendicular of the line
* through the point.
*/
Datum
close_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
+
+ PG_RETURN_POINT_P(close_pl_internal(pt, line));
+}
+
+static Point *
+close_pl_internal(Point *pt, LINE *line)
+{
Point *result;
- LINE *tmp;
- double invm;
+ double val;
- result = (Point *) palloc(sizeof(Point));
-
- if (FPzero(line->B)) /* vertical? */
+ if (FLOAT_EQ(line->B, 0.0)) /* vertical? */
{
- result->x = line->C;
- result->y = pt->y;
- PG_RETURN_POINT_P(result);
+ FLOAT_DIV(val, -line->C, line->A);
+ result = point_construct(val, pt->y);
}
- if (FPzero(line->A)) /* horizontal? */
+ else if (FLOAT_EQ(line->A, 0.0)) /* horizontal? */
{
- result->x = pt->x;
- result->y = line->C;
- PG_RETURN_POINT_P(result);
+ FLOAT_DIV(val, -line->C, line->B);
+ result = point_construct(val, pt->y);
+ }
+ else
+ {
+ /*
+ * Drop a perpendicular and find the intersection point
+ *
+ * We need to invert and flip the sign on the slope to get
+ * the perpendicular. line_interpt_internal() returns NULL,
+ * when the lines are parallel. We might lose some precision
+ * on the division, but it cannot be as much to turn the line.
+ */
+ FLOAT_DIV(val, line->B, line->A);
+ result = line_interpt_internal(line_construct_pm(pt, val), line);
+ Assert(result != NULL);
}
- /* drop a perpendicular and find the intersection point */
- /* invert and flip the sign on the slope to get a perpendicular */
- invm = line->B / line->A;
- tmp = line_construct_pm(pt, invm);
- result = line_interpt_internal(tmp, line);
- Assert(result != NULL);
- PG_RETURN_POINT_P(result);
+ return result;
}
-/* close_ps()
- * Closest point on line segment to specified point.
- * Take the closest endpoint if the point is left, right,
- * above, or below the segment, otherwise find the intersection
- * point of the segment and its perpendicular through the point.
+/*
+ * Closest point on line segment to specified point
*
- * Some tricky code here, relying on boolean expressions
- * evaluating to only zero or one to use as an array index.
- * bug fixes by gthaker@atl.lmco.com; May 1, 1998
+ * We find the closest point to the line, and then test if the point is
+ * on the line segment. If it is not, we return the closest endpoint
+ * of the line segment.
*/
Datum
close_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
- Point *result = NULL;
- LINE *tmp;
- double invm;
- int xh,
- yh;
+ Point *result;
+ int order;
-#ifdef GEODEBUG
- printf("close_sp:pt->x %f pt->y %f\nlseg(0).x %f lseg(0).y %f lseg(1).x %f lseg(1).y %f\n",
- pt->x, pt->y, lseg->p[0].x, lseg->p[0].y,
- lseg->p[1].x, lseg->p[1].y);
-#endif
+ result = close_pl_internal(pt, line_construct_pts(&lseg->p[0], &lseg->p[1]));
+ order = point_lseg_cmp(result, lseg);
- /* xh (or yh) is the index of upper x( or y) end point of lseg */
- /* !xh (or !yh) is the index of lower x( or y) end point of lseg */
- xh = lseg->p[0].x < lseg->p[1].x;
- yh = lseg->p[0].y < lseg->p[1].y;
-
- if (FPeq(lseg->p[0].x, lseg->p[1].x)) /* vertical? */
- {
-#ifdef GEODEBUG
- printf("close_ps- segment is vertical\n");
-#endif
- /* first check if point is below or above the entire lseg. */
- if (pt->y < lseg->p[!yh].y)
- result = point_copy(&lseg->p[!yh]); /* below the lseg */
- else if (pt->y > lseg->p[yh].y)
- result = point_copy(&lseg->p[yh]); /* above the lseg */
- if (result != NULL)
- PG_RETURN_POINT_P(result);
-
- /* point lines along (to left or right) of the vertical lseg. */
-
- result = (Point *) palloc(sizeof(Point));
- result->x = lseg->p[0].x;
- result->y = pt->y;
- PG_RETURN_POINT_P(result);
- }
- else if (FPeq(lseg->p[0].y, lseg->p[1].y)) /* horizontal? */
- {
-#ifdef GEODEBUG
- printf("close_ps- segment is horizontal\n");
-#endif
- /* first check if point is left or right of the entire lseg. */
- if (pt->x < lseg->p[!xh].x)
- result = point_copy(&lseg->p[!xh]); /* left of the lseg */
- else if (pt->x > lseg->p[xh].x)
- result = point_copy(&lseg->p[xh]); /* right of the lseg */
- if (result != NULL)
- PG_RETURN_POINT_P(result);
-
- /* point lines along (at top or below) the horiz. lseg. */
- result = (Point *) palloc(sizeof(Point));
- result->x = pt->x;
- result->y = lseg->p[0].y;
- PG_RETURN_POINT_P(result);
- }
-
- /*
- * vert. and horiz. cases are down, now check if the closest point is one
- * of the end points or someplace on the lseg.
- */
-
- invm = -1.0 / point_sl(&(lseg->p[0]), &(lseg->p[1]));
- tmp = line_construct_pm(&lseg->p[!yh], invm); /* lower edge of the
- * "band" */
- if (pt->y < (tmp->A * pt->x + tmp->C))
- { /* we are below the lower edge */
- result = point_copy(&lseg->p[!yh]); /* below the lseg, take lower
- * end pt */
-#ifdef GEODEBUG
- printf("close_ps below: tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- PG_RETURN_POINT_P(result);
- }
- tmp = line_construct_pm(&lseg->p[yh], invm); /* upper edge of the
- * "band" */
- if (pt->y > (tmp->A * pt->x + tmp->C))
- { /* we are below the lower edge */
- result = point_copy(&lseg->p[yh]); /* above the lseg, take higher
- * end pt */
-#ifdef GEODEBUG
- printf("close_ps above: tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- PG_RETURN_POINT_P(result);
- }
-
- /*
- * at this point the "normal" from point will hit lseg. The closest point
- * will be somewhere on the lseg
- */
- tmp = line_construct_pm(pt, invm);
-#ifdef GEODEBUG
- printf("close_ps- tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- result = interpt_sl(lseg, tmp);
-
- /*
- * ordinarily we should always find an intersection point, but that could
- * fail in the presence of NaN coordinates, and perhaps even from simple
- * roundoff issues. Return a SQL NULL if so.
- */
- if (result == NULL)
- PG_RETURN_NULL();
-
-#ifdef GEODEBUG
- printf("close_ps- result.x %f result.y %f\n", result->x, result->y);
-#endif
+ if (order < 0)
+ PG_RETURN_POINT_P(point_copy(&lseg->p[0]));
+ if (order > 0)
+ PG_RETURN_POINT_P(point_copy(&lseg->p[1]));
PG_RETURN_POINT_P(result);
}
/* close_lseg()
* Closest point to l1 on l2.
*/
Datum
close_lseg(PG_FUNCTION_ARGS)
{
@@ -2879,38 +2864,39 @@ close_lseg(PG_FUNCTION_ARGS)
LSEG *l2 = PG_GETARG_LSEG_P(1);
Point *result = NULL;
Point point;
double dist;
double d;
d = dist_ps_internal(&l1->p[0], l2);
dist = d;
memcpy(&point, &l1->p[0], sizeof(Point));
- if ((d = dist_ps_internal(&l1->p[1], l2)) < dist)
+ d = dist_ps_internal(&l1->p[1], l2);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&point, &l1->p[1], sizeof(Point));
}
- if (dist_ps_internal(&l2->p[0], l1) < dist)
+ if (FLOAT_LT(dist_ps_internal(&l2->p[0], l1), dist))
{
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&l2->p[0]),
LsegPGetDatum(l1)));
memcpy(&point, result, sizeof(Point));
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&point),
LsegPGetDatum(l2)));
}
- if (dist_ps_internal(&l2->p[1], l1) < dist)
+ if (FLOAT_LT(dist_ps_internal(&l2->p[1], l1), dist))
{
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&l2->p[1]),
LsegPGetDatum(l1)));
memcpy(&point, result, sizeof(Point));
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&point),
LsegPGetDatum(l2)));
}
@@ -2939,37 +2925,40 @@ close_pb(PG_FUNCTION_ARGS)
BoxPGetDatum(box))))
PG_RETURN_POINT_P(pt);
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&lseg, &box->low, &point);
dist = dist_ps_internal(pt, &lseg);
statlseg_construct(&seg, &box->high, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&seg, &box->low, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
statlseg_construct(&seg, &box->high, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
PG_RETURN_DATUM(DirectFunctionCall2(close_ps,
PointPGetDatum(pt),
LsegPGetDatum(&lseg)));
}
@@ -2991,21 +2980,21 @@ close_sl(PG_FUNCTION_ARGS)
Point *result;
float8 d1,
d2;
result = interpt_sl(lseg, line);
if (result)
PG_RETURN_POINT_P(result);
d1 = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- if (d1 < d2)
+ if (FLOAT_LT(d1, d2))
result = point_copy(&lseg->p[0]);
else
result = point_copy(&lseg->p[1]);
PG_RETURN_POINT_P(result);
#endif
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function \"close_sl\" not implemented")));
@@ -3024,21 +3013,21 @@ close_ls(PG_FUNCTION_ARGS)
Point *result;
float8 d1,
d2;
result = interpt_sl(lseg, line);
if (result)
PG_RETURN_POINT_P(result);
d1 = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- if (d1 < d2)
+ if (FLOAT_LT(d1, d2))
result = point_copy(&lseg->p[0]);
else
result = point_copy(&lseg->p[1]);
PG_RETURN_POINT_P(result);
}
/* close_sb()
* Closest point on or in box to line segment.
*/
@@ -3064,37 +3053,40 @@ close_sb(PG_FUNCTION_ARGS)
LsegPGetDatum(lseg)));
}
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&bseg, &box->low, &point);
dist = lseg_dt(lseg, &bseg);
statlseg_construct(&seg, &box->high, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&seg, &box->low, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (FLOAT_LE(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
statlseg_construct(&seg, &box->high, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (FLOAT_LE(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
/* OK, we now have the closest line segment on the box boundary */
PG_RETURN_DATUM(DirectFunctionCall2(close_lseg,
LsegPGetDatum(lseg),
LsegPGetDatum(&bseg)));
}
@@ -3113,71 +3105,152 @@ close_lb(PG_FUNCTION_ARGS)
errmsg("function \"close_lb\" not implemented")));
PG_RETURN_NULL();
}
/*---------------------------------------------------------------------
* on_
* Whether one object lies completely within another.
*-------------------------------------------------------------------*/
-/* on_pl -
- * Does the point satisfy the equation?
+/*
+ * Test if the point satisfies the line equation
*/
Datum
on_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
- PG_RETURN_BOOL(FPzero(line->A * pt->x + line->B * pt->y + line->C));
+ PG_RETURN_BOOL(point_on_line_value(pt, line) == 0);
}
-
-/* on_ps -
- * Determine colinearity by detecting a triangle inequality.
- * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
- */
Datum
on_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
PG_RETURN_BOOL(on_ps_internal(pt, lseg));
}
+/*
+ * Test if the point is on the line segment
+ *
+ * We are first comparing the point with the line segment, even though
+ * this comparison makes no sense when the point is not on the line.
+ * The reason of it is that this check is cheaper than constructing
+ * the line and validating the line equation.
+ */
static bool
on_ps_internal(Point *pt, LSEG *lseg)
{
- return FPeq(point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1]),
- point_dt(&lseg->p[0], &lseg->p[1]));
+ return (point_lseg_cmp(pt, lseg) == 0 &&
+ FLOAT_EQ(point_on_line_value(pt,
+ line_construct_pts(&lseg->p[0], &lseg->p[1])),
+ 0.0));
+}
+
+/*
+ * Calculate the line equation for a point
+ *
+ * This returns the result of the line equation Ax + By + C. The result
+ * needs to be 0 for the point to be on the line.
+ */
+static double
+point_on_line_value(Point *pt, LINE *line)
+{
+ double mulx;
+ double muly;
+ double sumxy;
+ double result;
+
+ FLOAT_MUL(mulx, line->A, pt->x);
+ FLOAT_MUL(muly, line->B, pt->y);
+ FLOAT_PL(sumxy, mulx, muly);
+ FLOAT_PL(result, sumxy, line->C);
+
+ return result;
+}
+
+/*
+ * Compare a point with a line segment
+ *
+ * This function is useful when the point and line segment are on
+ * the same line. It returns
+ *
+ * * -1, if the point extends the first edge of the segment
+ * * 0, if the point is on the segment
+ * * 1, if the point extends the last edge of the segment.
+ *
+ * This check is not as trivial as checking if a point is inside a box,
+ * because the edges of the line segments are not normalised. We are
+ * doing the same checks for both x and y coordinates, to be correct
+ * when one of the coordinates of the point is on one edge of the segment.
+ */
+static int
+point_lseg_cmp(Point *pt, LSEG *lseg)
+{
+ if (FLOAT_LT(lseg->p[0].x, lseg->p[1].x))
+ {
+ if (FLOAT_LT(pt->x, lseg->p[0].x))
+ return -1;
+ if (FLOAT_GT(pt->x, lseg->p[1].x))
+ return 1;
+ }
+ else if (FLOAT_GT(lseg->p[0].x, lseg->p[1].x))
+ {
+ if (FLOAT_GT(pt->x, lseg->p[0].x))
+ return -1;
+ if (FLOAT_LT(pt->x, lseg->p[1].x))
+ return 1;
+ }
+ else if (FLOAT_LE(lseg->p[0].y, lseg->p[1].y))
+ {
+ if (FLOAT_LT(pt->y, lseg->p[0].y))
+ return -1;
+ if (FLOAT_GT(pt->y, lseg->p[1].y))
+ return 1;
+ }
+ else
+ {
+ if (FLOAT_GT(pt->y, lseg->p[0].y))
+ return -1;
+ if (FLOAT_LT(pt->y, lseg->p[1].y))
+ return 1;
+ }
+
+ return 0;
}
Datum
on_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
BOX *box = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(pt->x <= box->high.x && pt->x >= box->low.x &&
- pt->y <= box->high.y && pt->y >= box->low.y);
+ PG_RETURN_BOOL(FLOAT_LE(pt->x, box->high.x) &&
+ FLOAT_GE(pt->x, box->low.x) &&
+ FLOAT_LE(pt->y, box->high.y) &&
+ FLOAT_GE(pt->y, box->low.y));
}
Datum
box_contain_pt(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *pt = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(pt->x <= box->high.x && pt->x >= box->low.x &&
- pt->y <= box->high.y && pt->y >= box->low.y);
+ PG_RETURN_BOOL(FLOAT_LE(pt->x, box->high.x) &&
+ FLOAT_GE(pt->x, box->low.x) &&
+ FLOAT_LE(pt->y, box->high.y) &&
+ FLOAT_GE(pt->y, box->low.y));
}
/* on_ppath -
* Whether a point lies within (on) a polyline.
* If open, we have to (groan) check each segment.
* (uses same algorithm as for point intersecting segment - tgl 1997-07-09)
* If closed, we use the old O(n) ray method for point-in-polygon.
* The ray is horizontal, from pt out to the right.
* Each segment that crosses the ray counts as an
* intersection; note that an endpoint or edge may touch
@@ -3195,22 +3268,21 @@ on_ppath(PG_FUNCTION_ARGS)
b;
/*-- OPEN --*/
if (!path->closed)
{
n = path->npts - 1;
a = point_dt(pt, &path->p[0]);
for (i = 0; i < n; i++)
{
b = point_dt(pt, &path->p[i + 1]);
- if (FPeq(a + b,
- point_dt(&path->p[i], &path->p[i + 1])))
+ if (FLOAT_EQ(a + b, point_dt(&path->p[i], &path->p[i + 1])))
PG_RETURN_BOOL(true);
a = b;
}
PG_RETURN_BOOL(false);
}
/*-- CLOSED --*/
PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
}
@@ -3268,24 +3340,24 @@ inter_sl(PG_FUNCTION_ARGS)
*/
Datum
inter_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
BOX *box = PG_GETARG_BOX_P(1);
BOX lbox;
LSEG bseg;
Point point;
- lbox.low.x = Min(lseg->p[0].x, lseg->p[1].x);
- lbox.low.y = Min(lseg->p[0].y, lseg->p[1].y);
- lbox.high.x = Max(lseg->p[0].x, lseg->p[1].x);
- lbox.high.y = Max(lseg->p[0].y, lseg->p[1].y);
+ lbox.low.x = FLOAT_MIN(lseg->p[0].x, lseg->p[1].x);
+ lbox.low.y = FLOAT_MIN(lseg->p[0].y, lseg->p[1].y);
+ lbox.high.x = FLOAT_MAX(lseg->p[0].x, lseg->p[1].x);
+ lbox.high.y = FLOAT_MAX(lseg->p[0].y, lseg->p[1].y);
/* nothing close to overlap? then not going to intersect */
if (!box_ov(&lbox, box))
PG_RETURN_BOOL(false);
/* an endpoint of segment is inside box? then clearly intersects */
if (DatumGetBool(DirectFunctionCall2(on_pb,
PointPGetDatum(&lseg->p[0]),
BoxPGetDatum(box))) ||
DatumGetBool(DirectFunctionCall2(on_pb,
@@ -3377,27 +3449,27 @@ make_bound_box(POLYGON *poly)
y1,
x2,
y2;
if (poly->npts > 0)
{
x2 = x1 = poly->p[0].x;
y2 = y1 = poly->p[0].y;
for (i = 1; i < poly->npts; i++)
{
- if (poly->p[i].x < x1)
+ if (FLOAT_LT(poly->p[i].x, x1))
x1 = poly->p[i].x;
- if (poly->p[i].x > x2)
+ if (FLOAT_GT(poly->p[i].x, x2))
x2 = poly->p[i].x;
- if (poly->p[i].y < y1)
+ if (FLOAT_LT(poly->p[i].y, y1))
y1 = poly->p[i].y;
- if (poly->p[i].y > y2)
+ if (FLOAT_GT(poly->p[i].y, y2))
y2 = poly->p[i].y;
}
box_fill(&(poly->boundbox), x1, x2, y1, y2);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot create bounding box for empty polygon")));
}
@@ -3420,21 +3492,21 @@ poly_in(PG_FUNCTION_ARGS)
int base_size;
bool isopen;
if ((npts = pair_count(str, ',')) <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"polygon", str)));
base_size = sizeof(poly->p[0]) * npts;
- size = offsetof(POLYGON, p) +base_size;
+ size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
@@ -3475,21 +3547,21 @@ poly_recv(PG_FUNCTION_ARGS)
int32 npts;
int32 i;
int size;
npts = pq_getmsgint(buf, sizeof(int32));
if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(POLYGON, p)) / sizeof(Point)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of points in external \"polygon\" value")));
- size = offsetof(POLYGON, p) +sizeof(poly->p[0]) * npts;
+ size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * npts;
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
for (i = 0; i < npts; i++)
{
poly->p[i].x = pq_getmsgfloat8(buf);
poly->p[i].y = pq_getmsgfloat8(buf);
}
@@ -3525,21 +3597,21 @@ poly_send(PG_FUNCTION_ARGS)
* the right most point of A left of the left most point
* of B?
*-------------------------------------------------------*/
Datum
poly_left(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.x < polyb->boundbox.low.x;
+ result = FLOAT_LT(polya->boundbox.high.x, polyb->boundbox.low.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3548,21 +3620,21 @@ poly_left(PG_FUNCTION_ARGS)
* the right most point of A at or left of the right most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overleft(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.x <= polyb->boundbox.high.x;
+ result = FLOAT_LE(polya->boundbox.high.x, polyb->boundbox.high.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3571,21 +3643,21 @@ poly_overleft(PG_FUNCTION_ARGS)
* the left most point of A right of the right most point
* of B?
*-------------------------------------------------------*/
Datum
poly_right(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.x > polyb->boundbox.high.x;
+ result = FLOAT_GT(polya->boundbox.low.x, polyb->boundbox.high.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3594,21 +3666,21 @@ poly_right(PG_FUNCTION_ARGS)
* the left most point of A at or right of the left most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overright(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.x >= polyb->boundbox.low.x;
+ result = FLOAT_GE(polya->boundbox.low.x, polyb->boundbox.low.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3617,21 +3689,21 @@ poly_overright(PG_FUNCTION_ARGS)
* the upper most point of A below the lower most point
* of B?
*-------------------------------------------------------*/
Datum
poly_below(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.y < polyb->boundbox.low.y;
+ result = FLOAT_LT(polya->boundbox.high.y, polyb->boundbox.low.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3640,21 +3712,21 @@ poly_below(PG_FUNCTION_ARGS)
* the upper most point of A at or below the upper most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overbelow(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.y <= polyb->boundbox.high.y;
+ result = FLOAT_LE(polya->boundbox.high.y, polyb->boundbox.high.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3663,21 +3735,21 @@ poly_overbelow(PG_FUNCTION_ARGS)
* the lower most point of A above the upper most point
* of B?
*-------------------------------------------------------*/
Datum
poly_above(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.y > polyb->boundbox.high.y;
+ result = FLOAT_GT(polya->boundbox.low.y, polyb->boundbox.high.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3686,21 +3758,21 @@ poly_above(PG_FUNCTION_ARGS)
* the lower most point of A at or above the lower most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overabove(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.y >= polyb->boundbox.low.y;
+ result = FLOAT_GE(polya->boundbox.low.y, polyb->boundbox.low.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3738,21 +3810,21 @@ poly_same(PG_FUNCTION_ARGS)
*-----------------------------------------------------------------*/
Datum
poly_overlap(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
/* Quick check by bounding box */
result = (polya->npts > 0 && polyb->npts > 0 &&
- box_ov(&polya->boundbox, &polyb->boundbox)) ? true : false;
+ box_ov(&polya->boundbox, &polyb->boundbox));
/*
* Brute-force algorithm - try to find intersected edges, if so then
* polygons are overlapped else check is one polygon inside other or not
* by testing single point of them.
*/
if (result)
{
int ia,
ib;
@@ -3779,22 +3851,21 @@ poly_overlap(PG_FUNCTION_ARGS)
}
/*
* move current endpoint to the first point of next edge
*/
sa.p[0] = sa.p[1];
}
if (result == false)
{
- result = (point_inside(polya->p, polyb->npts, polyb->p)
- ||
+ result = (point_inside(polya->p, polyb->npts, polyb->p) ||
point_inside(polyb->p, polya->npts, polya->p));
}
}
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
@@ -3814,21 +3885,21 @@ poly_overlap(PG_FUNCTION_ARGS)
static bool
touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
{
/* point a is on s, b is not */
LSEG t;
t.p[0] = *a;
t.p[1] = *b;
-#define POINTEQ(pt1, pt2) (FPeq((pt1)->x, (pt2)->x) && FPeq((pt1)->y, (pt2)->y))
+#define POINTEQ(pt1, pt2) (FLOAT_EQ((pt1)->x, (pt2)->x) && FLOAT_EQ((pt1)->y, (pt2)->y))
if (POINTEQ(a, s->p))
{
if (on_ps_internal(s->p + 1, &t))
return lseg_inside_poly(b, s->p + 1, poly, start);
}
else if (POINTEQ(a, s->p + 1))
{
if (on_ps_internal(s->p, &t))
return lseg_inside_poly(b, s->p, poly, start);
}
@@ -3894,30 +3965,25 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
if (res)
res = lseg_inside_poly(t.p + 1, interpt, poly, i + 1);
pfree(interpt);
}
s.p[0] = s.p[1];
}
if (res && !intersection)
{
- Point p;
-
/*
* if X-intersection wasn't found then check central point of tested
* segment. In opposite case we already check all subsegments
*/
- p.x = (t.p[0].x + t.p[1].x) / 2.0;
- p.y = (t.p[0].y + t.p[1].y) / 2.0;
-
- res = point_inside(&p, poly->npts, poly->p);
+ res = point_inside(lseg_center_internal(&t), poly->npts, poly->p);
}
return res;
}
/*-----------------------------------------------------------------
* Determine if polygon A contains polygon B.
*-----------------------------------------------------------------*/
Datum
poly_contain(PG_FUNCTION_ARGS)
@@ -4013,92 +4079,102 @@ poly_distance(PG_FUNCTION_ARGS)
/***********************************************************************
**
** Routines for 2D points.
**
***********************************************************************/
Datum
construct_point(PG_FUNCTION_ARGS)
{
- float8 x = PG_GETARG_FLOAT8(0);
- float8 y = PG_GETARG_FLOAT8(1);
+ double x = PG_GETARG_FLOAT8(0);
+ double y = PG_GETARG_FLOAT8(1);
PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_add(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_PL(x, p1->x, p2->x);
+ FLOAT_PL(y, p1->y, p2->y);
- result->x = (p1->x + p2->x);
- result->y = (p1->y + p2->y);
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_sub(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_MI(x, p1->x, p2->x);
+ FLOAT_MI(y, p1->y, p2->y);
- result->x = (p1->x - p2->x);
- result->y = (p1->y - p2->y);
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_mul(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double val1;
+ double val2;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_MUL(val1, p1->x, p2->x);
+ FLOAT_MUL(val2, p1->y, p2->y);
+ FLOAT_MI(x, val1, val2);
- result->x = (p1->x * p2->x) - (p1->y * p2->y);
- result->y = (p1->x * p2->y) + (p1->y * p2->x);
+ FLOAT_MUL(val1, p1->x, p2->y);
+ FLOAT_MUL(val2, p1->y, p2->x);
+ FLOAT_PL(y, val1, val2);
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_div(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double mul1;
+ double mul2;
double div;
+ double val;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_MUL(mul1, p2->x, p2->x);
+ FLOAT_MUL(mul2, p2->y, p2->y);
+ FLOAT_PL(div, mul1, mul2);
- div = (p2->x * p2->x) + (p2->y * p2->y);
+ FLOAT_MUL(mul1, p1->x, p2->x);
+ FLOAT_MUL(mul2, p1->y, p2->y);
+ FLOAT_PL(val, mul1, mul2);
+ FLOAT_DIV(x, val, div);
- if (div == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_MUL(mul1, p1->y, p2->x);
+ FLOAT_MUL(mul2, p1->x, p2->y);
+ FLOAT_MI(val, mul1, mul2);
+ FLOAT_DIV(y, val, div);
- result->x = ((p1->x * p2->x) + (p1->y * p2->y)) / div;
- result->y = ((p2->x * p1->y) - (p2->y * p1->x)) / div;
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
/***********************************************************************
**
** Routines for 2D boxes.
**
***********************************************************************/
Datum
@@ -4108,37 +4184,49 @@ points_box(PG_FUNCTION_ARGS)
Point *p2 = PG_GETARG_POINT_P(1);
PG_RETURN_BOX_P(box_construct(p1->x, p2->x, p1->y, p2->y));
}
Datum
box_add(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
+ double highx;
+ double lowx;
+ double highy;
+ double lowy;
- PG_RETURN_BOX_P(box_construct((box->high.x + p->x),
- (box->low.x + p->x),
- (box->high.y + p->y),
- (box->low.y + p->y)));
+ FLOAT_PL(highx, box->high.x, p->x);
+ FLOAT_PL(lowx, box->low.x, p->x);
+ FLOAT_PL(highy, box->high.y, p->y);
+ FLOAT_PL(lowy, box->low.y, p->y);
+
+ PG_RETURN_BOX_P(box_construct(highx, lowx, highy, lowy));
}
Datum
box_sub(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
+ double highx;
+ double lowx;
+ double highy;
+ double lowy;
- PG_RETURN_BOX_P(box_construct((box->high.x - p->x),
- (box->low.x - p->x),
- (box->high.y - p->y),
- (box->low.y - p->y)));
+ FLOAT_MI(highx, box->high.x, p->x);
+ FLOAT_MI(lowx, box->low.x, p->x);
+ FLOAT_MI(highy, box->high.y, p->y);
+ FLOAT_MI(lowy, box->low.y, p->y);
+
+ PG_RETURN_BOX_P(box_construct(highx, lowx, highy, lowy));
}
Datum
box_mul(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
BOX *result;
Point *high,
*low;
@@ -4200,24 +4288,24 @@ point_box(PG_FUNCTION_ARGS)
*/
Datum
boxes_bound_box(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0),
*box2 = PG_GETARG_BOX_P(1),
*container;
container = (BOX *) palloc(sizeof(BOX));
- container->high.x = Max(box1->high.x, box2->high.x);
- container->low.x = Min(box1->low.x, box2->low.x);
- container->high.y = Max(box1->high.y, box2->high.y);
- container->low.y = Min(box1->low.y, box2->low.y);
+ container->high.x = FLOAT_MAX(box1->high.x, box2->high.x);
+ container->low.x = FLOAT_MIN(box1->low.x, box2->low.x);
+ container->high.y = FLOAT_MAX(box1->high.y, box2->high.y);
+ container->low.y = FLOAT_MIN(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(container);
}
/***********************************************************************
**
** Routines for 2D paths.
**
***********************************************************************/
@@ -4272,41 +4360,49 @@ path_add(PG_FUNCTION_ARGS)
/* path_add_pt()
* Translation operators.
*/
Datum
path_add_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
int i;
+ double val;
for (i = 0; i < path->npts; i++)
{
- path->p[i].x += point->x;
- path->p[i].y += point->y;
+ FLOAT_PL(val, path->p[i].x, point->x);
+ path->p[i].x = val;
+
+ FLOAT_PL(val, path->p[i].y, point->y);
+ path->p[i].y = val;
}
PG_RETURN_PATH_P(path);
}
Datum
path_sub_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
int i;
+ double val;
for (i = 0; i < path->npts; i++)
{
- path->p[i].x -= point->x;
- path->p[i].y -= point->y;
+ FLOAT_MI(val, path->p[i].x, point->x);
+ path->p[i].x = val;
+
+ FLOAT_MI(val, path->p[i].y, point->y);
+ path->p[i].y = val;
}
PG_RETURN_PATH_P(path);
}
/* path_mul_pt()
* Rotation and scaling operators.
*/
Datum
path_mul_pt(PG_FUNCTION_ARGS)
@@ -4374,21 +4470,21 @@ path_poly(PG_FUNCTION_ARGS)
/* This is not very consistent --- other similar cases return NULL ... */
if (!path->closed)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("open path cannot be converted to polygon")));
/*
* Never overflows: the old size fit in MaxAllocSize, and the new size is
* just a small constant larger.
*/
- size = offsetof(POLYGON, p) +sizeof(poly->p[0]) * path->npts;
+ size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * path->npts;
poly = (POLYGON *) palloc(size);
SET_VARSIZE(poly, size);
poly->npts = path->npts;
for (i = 0; i < path->npts; i++)
{
poly->p[i].x = path->p[i].x;
poly->p[i].y = path->p[i].y;
}
@@ -4483,26 +4579,26 @@ poly_path(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0);
PATH *path;
int size;
int i;
/*
* Never overflows: the old size fit in MaxAllocSize, and the new size is
* smaller by a small constant.
*/
- size = offsetof(PATH, p) +sizeof(path->p[0]) * poly->npts;
+ size = offsetof(PATH, p) + sizeof(path->p[0]) * poly->npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = poly->npts;
- path->closed = TRUE;
+ path->closed = true;
/* prevent instability in unused pad bytes */
path->dummy = 0;
for (i = 0; i < poly->npts; i++)
{
path->p[i].x = poly->p[i].x;
path->p[i].y = poly->p[i].y;
}
PG_RETURN_PATH_P(path);
@@ -4546,30 +4642,29 @@ circle_in(PG_FUNCTION_ARGS)
if (*cp == LDELIM)
s = cp;
}
pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle", str);
if (*s == DELIM)
s++;
circle->radius = single_decode(s, &s, "circle", str);
- if (circle->radius < 0)
+ if (FLOAT_LT(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"circle", str)));
while (depth > 0)
{
- if ((*s == RDELIM)
- || ((*s == RDELIM_C) && (depth == 1)))
+ if ((*s == RDELIM) || ((*s == RDELIM_C) && (depth == 1)))
{
depth--;
s++;
while (isspace((unsigned char) *s))
s++;
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
@@ -4614,21 +4709,21 @@ circle_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
CIRCLE *circle;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
circle->center.x = pq_getmsgfloat8(buf);
circle->center.y = pq_getmsgfloat8(buf);
circle->radius = pq_getmsgfloat8(buf);
- if (circle->radius < 0)
+ if (FLOAT_LT(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid radius in external \"circle\" value")));
PG_RETURN_CIRCLE_P(circle);
}
/*
* circle_send - converts circle to binary format
*/
@@ -4652,334 +4747,371 @@ circle_send(PG_FUNCTION_ARGS)
*---------------------------------------------------------*/
/* circles identical?
*/
Datum
circle_same(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle1->radius, circle2->radius) &&
- FPeq(circle1->center.x, circle2->center.x) &&
- FPeq(circle1->center.y, circle2->center.y));
+ PG_RETURN_BOOL(FLOAT_EQ(circle1->radius, circle2->radius) &&
+ FLOAT_EQ(circle1->center.x, circle2->center.x) &&
+ FLOAT_EQ(circle1->center.y, circle2->center.y));
}
/* circle_overlap - does circle1 overlap circle2?
*/
Datum
circle_overlap(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double sum_radius;
- PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
- circle1->radius + circle2->radius));
+ FLOAT_PL(sum_radius, circle1->radius, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&circle1->center, &circle2->center),
+ sum_radius));
}
/* circle_overleft - is the right edge of circle1 at or left of
* the right edge of circle2?
*/
Datum
circle_overleft(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPle((circle1->center.x + circle1->radius),
- (circle2->center.x + circle2->radius)));
+ FLOAT_PL(x1, circle1->center.x, circle1->radius);
+ FLOAT_PL(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(x1, x2));
}
/* circle_left - is circle1 strictly left of circle2?
*/
Datum
circle_left(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPlt((circle1->center.x + circle1->radius),
- (circle2->center.x - circle2->radius)));
+ FLOAT_PL(x1, circle1->center.x, circle1->radius);
+ FLOAT_MI(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LT(x1, x2));
}
/* circle_right - is circle1 strictly right of circle2?
*/
Datum
circle_right(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPgt((circle1->center.x - circle1->radius),
- (circle2->center.x + circle2->radius)));
+ FLOAT_MI(x1, circle1->center.x, circle1->radius);
+ FLOAT_PL(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GT(x1, x2));
}
/* circle_overright - is the left edge of circle1 at or right of
* the left edge of circle2?
*/
Datum
circle_overright(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPge((circle1->center.x - circle1->radius),
- (circle2->center.x - circle2->radius)));
+ FLOAT_MI(x1, circle1->center.x, circle1->radius);
+ FLOAT_MI(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GE(x1, x2));
}
/* circle_contained - is circle1 contained by circle2?
*/
Datum
circle_contained(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double radius_diff;
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle1->radius), circle2->radius));
+ FLOAT_MI(radius_diff, circle2->radius, circle1->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&circle1->center, &circle2->center),
+ radius_diff));
}
/* circle_contain - does circle1 contain circle2?
*/
Datum
circle_contain(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double radius_diff;
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle2->radius), circle1->radius));
+ FLOAT_MI(radius_diff, circle1->radius, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&circle1->center, &circle2->center),
+ radius_diff));
}
/* circle_below - is circle1 strictly below circle2?
*/
Datum
circle_below(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPlt((circle1->center.y + circle1->radius),
- (circle2->center.y - circle2->radius)));
+ FLOAT_PL(y1, circle1->center.y, circle1->radius);
+ FLOAT_MI(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LT(y1, y2));
}
/* circle_above - is circle1 strictly above circle2?
*/
Datum
circle_above(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPgt((circle1->center.y - circle1->radius),
- (circle2->center.y + circle2->radius)));
+ FLOAT_MI(y1, circle1->center.y, circle1->radius);
+ FLOAT_PL(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GT(y1, y2));
}
/* circle_overbelow - is the upper edge of circle1 at or below
* the upper edge of circle2?
*/
Datum
circle_overbelow(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPle((circle1->center.y + circle1->radius),
- (circle2->center.y + circle2->radius)));
+ FLOAT_PL(y1, circle1->center.y, circle1->radius);
+ FLOAT_PL(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(y1, y2));
}
/* circle_overabove - is the lower edge of circle1 at or above
* the lower edge of circle2?
*/
Datum
circle_overabove(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPge((circle1->center.y - circle1->radius),
- (circle2->center.y - circle2->radius)));
+ FLOAT_MI(y1, circle1->center.y, circle1->radius);
+ FLOAT_MI(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GE(y1, y2));
}
/* circle_relop - is area(circle1) relop area(circle2), within
* our accuracy constraint?
*/
Datum
circle_eq(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_EQ(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ne(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_NE(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_lt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_LT(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_gt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_GT(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_le(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_LE(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ge(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_GE(circle_ar(circle1), circle_ar(circle2)));
}
/*----------------------------------------------------------
* "Arithmetic" operators on circles.
*---------------------------------------------------------*/
static CIRCLE *
-circle_copy(CIRCLE *circle)
+circle_construct(double x, double y, double radius)
{
CIRCLE *result;
- if (!PointerIsValid(circle))
- return NULL;
-
result = (CIRCLE *) palloc(sizeof(CIRCLE));
- memcpy((char *) result, (char *) circle, sizeof(CIRCLE));
+ result->center.x = x;
+ result->center.y = y;
+ result->radius = radius;
+
return result;
}
/* circle_add_pt()
* Translation operator.
*/
Datum
circle_add_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
- result = circle_copy(circle);
-
- result->center.x += point->x;
- result->center.y += point->y;
-
- PG_RETURN_CIRCLE_P(result);
+ PG_RETURN_CIRCLE_P(circle_construct(point->x, point->y, circle->radius));
}
Datum
circle_sub_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
+ double x;
+ double y;
- result = circle_copy(circle);
+ FLOAT_MI(x, circle->center.x, point->x);
+ FLOAT_MI(y, circle->center.y, point->y);
- result->center.x -= point->x;
- result->center.y -= point->y;
-
- PG_RETURN_CIRCLE_P(result);
+ PG_RETURN_CIRCLE_P(circle_construct(x, y, circle->radius));
}
/* circle_mul_pt()
* Rotation and scaling operators.
*/
Datum
circle_mul_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
Point *p;
-
- result = circle_copy(circle);
+ double hyp;
+ double radius;
p = DatumGetPointP(DirectFunctionCall2(point_mul,
PointPGetDatum(&circle->center),
PointPGetDatum(point)));
- result->center.x = p->x;
- result->center.y = p->y;
- result->radius *= HYPOT(point->x, point->y);
- PG_RETURN_CIRCLE_P(result);
+ FLOAT_HYPOT(hyp, point->x, point->y);
+ FLOAT_MUL(radius, circle->radius, hyp);
+
+ PG_RETURN_CIRCLE_P(circle_construct(p->x, p->y, radius));
}
Datum
circle_div_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
Point *p;
-
- result = circle_copy(circle);
+ double hyp;
+ double radius;
p = DatumGetPointP(DirectFunctionCall2(point_div,
PointPGetDatum(&circle->center),
PointPGetDatum(point)));
- result->center.x = p->x;
- result->center.y = p->y;
- result->radius /= HYPOT(point->x, point->y);
- PG_RETURN_CIRCLE_P(result);
+ FLOAT_HYPOT(hyp, point->x, point->y);
+ FLOAT_DIV(radius, circle->radius, hyp);
+
+ PG_RETURN_CIRCLE_P(circle_construct(p->x, p->y, radius));
}
/* circle_area - returns the area of the circle.
*/
Datum
circle_area(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
PG_RETURN_FLOAT8(circle_ar(circle));
}
/* circle_diameter - returns the diameter of the circle.
*/
Datum
circle_diameter(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
+ double result;
- PG_RETURN_FLOAT8(2 * circle->radius);
+ FLOAT_MUL(result, circle->radius, 2.0);
+
+ PG_RETURN_FLOAT8(result);
}
/* circle_radius - returns the radius of the circle.
*/
Datum
circle_radius(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
@@ -4988,253 +5120,284 @@ circle_radius(PG_FUNCTION_ARGS)
/* circle_distance - returns the distance between
* two circles.
*/
Datum
circle_distance(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- float8 result;
+ double d;
+ double sum_radius;
+ double result;
+
+ d = point_dt(&circle1->center, &circle2->center);
+ FLOAT_PL(sum_radius, circle1->radius, circle2->radius);
+ FLOAT_MI(result, d, sum_radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
- result = point_dt(&circle1->center, &circle2->center)
- - (circle1->radius + circle2->radius);
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
Datum
circle_contain_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
double d;
d = point_dt(&circle->center, point);
- PG_RETURN_BOOL(d <= circle->radius);
+ PG_RETURN_BOOL(FLOAT_LE(d, circle->radius));
}
Datum
pt_contained_circle(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
double d;
d = point_dt(&circle->center, point);
- PG_RETURN_BOOL(d <= circle->radius);
+ PG_RETURN_BOOL(FLOAT_LE(d, circle->radius));
}
/* dist_pc - returns the distance between
* a point and a circle.
*/
Datum
dist_pc(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
- float8 result;
+ double d;
+ double result;
+
+ d = point_dt(point, &circle->center);
+ FLOAT_MI(result, d, circle->radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
- result = point_dt(point, &circle->center) - circle->radius;
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a circle to a point
*/
Datum
dist_cpoint(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- float8 result;
+ double d;
+ double result;
+
+ d = point_dt(point, &circle->center);
+ FLOAT_MI(result, d, circle->radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
- result = point_dt(point, &circle->center) - circle->radius;
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
/* circle_center - returns the center point of the circle.
*/
Datum
circle_center(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
- Point *result;
- result = (Point *) palloc(sizeof(Point));
- result->x = circle->center.x;
- result->y = circle->center.y;
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_copy(&circle->center));
}
/* circle_ar - returns the area of the circle.
*/
static double
circle_ar(CIRCLE *circle)
{
- return M_PI * (circle->radius * circle->radius);
+ double radius2;
+ double result;
+
+ FLOAT_MUL(radius2, circle->radius, circle->radius);
+ FLOAT_MUL(result, radius2, M_PI);
+
+ return result;
}
/*----------------------------------------------------------
* Conversion operators.
*---------------------------------------------------------*/
Datum
cr_circle(PG_FUNCTION_ARGS)
{
Point *center = PG_GETARG_POINT_P(0);
- float8 radius = PG_GETARG_FLOAT8(1);
- CIRCLE *result;
+ double radius = PG_GETARG_FLOAT8(1);
- result = (CIRCLE *) palloc(sizeof(CIRCLE));
-
- result->center.x = center->x;
- result->center.y = center->y;
- result->radius = radius;
-
- PG_RETURN_CIRCLE_P(result);
+ PG_RETURN_CIRCLE_P(circle_construct(center->x, center->y, radius));
}
Datum
circle_box(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
BOX *box;
double delta;
box = (BOX *) palloc(sizeof(BOX));
- delta = circle->radius / sqrt(2.0);
+ FLOAT_DIV(delta, circle->radius, sqrt(2.0));
- box->high.x = circle->center.x + delta;
- box->low.x = circle->center.x - delta;
- box->high.y = circle->center.y + delta;
- box->low.y = circle->center.y - delta;
+ FLOAT_PL(box->high.x, circle->center.x, delta);
+ FLOAT_MI(box->low.x, circle->center.x, delta);
+ FLOAT_PL(box->high.y, circle->center.y, delta);
+ FLOAT_MI(box->low.y, circle->center.y, delta);
PG_RETURN_BOX_P(box);
}
/* box_circle()
* Convert a box to a circle.
*/
Datum
box_circle(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
CIRCLE *circle;
+ double sum;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
- circle->center.x = (box->high.x + box->low.x) / 2;
- circle->center.y = (box->high.y + box->low.y) / 2;
+ FLOAT_PL(sum, box->high.x, box->low.x);
+ FLOAT_DIV(circle->center.x, sum, 2.0);
+
+ FLOAT_PL(sum, box->high.y, box->low.y);
+ FLOAT_DIV(circle->center.y, sum, 2.0);
circle->radius = point_dt(&circle->center, &box->high);
PG_RETURN_CIRCLE_P(circle);
}
Datum
circle_poly(PG_FUNCTION_ARGS)
{
int32 npts = PG_GETARG_INT32(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
POLYGON *poly;
int base_size,
size;
int i;
double angle;
double anglestep;
+ double mul;
- if (FPzero(circle->radius))
+ if (circle->radius == 0.0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert circle with radius zero to polygon")));
if (npts < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must request at least 2 points")));
base_size = sizeof(poly->p[0]) * npts;
- size = offsetof(POLYGON, p) +base_size;
+ size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
- anglestep = (2.0 * M_PI) / npts;
+ FLOAT_DIV(anglestep, 2.0 * M_PI, npts);
for (i = 0; i < npts; i++)
{
- angle = i * anglestep;
- poly->p[i].x = circle->center.x - (circle->radius * cos(angle));
- poly->p[i].y = circle->center.y + (circle->radius * sin(angle));
+ FLOAT_MUL(angle, anglestep, i);
+
+ FLOAT_MUL(mul, circle->radius, cos(angle));
+ FLOAT_MI(poly->p[i].x, circle->center.x, mul);
+
+ FLOAT_MUL(mul, circle->radius, sin(angle));
+ FLOAT_PL(poly->p[i].y, circle->center.y, mul);
}
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
}
/* poly_circle - convert polygon to circle
*
* XXX This algorithm should use weighted means of line segments
* rather than straight average values of points - tgl 97/01/21.
*/
Datum
poly_circle(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0);
CIRCLE *circle;
+ bool has_inf;
int i;
+ double sum;
+ double dist;
if (poly->npts < 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot convert empty polygon to circle")));
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
- circle->center.x = 0;
- circle->center.y = 0;
- circle->radius = 0;
-
+ has_inf = false;
+ sum = 0.0;
for (i = 0; i < poly->npts; i++)
{
- circle->center.x += poly->p[i].x;
- circle->center.y += poly->p[i].y;
+ has_inf |= isinf(poly->p[i].x);
+ sum += poly->p[i].x;
}
- circle->center.x /= poly->npts;
- circle->center.y /= poly->npts;
+ CHECKFLOATVAL(sum, has_inf, true);
+ FLOAT_DIV(circle->center.x, sum, poly->npts);
+ has_inf = false;
+ sum = 0.0;
for (i = 0; i < poly->npts; i++)
- circle->radius += point_dt(&poly->p[i], &circle->center);
- circle->radius /= poly->npts;
+ {
+ has_inf |= isinf(poly->p[i].y);
+ sum += poly->p[i].y;
+ }
+ CHECKFLOATVAL(sum, has_inf, true);
+ FLOAT_DIV(circle->center.y, sum, poly->npts);
+
+ has_inf = false;
+ sum = 0.0;
+ for (i = 0; i < poly->npts; i++)
+ {
+ dist = point_dt(&poly->p[i], &circle->center);
+ has_inf |= isinf(dist);
+ sum += dist;
+ }
+ CHECKFLOATVAL(sum, has_inf, true);
+ FLOAT_DIV(circle->radius, sum, poly->npts);
PG_RETURN_CIRCLE_P(circle);
}
/***********************************************************************
**
** Private routines for multiple types.
**
***********************************************************************/
@@ -5262,31 +5425,31 @@ point_inside(Point *p, int npts, Point *plist)
int i = 0;
double x,
y;
int cross,
total_cross = 0;
if (npts <= 0)
return 0;
/* compute first polygon point relative to single point */
- x0 = plist[0].x - p->x;
- y0 = plist[0].y - p->y;
+ FLOAT_MI(x0, plist[0].x, p->x);
+ FLOAT_MI(y0, plist[0].y, p->y);
prev_x = x0;
prev_y = y0;
/* loop over polygon points and aggregate total_cross */
for (i = 1; i < npts; i++)
{
/* compute next polygon point relative to single point */
- x = plist[i].x - p->x;
- y = plist[i].y - p->y;
+ FLOAT_MI(x, plist[i].x, p->x);
+ FLOAT_MI(y, plist[i].y, p->y);
/* compute previous to current point crossing */
if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
return 2;
total_cross += cross;
prev_x = x;
prev_y = y;
}
@@ -5306,129 +5469,139 @@ point_inside(Point *p, int npts, Point *plist)
* Returns +/-1 if one point is on the positive X-axis.
* Returns 0 if both points are on the positive X-axis, or there is no crossing.
* Returns POINT_ON_POLYGON if the segment contains (0,0).
* Wow, that is one confusing API, but it is used above, and when summed,
* can tell is if a point is in a polygon.
*/
static int
lseg_crossing(double x, double y, double prev_x, double prev_y)
{
- double z;
int y_sign;
+ double xdiff;
+ double ydiff;
+ double mulxy;
+ double mulyx;
- if (FPzero(y))
+ if (FLOAT_EQ(y, 0.0))
{ /* y == 0, on X axis */
- if (FPzero(x)) /* (x,y) is (0,0)? */
+ if (FLOAT_EQ(x, 0.0)) /* (x,y) is (0,0)? */
return POINT_ON_POLYGON;
- else if (FPgt(x, 0))
+ else if (FLOAT_GT(x, 0.0))
{ /* x > 0 */
- if (FPzero(prev_y)) /* y and prev_y are zero */
+ if (FLOAT_EQ(prev_y, 0.0)) /* y and prev_y are zero */
/* prev_x > 0? */
- return FPgt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
- return FPlt(prev_y, 0) ? 1 : -1;
+ return FLOAT_GT(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
+ return FLOAT_LT(prev_y, 0.0) ? 1 : -1;
}
else
{ /* x < 0, x not on positive X axis */
- if (FPzero(prev_y))
+ if (FLOAT_EQ(prev_y, 0.0))
/* prev_x < 0? */
- return FPlt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
+ return FLOAT_LT(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
return 0;
}
}
else
{ /* y != 0 */
/* compute y crossing direction from previous point */
- y_sign = FPgt(y, 0) ? 1 : -1;
+ y_sign = FLOAT_GT(y, 0.0) ? 1 : -1;
- if (FPzero(prev_y))
+ if (FLOAT_EQ(prev_y, 0.0))
/* previous point was on X axis, so new point is either off or on */
- return FPlt(prev_x, 0) ? 0 : y_sign;
- else if (FPgt(y_sign * prev_y, 0))
+ return prev_x < 0.0 ? 0 : y_sign;
+ else if ((y_sign < 0 && FLOAT_LT(prev_y, 0.0)) ||
+ (y_sign > 0 && FLOAT_GT(prev_y, 0.0)))
/* both above or below X axis */
return 0; /* same sign */
else
{ /* y and prev_y cross X-axis */
- if (FPge(x, 0) && FPgt(prev_x, 0))
+ if (FLOAT_GE(x, 0.0) && FLOAT_GT(prev_x, 0.0))
/* both non-negative so cross positive X-axis */
return 2 * y_sign;
- if (FPlt(x, 0) && FPle(prev_x, 0))
+ if (FLOAT_LT(x, 0.0) && FLOAT_LE(prev_x, 0.0))
/* both non-positive so do not cross positive X-axis */
return 0;
/* x and y cross axises, see URL above point_inside() */
- z = (x - prev_x) * y - (y - prev_y) * x;
- if (FPzero(z))
+ FLOAT_MI(xdiff, x, prev_x);
+ FLOAT_MI(ydiff, y, prev_y);
+ FLOAT_MUL(mulxy, xdiff, y);
+ FLOAT_MUL(mulyx, ydiff, x);
+ if (FLOAT_EQ(mulxy, mulyx))
return POINT_ON_POLYGON;
- return FPgt((y_sign * z), 0) ? 0 : 2 * y_sign;
+ if ((y_sign < 0 && FLOAT_LT(mulxy, mulyx)) ||
+ (y_sign > 0 && FLOAT_GT(mulxy, mulyx)))
+ return 0;
+
+ return 2 * y_sign;
}
}
}
static bool
plist_same(int npts, Point *p1, Point *p2)
{
int i,
ii,
j;
/* find match for first point */
for (i = 0; i < npts; i++)
{
- if ((FPeq(p2[i].x, p1[0].x))
- && (FPeq(p2[i].y, p1[0].y)))
+ if (FLOAT_EQ(p2[i].x, p1[0].x) && FLOAT_EQ(p2[i].y, p1[0].y))
{
/* match found? then look forward through remaining points */
for (ii = 1, j = i + 1; ii < npts; ii++, j++)
{
if (j >= npts)
j = 0;
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (FLOAT_NE(p2[j].x, p1[ii].x) ||
+ FLOAT_NE(p2[j].y, p1[ii].y))
{
#ifdef GEODEBUG
printf("plist_same- %d failed forward match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after forward match\n", ii, npts);
#endif
if (ii == npts)
- return TRUE;
+ return true;
/* match not found forwards? then look backwards */
for (ii = 1, j = i - 1; ii < npts; ii++, j--)
{
if (j < 0)
j = (npts - 1);
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (FLOAT_NE(p2[j].x, p1[ii].x) ||
+ FLOAT_NE(p2[j].y, p1[ii].y))
{
#ifdef GEODEBUG
printf("plist_same- %d failed reverse match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after reverse match\n", ii, npts);
#endif
if (ii == npts)
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/*-------------------------------------------------------------------------
* Determine the hypotenuse.
*
* If required, x and y are swapped to make x the larger number. The
* traditional formula of x^2+y^2 is rearranged to factor x outside the
* sqrt. This allows computation of the hypotenuse for significantly
* larger values, and with a higher precision than when using the naive
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 565a034..537414c 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -76,21 +76,21 @@
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/float.h"
#include "utils/geo_decls.h"
/*
* Comparator for qsort
*
* We don't need to use the floating point macros in here, because this
- * is going only going to be used in a place to effect the performance
+ * is only going to be used in a place to effect the performance
* of the index, not the correctness.
*/
static int
compareDoubles(const void *a, const void *b)
{
double x = *(double *) a;
double y = *(double *) b;
if (x == y)
return 0;
@@ -225,80 +225,80 @@ nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant)
else
next_rect_box->range_box_y.right.high = centroid->right.high;
return next_rect_box;
}
/* Can any range from range_box overlap with this argument? */
static bool
overlap2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->low) &&
- FPle(range_box->left.low, query->high);
+ return range_box->right.high >= query->low &&
+ range_box->left.low <= query->high;
}
/* Can any rectangle from rect_box overlap with this argument? */
static bool
overlap4D(RectBox *rect_box, RangeBox *query)
{
return overlap2D(&rect_box->range_box_x, &query->left) &&
overlap2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box contain this argument? */
static bool
contain2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->high) &&
- FPle(range_box->left.low, query->low);
+ return range_box->right.high >= query->high &&
+ range_box->left.low <= query->low;
}
/* Can any rectangle from rect_box contain this argument? */
static bool
contain4D(RectBox *rect_box, RangeBox *query)
{
return contain2D(&rect_box->range_box_x, &query->left) &&
contain2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box be contained by this argument? */
static bool
contained2D(RangeBox *range_box, Range *query)
{
- return FPle(range_box->left.low, query->high) &&
- FPge(range_box->left.high, query->low) &&
- FPle(range_box->right.low, query->high) &&
- FPge(range_box->right.high, query->low);
+ return range_box->left.low <= query->high &&
+ range_box->left.high >= query->low &&
+ range_box->right.low <= query->high &&
+ range_box->right.high >= query->low;
}
/* Can any rectangle from rect_box be contained by this argument? */
static bool
contained4D(RectBox *rect_box, RangeBox *query)
{
return contained2D(&rect_box->range_box_x, &query->left) &&
contained2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box to be lower than this argument? */
static bool
lower2D(RangeBox *range_box, Range *query)
{
- return FPlt(range_box->left.low, query->low) &&
- FPlt(range_box->right.low, query->low);
+ return range_box->left.low < query->low &&
+ range_box->right.low < query->low;
}
/* Can any range from range_box to be higher than this argument? */
static bool
higher2D(RangeBox *range_box, Range *query)
{
- return FPgt(range_box->left.high, query->high) &&
- FPgt(range_box->right.high, query->high);
+ return range_box->left.high > query->high &&
+ range_box->right.high > query->high;
}
/* Can any rectangle from rect_box be left of this argument? */
static bool
left4D(RectBox *rect_box, RangeBox *query)
{
return lower2D(&rect_box->range_box_x, &query->left);
}
/* Can any rectangle from rect_box does not extend the right of this argument? */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index fe9bc60..7a6f34b 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -1,63 +1,42 @@
/*-------------------------------------------------------------------------
*
* geo_decls.h - Declarations for various 2D constructs.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/geo_decls.h
*
- * NOTE
- * These routines do *not* use the float types from adt/.
- *
- * XXX These routines were not written by a numerical analyst.
- *
* XXX I have made some attempt to flesh out the operators
* and data types. There are still some more to do. - tgl 97/04/19
*
*-------------------------------------------------------------------------
*/
#ifndef GEO_DECLS_H
#define GEO_DECLS_H
#include <math.h>
#include "fmgr.h"
/*--------------------------------------------------------------------
* Useful floating point utilities and constants.
*-------------------------------------------------------------------*/
-
-#define EPSILON 1.0E-06
-
-#ifdef EPSILON
-#define FPzero(A) (fabs(A) <= EPSILON)
-#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
-#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
-#define FPlt(A,B) ((B) - (A) > EPSILON)
-#define FPle(A,B) ((A) - (B) <= EPSILON)
-#define FPgt(A,B) ((A) - (B) > EPSILON)
-#define FPge(A,B) ((B) - (A) <= EPSILON)
-#else
-#define FPzero(A) ((A) == 0)
-#define FPeq(A,B) ((A) == (B))
-#define FPne(A,B) ((A) != (B))
-#define FPlt(A,B) ((A) < (B))
-#define FPle(A,B) ((A) <= (B))
-#define FPgt(A,B) ((A) > (B))
-#define FPge(A,B) ((A) >= (B))
-#endif
-
-#define HYPOT(A, B) pg_hypot(A, B)
+#define FLOAT_HYPOT(result, val1, val2) \
+do { \
+ result = pg_hypot(val1, val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), \
+ (val1) == 0.0 && (val2) == 0.0); \
+} while(0)
/*---------------------------------------------------------------------
* Point - (x,y)
*-------------------------------------------------------------------*/
typedef struct
{
double x,
y;
} Point;
@@ -193,22 +172,20 @@ extern Datum point_horiz(PG_FUNCTION_ARGS);
extern Datum point_eq(PG_FUNCTION_ARGS);
extern Datum point_ne(PG_FUNCTION_ARGS);
extern Datum point_distance(PG_FUNCTION_ARGS);
extern Datum point_slope(PG_FUNCTION_ARGS);
extern Datum point_add(PG_FUNCTION_ARGS);
extern Datum point_sub(PG_FUNCTION_ARGS);
extern Datum point_mul(PG_FUNCTION_ARGS);
extern Datum point_div(PG_FUNCTION_ARGS);
/* private routines */
-extern double point_dt(Point *pt1, Point *pt2);
-extern double point_sl(Point *pt1, Point *pt2);
extern double pg_hypot(double x, double y);
/* public lseg routines */
extern Datum lseg_in(PG_FUNCTION_ARGS);
extern Datum lseg_out(PG_FUNCTION_ARGS);
extern Datum lseg_recv(PG_FUNCTION_ARGS);
extern Datum lseg_send(PG_FUNCTION_ARGS);
extern Datum lseg_intersect(PG_FUNCTION_ARGS);
extern Datum lseg_parallel(PG_FUNCTION_ARGS);
extern Datum lseg_perp(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/line.out b/src/test/regress/expected/line.out
index f20abdc..16ff522 100644
--- a/src/test/regress/expected/line.out
+++ b/src/test/regress/expected/line.out
@@ -32,56 +32,56 @@ LINE 1: INSERT INTO LINE_TBL VALUES ('[1,2,3, 4');
^
INSERT INTO LINE_TBL VALUES ('[(,2),(3,4)]');
ERROR: invalid input syntax for type line: "[(,2),(3,4)]"
LINE 1: INSERT INTO LINE_TBL VALUES ('[(,2),(3,4)]');
^
INSERT INTO LINE_TBL VALUES ('[(1,2),(3,4)');
ERROR: invalid input syntax for type line: "[(1,2),(3,4)"
LINE 1: INSERT INTO LINE_TBL VALUES ('[(1,2),(3,4)');
^
select * from LINE_TBL;
- s
----------------------------------------------
+ s
+--------------------------
{1,-1,1}
- {1,-1,0}
- {-0.4,-1,-6}
- {-0.000184615384615385,-1,15.3846153846154}
- {1,-1,11}
+ {6,-6,0}
+ {6,15,90}
+ {-240,-1300000,20000000}
+ {22,-22,242}
{0,-1,3}
{-1,0,3}
(7 rows)
-- functions and operators
SELECT * FROM LINE_TBL WHERE (s <-> line '[(1,2),(3,4)]') < 10;
- s
----------------------------------------------
+ s
+--------------------------
{1,-1,1}
- {1,-1,0}
- {-0.4,-1,-6}
- {-0.000184615384615385,-1,15.3846153846154}
- {1,-1,11}
+ {6,-6,0}
+ {6,15,90}
+ {-240,-1300000,20000000}
+ {22,-22,242}
{0,-1,3}
{-1,0,3}
(7 rows)
SELECT * FROM LINE_TBL WHERE (point '(0.1,0.1)' <-> s) < 1;
s
----------
{1,-1,1}
- {1,-1,0}
+ {6,-6,0}
(2 rows)
SELECT * FROM LINE_TBL WHERE (lseg '[(0.1,0.1),(0.2,0.2)]' <-> s) < 1;
s
----------
{1,-1,1}
- {1,-1,0}
+ {6,-6,0}
(2 rows)
SELECT line '[(1,1),(2,1)]' <-> line '[(-1,-1),(-2,-1)]';
?column?
----------
2
(1 row)
SELECT lseg '[(1,1),(2,1)]' <-> line '[(-1,-1),(-2,-1)]';
?column?
@@ -247,21 +247,21 @@ SELECT ?| line '[(0,0),(0,1)]'; -- true
SELECT ?| line '[(0,0),(1,1)]'; -- false
?column?
----------
f
(1 row)
SELECT line(point '(1,2)', point '(3,4)');
line
----------
- {1,-1,1}
+ {2,-2,2}
(1 row)
SELECT line '[(1,2),(3,4)]' = line '[(3,4),(4,5)]'; -- true
?column?
----------
t
(1 row)
SELECT line '[(1,2),(3,4)]' = line '[(3,4),(4,4)]'; -- false
?column?
diff --git a/src/test/regress/expected/point.out b/src/test/regress/expected/point.out
index bfc0962..6319652 100644
--- a/src/test/regress/expected/point.out
+++ b/src/test/regress/expected/point.out
@@ -249,49 +249,49 @@ SELECT '' AS three, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS dista
CREATE TEMP TABLE point_gist_tbl(f1 point);
INSERT INTO point_gist_tbl SELECT '(0,0)' FROM generate_series(0,1000);
CREATE INDEX point_gist_tbl_index ON point_gist_tbl USING gist (f1);
INSERT INTO point_gist_tbl VALUES ('(0.0000009,0.0000009)');
SET enable_seqscan TO true;
SET enable_indexscan TO false;
SET enable_bitmapscan TO false;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
SET enable_seqscan TO false;
SET enable_indexscan TO true;
SET enable_bitmapscan TO true;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index 7f57526..61f9fca 100644
--- a/src/test/regress/expected/select_views.out
+++ b/src/test/regress/expected/select_views.out
@@ -2,21 +2,20 @@
-- SELECT_VIEWS
-- test the views defined in CREATE_VIEWS
--
SELECT * FROM street;
name | thepath | cname
------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------
Access Rd 25 | [(-121.9283,37.894),(-121.9283,37.9)] | Oakland
Ada St | [(-122.2487,37.398),(-122.2496,37.401)] | Lafayette
Agua Fria Creek | [(-121.9254,37.922),(-121.9281,37.889)] | Oakland
Allen Ct | [(-122.0131,37.602),(-122.0117,37.597)] | Berkeley
- Alvarado Niles Road | [(-122.0325,37.903),(-122.0316,37.9)] | Berkeley
Andrea Cir | [(-121.733218,37.88641),(-121.733286,37.90617)] | Oakland
Apricot Lane | [(-121.9471,37.401),(-121.9456,37.392)] | Oakland
Apricot Lane | [(-121.9471,37.401),(-121.9456,37.392)] | Oakland
Arden Road | [(-122.0978,37.177),(-122.1,37.177)] | Oakland
Arizona St | [(-122.0381,37.901),(-122.0367,37.898)] | Berkeley
Arlington Dr | [(-121.8802,37.408),(-121.8807,37.394)] | Oakland
Arlington Dr | [(-121.8802,37.408),(-121.8807,37.394)] | Oakland
Arlington Road | [(-121.7957,37.898),(-121.7956,37.906)] | Oakland
Arroyo Las Positas | [(-121.7973,37.997),(-121.7957,37.005)] | Oakland
Arroyo Las Positas | [(-121.7973,37.997),(-121.7957,37.005)] | Oakland
@@ -41,21 +40,20 @@ SELECT * FROM street;
Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Oakland
Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Berkeley
C St | [(-122.1768,37.46),(-122.1749,37.435)] | Oakland
Calaveras Creek | [(-121.8203,37.035),(-121.8207,37.931)] | Oakland
Calaveras Creek | [(-121.8203,37.035),(-121.8207,37.931)] | Oakland
California St | [(-122.2032,37.005),(-122.2016,37.996)] | Berkeley
California St | [(-122.2032,37.005),(-122.2016,37.996)] | Lafayette
Cameron Ave | [(-122.1316,37.502),(-122.1327,37.481)] | Oakland
Campus Dr | [(-122.1704,37.905),(-122.1678,37.868),(-122.1671,37.865)] | Berkeley
Capricorn Ave | [(-122.2176,37.404),(-122.2164,37.384)] | Lafayette
- Carson St | [(-122.1846,37.9),(-122.1843,37.901)] | Berkeley
Cedar Blvd | [(-122.0282,37.446),(-122.0265,37.43)] | Oakland
Cedar St | [(-122.3011,37.737),(-122.2999,37.739)] | Berkeley
Celia St | [(-122.0611,37.3),(-122.0616,37.299)] | Oakland
Central Ave | [(-122.2343,37.602),(-122.2331,37.595)] | Berkeley
Chambers Dr | [(-122.2004,37.352),(-122.1972,37.368)] | Lafayette
Chambers Lane | [(-122.2001,37.359),(-122.1975,37.371)] | Lafayette
Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Berkeley
Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Lafayette
Chapman Dr | [(-122.0421,37.504),(-122.0414,37.498)] | Oakland
Charles St | [(-122.0255,37.505),(-122.0252,37.499)] | Oakland
@@ -66,21 +64,20 @@ SELECT * FROM street;
Coliseum Way | [(-122.2001,37.47),(-122.1978,37.516)] | Oakland
Coliseum Way | [(-122.2113,37.626),(-122.2085,37.592),(-122.2063,37.568)] | Berkeley
Coolidge Ave | [(-122.2007,37.058),(-122.1992,37.06)] | Lafayette
Cornell Ave | [(-122.2956,37.925),(-122.2949,37.906),(-122.2939,37.875)] | Berkeley
Corriea Way | [(-121.9501,37.402),(-121.9505,37.398)] | Oakland
Corriea Way | [(-121.9501,37.402),(-121.9505,37.398)] | Oakland
Cowing Road | [(-122.0002,37.934),(-121.9772,37.782)] | Oakland
Creston Road | [(-122.2639,37.002),(-122.2613,37.986),(-122.2602,37.978),(-122.2598,37.973)] | Berkeley
Creston Road | [(-122.2639,37.002),(-122.2613,37.986),(-122.2602,37.978),(-122.2598,37.973)] | Lafayette
Crow Canyon Creek | [(-122.043,37.905),(-122.0368,37.71)] | Berkeley
- Crystaline Dr | [(-121.925856,37),(-121.925869,37.00527)] | Oakland
Cull Canyon Road | [(-122.0536,37.435),(-122.0499,37.315)] | Oakland
Cull Creek | [(-122.0624,37.875),(-122.0582,37.527)] | Berkeley
D St | [(-122.1811,37.505),(-122.1805,37.497)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Berkeley
Deering St | [(-122.2146,37.904),(-122.2126,37.897)] | Berkeley
Dimond Ave | [(-122.2167,37.994),(-122.2162,37.006)] | Berkeley
Dimond Ave | [(-122.2167,37.994),(-122.2162,37.006)] | Lafayette
Donna Way | [(-122.1333,37.606),(-122.1316,37.599)] | Berkeley
@@ -182,21 +179,20 @@ SELECT * FROM street;
I- 880 Ramp | [(-122.0618,37.011),(-122.0631,37.982),(-122.0585,37.967)] | Oakland
I- 880 Ramp | [(-122.0618,37.011),(-122.0631,37.982),(-122.0585,37.967)] | Berkeley
I- 880 Ramp | [(-122.085,37.34),(-122.0801,37.316),(-122.081,37.285)] | Oakland
I- 880 Ramp | [(-122.085,37.34),(-122.0801,37.316),(-122.081,37.285)] | Oakland
I- 880 Ramp | [(-122.085,37.34),(-122.0866,37.316),(-122.0819,37.296)] | Oakland
I- 880 Ramp | [(-122.085,37.34),(-122.0866,37.316),(-122.0819,37.296)] | Oakland
I- 880 Ramp | [(-122.1029,37.61),(-122.1013,37.587),(-122.0999,37.569)] | Berkeley
I- 880 Ramp | [(-122.1379,37.891),(-122.1383,37.897),(-122.1377,37.902)] | Berkeley
I- 880 Ramp | [(-122.1379,37.931),(-122.137597,37.92736),(-122.1374,37.925),(-122.1373,37.924),(-122.1369,37.914),(-122.1358,37.905),(-122.1365,37.908),(-122.1358,37.898)] | Berkeley
I- 880 Ramp | [(-122.2536,37.898),(-122.254,37.902)] | Berkeley
- I- 880 Ramp | [(-122.2771,37.002),(-122.278,37)] | Lafayette
Indian Way | [(-122.2066,37.398),(-122.2045,37.411)] | Lafayette
Jackson St | [(-122.0845,37.6),(-122.0842,37.606)] | Berkeley
Johnson Dr | [(-121.9145,37.901),(-121.915,37.877)] | Oakland
Joyce St | [(-122.0792,37.604),(-122.0774,37.581)] | Berkeley
Juniper St | [(-121.7823,37.897),(-121.7815,37.9)] | Oakland
Kaiser Dr | [(-122.067163,37.47821),(-122.060402,37.51961)] | Oakland
Keeler Ave | [(-122.2578,37.906),(-122.2579,37.899)] | Berkeley
Kildare Road | [(-122.0968,37.016),(-122.0959,37)] | Oakland
La Playa Dr | [(-122.1039,37.545),(-122.101,37.493)] | Oakland
Laguna Ave | [(-122.2099,37.989),(-122.2089,37)] | Berkeley
@@ -234,21 +230,20 @@ SELECT * FROM street;
Mission Blvd | [(-122.0006,37.896),(-121.9989,37.88)] | Berkeley
Moores Ave | [(-122.0087,37.301),(-122.0094,37.292)] | Oakland
National Ave | [(-122.1192,37.5),(-122.1281,37.489)] | Oakland
Navajo Ct | [(-121.8779,37.901),(-121.8783,37.9)] | Oakland
Newark Blvd | [(-122.0352,37.438),(-122.0341,37.423)] | Oakland
Oakland Inner Harbor | [(-122.2625,37.913),(-122.260016,37.89484)] | Berkeley
Oakridge Road | [(-121.8316,37.049),(-121.828382,37)] | Oakland
Oneil Ave | [(-122.076754,37.62476),(-122.0745,37.595)] | Berkeley
Parkridge Dr | [(-122.1438,37.884),(-122.1428,37.9)] | Berkeley
Parkside Dr | [(-122.0475,37.603),(-122.0443,37.596)] | Berkeley
- Paseo Padre Pkwy | [(-121.9143,37.005),(-121.913522,37)] | Oakland
Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Oakland
Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Berkeley
Pearl St | [(-122.2383,37.594),(-122.2366,37.615)] | Berkeley
Periwinkle Road | [(-122.0451,37.301),(-122.044758,37.29844)] | Oakland
Pimlico Dr | [(-121.8616,37.998),(-121.8618,37.008)] | Oakland
Pimlico Dr | [(-121.8616,37.998),(-121.8618,37.008)] | Oakland
Portsmouth Ave | [(-122.1064,37.315),(-122.1064,37.308)] | Oakland
Proctor Ave | [(-122.2267,37.406),(-122.2251,37.386)] | Lafayette
Railroad Ave | [(-122.0245,37.013),(-122.0234,37.003),(-122.0223,37.993)] | Oakland
Railroad Ave | [(-122.0245,37.013),(-122.0234,37.003),(-122.0223,37.993)] | Oakland
@@ -263,39 +258,36 @@ SELECT * FROM street;
Rosedale Ct | [(-121.9232,37.9),(-121.924,37.897)] | Oakland
Sacramento St | [(-122.2799,37.606),(-122.2797,37.597)] | Berkeley
Saddle Brook Dr | [(-122.1478,37.909),(-122.1454,37.904),(-122.1451,37.888)] | Berkeley
Saginaw Ct | [(-121.8803,37.898),(-121.8806,37.901)] | Oakland
San Andreas Dr | [(-122.0609,37.9),(-122.0614,37.895)] | Berkeley
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Berkeley
Shattuck Ave | [(-122.2686,37.904),(-122.2686,37.897)] | Berkeley
Sheridan Road | [(-122.2279,37.425),(-122.2253,37.411),(-122.2223,37.377)] | Lafayette
- Shoreline Dr | [(-122.2657,37.603),(-122.2648,37.6)] | Berkeley
Skyline Blvd | [(-122.1738,37.01),(-122.1714,37.996)] | Oakland
Skyline Blvd | [(-122.1738,37.01),(-122.1714,37.996)] | Berkeley
- Skyline Dr | [(-122.0277,37.5),(-122.0284,37.498)] | Oakland
Skywest Dr | [(-122.1161,37.62),(-122.1123,37.586)] | Berkeley
Southern Pacific Railroad | [(-122.3002,37.674),(-122.2999,37.661)] | Berkeley
Sp Railroad | [(-121.893564,37.99009),(-121.897,37.016)] | Oakland
Sp Railroad | [(-121.893564,37.99009),(-121.897,37.016)] | Oakland
Sp Railroad | [(-121.9565,37.898),(-121.9562,37.9)] | Oakland
Sp Railroad | [(-122.0734,37.001),(-122.0734,37.997)] | Oakland
Sp Railroad | [(-122.0734,37.001),(-122.0734,37.997)] | Oakland
Sp Railroad | [(-122.0734,37.001),(-122.0734,37.997)] | Berkeley
Sp Railroad | [(-122.0914,37.601),(-122.087,37.56),(-122.086408,37.5551)] | Berkeley
Sp Railroad | [(-122.137792,37.003),(-122.1365,37.992),(-122.131257,37.94612)] | Oakland
Sp Railroad | [(-122.137792,37.003),(-122.1365,37.992),(-122.131257,37.94612)] | Berkeley
Sp Railroad | [(-122.1947,37.497),(-122.193328,37.4848)] | Oakland
Stanton Ave | [(-122.100392,37.0697),(-122.099513,37.06052)] | Oakland
State Hwy 123 | [(-122.3004,37.986),(-122.2998,37.969),(-122.2995,37.962),(-122.2992,37.952),(-122.299,37.942),(-122.2987,37.935),(-122.2984,37.924),(-122.2982,37.92),(-122.2976,37.904),(-122.297,37.88),(-122.2966,37.869),(-122.2959,37.848),(-122.2961,37.843)] | Berkeley
- State Hwy 13 | [(-122.1797,37.943),(-122.179871,37.91849),(-122.18,37.9),(-122.179023,37.86615),(-122.1787,37.862),(-122.1781,37.851),(-122.1777,37.845),(-122.1773,37.839),(-122.177,37.833)] | Berkeley
State Hwy 13 | [(-122.2049,37.2),(-122.20328,37.17975),(-122.1989,37.125),(-122.198078,37.11641),(-122.1975,37.11)] | Lafayette
State Hwy 13 Ramp | [(-122.2244,37.427),(-122.223,37.414),(-122.2214,37.396),(-122.2213,37.388)] | Lafayette
State Hwy 238 | ((-122.098,37.908),(-122.0983,37.907),(-122.099,37.905),(-122.101,37.898),(-122.101535,37.89711),(-122.103173,37.89438),(-122.1046,37.892),(-122.106,37.89)) | Berkeley
State Hwy 238 Ramp | [(-122.1288,37.9),(-122.1293,37.895),(-122.1296,37.906)] | Berkeley
State Hwy 24 | [(-122.2674,37.246),(-122.2673,37.248),(-122.267,37.261),(-122.2668,37.271),(-122.2663,37.298),(-122.2659,37.315),(-122.2655,37.336),(-122.265007,37.35882),(-122.264443,37.37286),(-122.2641,37.381),(-122.2638,37.388),(-122.2631,37.396),(-122.2617,37.405),(-122.2615,37.407),(-122.2605,37.412)] | Lafayette
State Hwy 84 | [(-121.9565,37.898),(-121.956589,37.89911),(-121.9569,37.903),(-121.956,37.91),(-121.9553,37.919)] | Oakland
State Hwy 84 | [(-122.0671,37.426),(-122.07,37.402),(-122.074,37.37),(-122.0773,37.338)] | Oakland
State Hwy 92 | [(-122.1085,37.326),(-122.1095,37.322),(-122.1111,37.316),(-122.1119,37.313),(-122.1125,37.311),(-122.1131,37.308),(-122.1167,37.292),(-122.1187,37.285),(-122.12,37.28)] | Oakland
State Hwy 92 Ramp | [(-122.1086,37.321),(-122.1089,37.315),(-122.1111,37.316)] | Oakland
Stuart St | [(-122.2518,37.6),(-122.2507,37.601),(-122.2491,37.606)] | Berkeley
@@ -331,148 +323,104 @@ SELECT * FROM street;
19th Ave | [(-122.2366,37.897),(-122.2359,37.905)] | Berkeley
1st St | [(-121.75508,37.89294),(-121.753581,37.90031)] | Oakland
5th St | [(-122.278,37),(-122.2792,37.005),(-122.2803,37.009)] | Lafayette
5th St | [(-122.296,37.615),(-122.2953,37.598)] | Berkeley
82nd Ave | [(-122.1695,37.596),(-122.1681,37.603)] | Berkeley
85th Ave | [(-122.1877,37.466),(-122.186,37.476)] | Oakland
89th Ave | [(-122.1822,37.459),(-122.1803,37.471)] | Oakland
98th Ave | [(-122.1568,37.498),(-122.1558,37.502)] | Oakland
98th Ave | [(-122.1693,37.438),(-122.1682,37.444)] | Oakland
98th Ave | [(-122.2001,37.258),(-122.1974,37.27)] | Lafayette
-(333 rows)
+(325 rows)
SELECT name, #thepath FROM iexit ORDER BY 1, 2;
name | ?column?
------------------------------------+----------
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 4
I- 580 | 4
- I- 580 | 4
- I- 580 | 4
I- 580 | 5
I- 580 | 5
I- 580 | 5
I- 580 | 5
- I- 580 | 5
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 7
I- 580 | 7
I- 580 | 7
I- 580 | 7
- I- 580 | 7
- I- 580 | 7
- I- 580 | 7
- I- 580 | 8
- I- 580 | 8
- I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 9
I- 580 | 9
- I- 580 | 9
- I- 580 | 9
- I- 580 | 9
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
- I- 580 | 12
- I- 580 | 12
- I- 580 | 12
- I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 18
I- 580 | 18
I- 580 | 18
I- 580 | 18
I- 580 | 18
- I- 580 | 18
I- 580 | 21
I- 580 | 21
I- 580 | 21
I- 580 | 21
I- 580 | 21
I- 580 | 21
- I- 580 | 21
- I- 580 | 21
- I- 580 | 21
- I- 580 | 21
- I- 580 | 22
I- 580 | 22
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
@@ -502,50 +450,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
@@ -587,90 +505,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
@@ -698,336 +546,231 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
- I- 580 Ramp | 5
- I- 580 Ramp | 5
- I- 580 Ramp | 6
- I- 580 Ramp | 6
I- 580 Ramp | 6
I- 580 Ramp | 7
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 4
I- 580/I-680 Ramp | 4
I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
I- 580/I-680 Ramp | 5
I- 580/I-680 Ramp | 6
I- 580/I-680 Ramp | 6
- I- 580/I-680 Ramp | 6
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
I- 680 | 2
I- 680 | 3
- I- 680 | 3
- I- 680 | 3
- I- 680 | 4
- I- 680 | 4
I- 680 | 4
I- 680 | 5
I- 680 | 5
- I- 680 | 5
- I- 680 | 7
I- 680 | 7
I- 680 | 7
I- 680 | 7
I- 680 | 8
I- 680 | 8
I- 680 | 8
- I- 680 | 8
- I- 680 | 10
- I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 16
I- 680 | 16
I- 680 | 16
I- 680 | 16
I- 680 | 16
- I- 680 | 16
- I- 680 | 16
- I- 680 | 16
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 4
I- 680 Ramp | 4
- I- 680 Ramp | 4
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 6
I- 680 Ramp | 6
- I- 680 Ramp | 6
- I- 680 Ramp | 6
- I- 680 Ramp | 7
I- 680 Ramp | 7
I- 680 Ramp | 7
I- 680 Ramp | 7
I- 680 Ramp | 8
I- 680 Ramp | 8
I- 680 Ramp | 8
- I- 680 Ramp | 8
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 3
- I- 80 | 3
I- 80 | 3
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
- I- 80 | 5
- I- 80 | 11
- I- 80 | 11
I- 80 | 11
I- 80 | 11
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 3
- I- 80 Ramp | 3
- I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
- I- 80 Ramp | 5
- I- 80 Ramp | 5
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 880 | 2
I- 880 | 2
- I- 880 | 2
- I- 880 | 2
- I- 880 | 2
- I- 880 | 5
- I- 880 | 5
- I- 880 | 5
I- 880 | 5
I- 880 | 5
I- 880 | 5
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
- I- 880 | 6
- I- 880 | 6
- I- 880 | 6
- I- 880 | 7
- I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 9
I- 880 | 9
I- 880 | 9
- I- 880 | 9
- I- 880 | 9
- I- 880 | 9
- I- 880 | 9
- I- 880 | 10
- I- 880 | 10
- I- 880 | 10
- I- 880 | 10
- I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
- I- 880 | 12
- I- 880 | 13
- I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 14
I- 880 | 14
I- 880 | 14
I- 880 | 14
- I- 880 | 14
- I- 880 | 14
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
- I- 880 | 19
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
@@ -1039,58 +782,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
@@ -1138,31 +843,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
@@ -1172,81 +866,56 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 8
I- 880 Ramp | 8
- I- 880 Ramp | 8
I- 980 | 2
I- 980 | 2
I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 3
- I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 4
I- 980 | 4
I- 980 | 5
I- 980 | 5
I- 980 | 7
I- 980 | 7
- I- 980 | 7
- I- 980 | 7
I- 980 | 12
I- 980 Ramp | 3
- I- 980 Ramp | 3
- I- 980 Ramp | 3
- I- 980 Ramp | 7
-(896 rows)
+(573 rows)
SELECT * FROM toyemp WHERE name = 'sharon';
name | age | location | annualsal
--------+-----+----------+-----------
sharon | 25 | (15,12) | 12000
(1 row)
--
-- Test for Leaky view scenario
--
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index e7826a4..3cd8427 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -66,21 +66,23 @@ regress_dist_ptpath(PG_FUNCTION_ARGS)
float8 result = 0.0; /* keep compiler quiet */
float8 tmp;
int i;
LSEG lseg;
switch (path->npts)
{
case 0:
PG_RETURN_NULL();
case 1:
- result = point_dt(pt, &path->p[0]);
+ result = DatumGetFloat8(DirectFunctionCall2(point_distance,
+ PointPGetDatum(pt),
+ PointPGetDatum(&path->p[0])));
break;
default:
/*
* the distance from a point to a path is the smallest distance
* from the point to any of its constituent segments.
*/
Assert(path->npts > 1);
for (i = 0; i < path->npts - 1; ++i)
{
@@ -280,22 +282,27 @@ widget_out(WIDGET *widget)
widget->center.x, widget->center.y, widget->radius);
}
PG_FUNCTION_INFO_V1(pt_in_widget);
Datum
pt_in_widget(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(1);
+ float8 distance;
- PG_RETURN_BOOL(point_dt(point, &widget->center) < widget->radius);
+ distance = DatumGetFloat8(DirectFunctionCall2(point_distance,
+ PointPGetDatum(point),
+ PointPGetDatum(&widget->center)));
+
+ PG_RETURN_BOOL(distance < widget->radius);
}
PG_FUNCTION_INFO_V1(boxarea);
Datum
boxarea(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
double width,
height;
--
2.7.4 (Apple Git-66)
On Mon, Jul 18, 2016 at 3:54 PM, Emre Hasegeli <emre@hasegeli.com> wrote:
My progress so far is attached as 2 patches. First one introduces an
header file for adt/float.c. Second one refactors the geometric
operations.
The first patch fails to apply due to bit-rot. That's easy enough
to correct, but then it runs into warnings on make:
btree_gin.c: In function ‘leftmostvalue_float4’:
btree_gin.c:234:2: error: implicit declaration of function
‘get_float4_infinity’ [-Werror=implicit-function-declaration]
return Float4GetDatum(-get_float4_infinity());
^
btree_gin.c: In function ‘leftmostvalue_float8’:
btree_gin.c:242:2: error: implicit declaration of function
‘get_float8_infinity’ [-Werror=implicit-function-declaration]
return Float8GetDatum(-get_float8_infinity());
^
Please fix.
Something to consider before posting new version -- should we
change some of those macros to static inline (in the .h files) to
avoid double-evaluation hazards? They might perform as well or
even better that way, and remove a subtle programmer foot-gun.
Changing status to "Waiting on Author".
--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
The first patch fails to apply due to bit-rot. That's easy enough
to correct, but then it runs into warnings on make:
Rebased and fixed.
Something to consider before posting new version -- should we
change some of those macros to static inline (in the .h files) to
avoid double-evaluation hazards? They might perform as well or
even better that way, and remove a subtle programmer foot-gun.
One reason to keep them as macros is that they are currently
supporting both float4 and float8. Currently they look like this:
FLOAT_EQ(val1, val2)
FLOAT_PL(result, val1, val2)
I guess if we would use inline functions, they would look like:
FLOAT8_EQ(val1, val2)
result = FLOAT8_PL(val1, val2)
Result would need to be defined again in the function to be checked
for overflow. I think this way would be more developer-friendly. I
have gone some trouble to allocate the result to be able to use the
macros. Do you know if the performance of both would be the same?
I am not much experienced in C. If you think that inline functions
are better suited, I can rework the patches.
Attachments:
0001-float-header-v02.patchapplication/octet-stream; name=0001-float-header-v02.patchDownload
From 352234925472956e4fdc7f63732dbc0b6b151da6 Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Sat, 28 May 2016 18:16:05 +0200
Subject: [PATCH 1/2] float-header-v02
---
contrib/btree_gin/btree_gin.c | 1 +
contrib/btree_gist/btree_ts.c | 2 +-
contrib/cube/cube.c | 2 +-
contrib/postgres_fdw/postgres_fdw.c | 1 +
src/backend/access/gist/gistget.c | 2 +-
src/backend/access/gist/gistproc.c | 56 ++---
src/backend/access/gist/gistutil.c | 2 +-
src/backend/utils/adt/float.c | 315 +++++++-------------------
src/backend/utils/adt/formatting.c | 8 +-
src/backend/utils/adt/geo_ops.c | 6 +-
src/backend/utils/adt/geo_spgist.c | 2 +-
src/backend/utils/adt/numeric.c | 1 +
src/backend/utils/adt/rangetypes_gist.c | 2 +-
src/backend/utils/adt/rangetypes_selfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_typanalyze.c | 2 +-
src/backend/utils/adt/timestamp.c | 1 +
src/backend/utils/misc/guc.c | 1 +
src/include/utils/builtins.h | 13 --
src/include/utils/float.h | 130 +++++++++++
19 files changed, 250 insertions(+), 299 deletions(-)
create mode 100644 src/include/utils/float.h
diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index 030b610..560ce85 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -3,20 +3,21 @@
*/
#include "postgres.h"
#include <limits.h>
#include "access/stratnum.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/cash.h"
#include "utils/date.h"
+#include "utils/float.h"
#include "utils/inet.h"
#include "utils/numeric.h"
#include "utils/timestamp.h"
#include "utils/varbit.h"
PG_MODULE_MAGIC;
typedef struct QueryInfo
{
StrategyNumber strategy;
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index ab22b27..5fb0a9f 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -1,19 +1,19 @@
/*
* contrib/btree_gist/btree_ts.c
*/
#include "postgres.h"
#include "btree_gist.h"
#include "btree_utils_num.h"
-#include "utils/builtins.h"
#include "utils/datetime.h"
+#include "utils/float.h"
typedef struct
{
Timestamp lower;
Timestamp upper;
} tsKEY;
/*
** timestamp ops
*/
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index 3feddef..01335d9 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -7,21 +7,21 @@
******************************************************************************/
#include "postgres.h"
#include <float.h>
#include <math.h>
#include "access/gist.h"
#include "access/stratnum.h"
#include "utils/array.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "cubedata.h"
PG_MODULE_MAGIC;
/*
* Taken from the intarray contrib header
*/
#define ARRPTR(x) ( (double *) ARR_DATA_PTR(x) )
#define ARRNELEMS(x) ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x))
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index daf0438..0bd98a1 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -26,20 +26,21 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
PG_MODULE_MAGIC;
/* Default CPU cost to start up a foreign query. */
#define DEFAULT_FDW_STARTUP_COST 100.0
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 5ba7d0a..eda48ef 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -13,21 +13,21 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist_private.h"
#include "access/relscan.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "lib/pairingheap.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/memutils.h"
#include "utils/rel.h"
/*
* gistkillitems() -- set LP_DEAD state for items an indexscan caller has
* told us were killed.
*
* We re-read page here, so it's important to check page LSN. If the page
* has been modified since the last read (as determined by LSN), we cannot
* flag any entries because it is possible that the old entry was vacuumed
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index d47211a..c7ac033 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -15,73 +15,65 @@
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <math.h>
#include "access/gist.h"
#include "access/stratnum.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
static bool gist_box_leaf_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
static bool rtree_internal_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
/* Minimum accepted ratio of split */
#define LIMIT_RATIO 0.3
-/* Convenience macros for NaN-aware comparisons */
-#define FLOAT8_EQ(a,b) (float8_cmp_internal(a, b) == 0)
-#define FLOAT8_LT(a,b) (float8_cmp_internal(a, b) < 0)
-#define FLOAT8_LE(a,b) (float8_cmp_internal(a, b) <= 0)
-#define FLOAT8_GT(a,b) (float8_cmp_internal(a, b) > 0)
-#define FLOAT8_GE(a,b) (float8_cmp_internal(a, b) >= 0)
-#define FLOAT8_MAX(a,b) (FLOAT8_GT(a, b) ? (a) : (b))
-#define FLOAT8_MIN(a,b) (FLOAT8_LT(a, b) ? (a) : (b))
-
/**************************************************
* Box ops
**************************************************/
/*
* Calculates union of two boxes, a and b. The result is stored in *n.
*/
static void
rt_box_union(BOX *n, const BOX *a, const BOX *b)
{
- n->high.x = FLOAT8_MAX(a->high.x, b->high.x);
- n->high.y = FLOAT8_MAX(a->high.y, b->high.y);
- n->low.x = FLOAT8_MIN(a->low.x, b->low.x);
- n->low.y = FLOAT8_MIN(a->low.y, b->low.y);
+ n->high.x = FLOAT_MAX(a->high.x, b->high.x);
+ n->high.y = FLOAT_MAX(a->high.y, b->high.y);
+ n->low.x = FLOAT_MIN(a->low.x, b->low.x);
+ n->low.y = FLOAT_MIN(a->low.y, b->low.y);
}
/*
* Size of a BOX for penalty-calculation purposes.
* The result can be +Infinity, but not NaN.
*/
static double
size_box(const BOX *box)
{
/*
* Check for zero-width cases. Note that we define the size of a zero-
* by-infinity box as zero. It's important to special-case this somehow,
* as naively multiplying infinity by zero will produce NaN.
*
* The less-than cases should not happen, but if they do, say "zero".
*/
- if (FLOAT8_LE(box->high.x, box->low.x) ||
- FLOAT8_LE(box->high.y, box->low.y))
+ if (FLOAT_LE(box->high.x, box->low.x) ||
+ FLOAT_LE(box->high.y, box->low.y))
return 0.0;
/*
* We treat NaN as larger than +Infinity, so any distance involving a NaN
* and a non-NaN is infinite. Note the previous check eliminated the
* possibility that the low fields are NaNs.
*/
if (isnan(box->high.x) || isnan(box->high.y))
return get_float8_infinity();
return (box->high.x - box->low.x) * (box->high.y - box->low.y);
@@ -136,27 +128,27 @@ gist_box_consistent(PG_FUNCTION_ARGS)
query,
strategy));
}
/*
* Increase BOX b to include addon.
*/
static void
adjustBox(BOX *b, const BOX *addon)
{
- if (FLOAT8_LT(b->high.x, addon->high.x))
+ if (FLOAT_LT(b->high.x, addon->high.x))
b->high.x = addon->high.x;
- if (FLOAT8_GT(b->low.x, addon->low.x))
+ if (FLOAT_GT(b->low.x, addon->low.x))
b->low.x = addon->low.x;
- if (FLOAT8_LT(b->high.y, addon->high.y))
+ if (FLOAT_LT(b->high.y, addon->high.y))
b->high.y = addon->high.y;
- if (FLOAT8_GT(b->low.y, addon->low.y))
+ if (FLOAT_GT(b->low.y, addon->low.y))
b->low.y = addon->low.y;
}
/*
* The GiST Union method for boxes
*
* returns the minimal bounding box that encloses all the entries in entryvec
*/
Datum
gist_box_union(PG_FUNCTION_ARGS)
@@ -636,36 +628,36 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
i1 = 0;
i2 = 0;
rightLower = intervalsLower[i1].lower;
leftUpper = intervalsUpper[i2].lower;
while (true)
{
/*
* Find next lower bound of right group.
*/
while (i1 < nentries &&
- FLOAT8_EQ(rightLower, intervalsLower[i1].lower))
+ FLOAT_EQ(rightLower, intervalsLower[i1].lower))
{
- if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper))
+ if (FLOAT_LT(leftUpper, intervalsLower[i1].upper))
leftUpper = intervalsLower[i1].upper;
i1++;
}
if (i1 >= nentries)
break;
rightLower = intervalsLower[i1].lower;
/*
* Find count of intervals which anyway should be placed to the
* left group.
*/
while (i2 < nentries &&
- FLOAT8_LE(intervalsUpper[i2].upper, leftUpper))
+ FLOAT_LE(intervalsUpper[i2].upper, leftUpper))
i2++;
/*
* Consider found split.
*/
g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
}
/*
* Iterate over upper bound of left group finding greatest possible
@@ -673,35 +665,35 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
*/
i1 = nentries - 1;
i2 = nentries - 1;
rightLower = intervalsLower[i1].upper;
leftUpper = intervalsUpper[i2].upper;
while (true)
{
/*
* Find next upper bound of left group.
*/
- while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper))
+ while (i2 >= 0 && FLOAT_EQ(leftUpper, intervalsUpper[i2].upper))
{
- if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower))
+ if (FLOAT_GT(rightLower, intervalsUpper[i2].lower))
rightLower = intervalsUpper[i2].lower;
i2--;
}
if (i2 < 0)
break;
leftUpper = intervalsUpper[i2].upper;
/*
* Find count of intervals which anyway should be placed to the
* right group.
*/
- while (i1 >= 0 && FLOAT8_GE(intervalsLower[i1].lower, rightLower))
+ while (i1 >= 0 && FLOAT_GE(intervalsLower[i1].lower, rightLower))
i1--;
/*
* Consider found split.
*/
g_box_consider_split(&context, dim,
rightLower, i1 + 1, leftUpper, i2 + 1);
}
}
@@ -775,42 +767,42 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
{
lower = box->low.x;
upper = box->high.x;
}
else
{
lower = box->low.y;
upper = box->high.y;
}
- if (FLOAT8_LE(upper, context.leftUpper))
+ if (FLOAT_LE(upper, context.leftUpper))
{
/* Fits to the left group */
- if (FLOAT8_GE(lower, context.rightLower))
+ if (FLOAT_GE(lower, context.rightLower))
{
/* Fits also to the right group, so "common entry" */
commonEntries[commonEntriesCount++].index = i;
}
else
{
/* Doesn't fit to the right group, so join to the left group */
PLACE_LEFT(box, i);
}
}
else
{
/*
* Each entry should fit on either left or right group. Since this
* entry didn't fit on the left group, it better fit in the right
* group.
*/
- Assert(FLOAT8_GE(lower, context.rightLower));
+ Assert(FLOAT_GE(lower, context.rightLower));
/* Doesn't fit to the left group, so join to the right group */
PLACE_RIGHT(box, i);
}
}
/*
* Distribute "common entries", if any.
*/
if (commonEntriesCount > 0)
@@ -880,24 +872,24 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
* equivalent to box_same().
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
- *result = (FLOAT8_EQ(b1->low.x, b2->low.x) &&
- FLOAT8_EQ(b1->low.y, b2->low.y) &&
- FLOAT8_EQ(b1->high.x, b2->high.x) &&
- FLOAT8_EQ(b1->high.y, b2->high.y));
+ *result = (FLOAT_EQ(b1->low.x, b2->low.x) &&
+ FLOAT_EQ(b1->low.y, b2->low.y) &&
+ FLOAT_EQ(b1->high.x, b2->high.x) &&
+ FLOAT_EQ(b1->high.y, b2->high.y));
else
*result = (b1 == NULL && b2 == NULL);
PG_RETURN_POINTER(result);
}
/*
* Leaf-level consistency for boxes: just apply the query operator
*/
static bool
gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 887c58b..db9bea5 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -14,21 +14,21 @@
#include "postgres.h"
#include <math.h>
#include "access/gist_private.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/syscache.h"
/*
* Write itup vector to page, has no control of free space.
*/
void
gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
{
OffsetNumber l = InvalidOffsetNumber;
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 8aa17e1..d4bb818 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -16,61 +16,24 @@
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <limits.h>
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/sortsupport.h"
-#ifndef M_PI
-/* from my RH5.2 gcc math.h file - thomas 2000-04-03 */
-#define M_PI 3.14159265358979323846
-#endif
-
-/* Radians per degree, a.k.a. PI / 180 */
-#define RADIANS_PER_DEGREE 0.0174532925199432957692
-
-/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
- * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
- */
-#if defined(WIN32) && !defined(NAN)
-static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
-
-#define NAN (*(const double *) nan)
-#endif
-
-/* not sure what the following should be, but better to make it over-sufficient */
-#define MAXFLOATWIDTH 64
-#define MAXDOUBLEWIDTH 128
-
-/*
- * check to see if a float4/8 val has underflowed or overflowed
- */
-#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \
-do { \
- if (isinf(val) && !(inf_is_valid)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
- errmsg("value out of range: overflow"))); \
- \
- if ((val) == 0.0 && !(zero_is_valid)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
- errmsg("value out of range: underflow"))); \
-} while(0)
-
-
/* Configurable GUC parameter */
int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */
/* Cached constants for degree-based trig functions */
static bool degree_consts_set = false;
static float8 sin_30 = 0;
static float8 one_minus_cos_60 = 0;
static float8 asin_0_5 = 0;
static float8 acos_0_5 = 0;
static float8 atan_1_0 = 0;
@@ -689,35 +652,35 @@ float4up(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT4(arg);
}
Datum
float4larger(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (float4_cmp_internal(arg1, arg2) > 0)
+ if (FLOAT_GT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT4(result);
}
Datum
float4smaller(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (float4_cmp_internal(arg1, arg2) < 0)
+ if (FLOAT_LT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT4(result);
}
/*
* ======================
* FLOAT8 BASE OPERATIONS
* ======================
@@ -756,35 +719,35 @@ float8up(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(arg);
}
Datum
float8larger(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (float8_cmp_internal(arg1, arg2) > 0)
+ if (FLOAT_GT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT8(result);
}
Datum
float8smaller(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (float8_cmp_internal(arg1, arg2) < 0)
+ if (FLOAT_LT(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* ARITHMETIC OPERATORS
@@ -797,232 +760,187 @@ float8smaller(PG_FUNCTION_ARGS)
* float4mul - returns arg1 * arg2
* float4div - returns arg1 / arg2
*/
Datum
float4pl(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- result = arg1 + arg2;
+ FLOAT_PL(result, arg1, arg2);
- /*
- * There isn't any way to check for underflow of addition/subtraction
- * because numbers near the underflow value have already been rounded to
- * the point where we can't detect that the two values were originally
- * different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
- * 1.4013e-45.
- */
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT4(result);
}
Datum
float4mi(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- result = arg1 - arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+ FLOAT_MI(result, arg1, arg2);
+
PG_RETURN_FLOAT4(result);
}
Datum
float4mul(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- result = arg1 * arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ FLOAT_MUL(result, arg1, arg2);
+
PG_RETURN_FLOAT4(result);
}
Datum
float4div(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT4(result);
}
/*
* float8pl - returns arg1 + arg2
* float8mi - returns arg1 - arg2
* float8mul - returns arg1 * arg2
* float8div - returns arg1 / arg2
*/
Datum
float8pl(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 + arg2;
+ FLOAT_PL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float8mi(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 - arg2;
+ FLOAT_MI(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float8mul(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 * arg2;
+ FLOAT_MUL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
Datum
float8div(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* COMPARISON OPERATORS
* ====================
*/
/*
* float4{eq,ne,lt,le,gt,ge} - float4/float4 comparison operations
*/
int
float4_cmp_internal(float4 a, float4 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
- if (isnan(a))
- {
- if (isnan(b))
- return 0; /* NAN = NAN */
- else
- return 1; /* NAN > non-NAN */
- }
- else if (isnan(b))
- {
- return -1; /* non-NAN < NAN */
- }
- else
- {
- if (a > b)
- return 1;
- else if (a < b)
- return -1;
- else
- return 0;
- }
+ if (FLOAT_GT(a, b))
+ return 1;
+ if (FLOAT_LT(a, b))
+ return -1;
+ return 0;
}
Datum
float4eq(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float4ne(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float4lt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float4le(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float4gt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float4ge(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
Datum
btfloat4cmp(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
}
@@ -1044,99 +962,79 @@ btfloat4sortsupport(PG_FUNCTION_ARGS)
ssup->comparator = btfloat4fastcmp;
PG_RETURN_VOID();
}
/*
* float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations
*/
int
float8_cmp_internal(float8 a, float8 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
- if (isnan(a))
- {
- if (isnan(b))
- return 0; /* NAN = NAN */
- else
- return 1; /* NAN > non-NAN */
- }
- else if (isnan(b))
- {
- return -1; /* non-NAN < NAN */
- }
- else
- {
- if (a > b)
- return 1;
- else if (a < b)
- return -1;
- else
- return 0;
- }
+ if (FLOAT_GT(a, b))
+ return 1;
+ if (FLOAT_LT(a, b))
+ return -1;
+ return 0;
}
Datum
float8eq(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float8ne(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float8lt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float8le(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float8gt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float8ge(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
Datum
btfloat8cmp(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
}
@@ -2369,23 +2267,22 @@ dtand(PG_FUNCTION_ARGS)
/*
* degrees - returns degrees converted from radians
*/
Datum
degrees(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
- result = arg1 / RADIANS_PER_DEGREE;
+ FLOAT_DIV(result, arg1, RADIANS_PER_DEGREE);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dpi - returns the constant PI
*/
Datum
dpi(PG_FUNCTION_ARGS)
{
@@ -2395,23 +2292,22 @@ dpi(PG_FUNCTION_ARGS)
/*
* radians - returns radians converted from degrees
*/
Datum
radians(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
- result = arg1 * RADIANS_PER_DEGREE;
+ FLOAT_MUL(result, arg1, RADIANS_PER_DEGREE);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* drandom - returns a random number
*/
Datum
drandom(PG_FUNCTION_ARGS)
{
@@ -2498,61 +2394,49 @@ float8_combine(PG_FUNCTION_ARGS)
float8 *transvalues1;
float8 *transvalues2;
float8 N,
sumX,
sumX2;
if (!AggCheckCallContext(fcinfo, NULL))
elog(ERROR, "aggregate function called in non-aggregate context");
transvalues1 = check_float8_array(transarray1, "float8_combine", 3);
- N = transvalues1[0];
- sumX = transvalues1[1];
- sumX2 = transvalues1[2];
-
transvalues2 = check_float8_array(transarray2, "float8_combine", 3);
- N += transvalues2[0];
- sumX += transvalues2[1];
- CHECKFLOATVAL(sumX, isinf(transvalues1[1]) || isinf(transvalues2[1]),
- true);
- sumX2 += transvalues2[2];
- CHECKFLOATVAL(sumX2, isinf(transvalues1[2]) || isinf(transvalues2[2]),
- true);
+ N = transvalues1[0] + transvalues2[0];
+ FLOAT_PL(sumX, transvalues1[1], transvalues2[1]);
+ FLOAT_PL(sumX2, transvalues1[2], transvalues2[2]);
transvalues1[0] = N;
transvalues1[1] = sumX;
transvalues1[2] = sumX2;
PG_RETURN_ARRAYTYPE_P(transarray1);
}
Datum
float8_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
float8 newval = PG_GETARG_FLOAT8(1);
float8 *transvalues;
float8 N,
+ newval2,
sumX,
sumX2;
transvalues = check_float8_array(transarray, "float8_accum", 3);
- N = transvalues[0];
- sumX = transvalues[1];
- sumX2 = transvalues[2];
-
- N += 1.0;
- sumX += newval;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true);
- sumX2 += newval * newval;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true);
+ N = transvalues[0] + 1.0;
+ FLOAT_PL(sumX, transvalues[1], newval);
+ FLOAT_MUL(newval2, newval, newval);
+ FLOAT_PL(sumX2, transvalues[2], newval2);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = sumX;
@@ -2579,33 +2463,29 @@ float8_accum(PG_FUNCTION_ARGS)
Datum
float4_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
/* do computations as float8 */
float8 newval = PG_GETARG_FLOAT4(1);
float8 *transvalues;
float8 N,
+ newval2,
sumX,
sumX2;
transvalues = check_float8_array(transarray, "float4_accum", 3);
- N = transvalues[0];
- sumX = transvalues[1];
- sumX2 = transvalues[2];
-
- N += 1.0;
- sumX += newval;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true);
- sumX2 += newval * newval;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true);
+ N = transvalues[0] + 1.0;
+ FLOAT_PL(sumX, transvalues[1], newval);
+ FLOAT_MUL(newval2, newval, newval);
+ FLOAT_PL(sumX2, transvalues[2], newval2);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = sumX;
@@ -2872,45 +2752,28 @@ float8_regr_combine(PG_FUNCTION_ARGS)
sumX,
sumX2,
sumY,
sumY2,
sumXY;
if (!AggCheckCallContext(fcinfo, NULL))
elog(ERROR, "aggregate function called in non-aggregate context");
transvalues1 = check_float8_array(transarray1, "float8_regr_combine", 6);
- N = transvalues1[0];
- sumX = transvalues1[1];
- sumX2 = transvalues1[2];
- sumY = transvalues1[3];
- sumY2 = transvalues1[4];
- sumXY = transvalues1[5];
-
transvalues2 = check_float8_array(transarray2, "float8_regr_combine", 6);
- N += transvalues2[0];
- sumX += transvalues2[1];
- CHECKFLOATVAL(sumX, isinf(transvalues1[1]) || isinf(transvalues2[1]),
- true);
- sumX2 += transvalues2[2];
- CHECKFLOATVAL(sumX2, isinf(transvalues1[2]) || isinf(transvalues2[2]),
- true);
- sumY += transvalues2[3];
- CHECKFLOATVAL(sumY, isinf(transvalues1[3]) || isinf(transvalues2[3]),
- true);
- sumY2 += transvalues2[4];
- CHECKFLOATVAL(sumY2, isinf(transvalues1[4]) || isinf(transvalues2[4]),
- true);
- sumXY += transvalues2[5];
- CHECKFLOATVAL(sumXY, isinf(transvalues1[5]) || isinf(transvalues2[5]),
- true);
+ N = transvalues1[0] + transvalues2[0];
+ FLOAT_PL(sumX, transvalues1[1], transvalues2[1]);
+ FLOAT_PL(sumX2, transvalues1[2], transvalues2[2]);
+ FLOAT_PL(sumY, transvalues1[3], transvalues2[3]);
+ FLOAT_PL(sumY2, transvalues1[4], transvalues2[4]);
+ FLOAT_PL(sumXY, transvalues1[5], transvalues2[5]);
transvalues1[0] = N;
transvalues1[1] = sumX;
transvalues1[2] = sumX2;
transvalues1[3] = sumY;
transvalues1[4] = sumY2;
transvalues1[5] = sumXY;
PG_RETURN_ARRAYTYPE_P(transarray1);
}
@@ -3265,249 +3128,233 @@ float8_regr_intercept(PG_FUNCTION_ARGS)
* float48mul - returns arg1 * arg2
* float48div - returns arg1 / arg2
*/
Datum
float48pl(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 + arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+ FLOAT_PL(result, arg1, arg2);
+
PG_RETURN_FLOAT8(result);
}
Datum
float48mi(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 - arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+ FLOAT_MI(result, arg1, arg2);
+
PG_RETURN_FLOAT8(result);
}
Datum
float48mul(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- result = arg1 * arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ FLOAT_MUL(result, arg1, arg2);
+
PG_RETURN_FLOAT8(result);
}
Datum
float48div(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* float84pl - returns arg1 + arg2
* float84mi - returns arg1 - arg2
* float84mul - returns arg1 * arg2
* float84div - returns arg1 / arg2
*/
Datum
float84pl(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- result = arg1 + arg2;
+ FLOAT_PL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float84mi(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- result = arg1 - arg2;
+ FLOAT_MI(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
PG_RETURN_FLOAT8(result);
}
Datum
float84mul(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- result = arg1 * arg2;
+ FLOAT_MUL(result, arg1, arg2);
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
Datum
float84div(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_DIV(result, arg1, arg2);
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* COMPARISON OPERATORS
* ====================
*/
/*
* float48{eq,ne,lt,le,gt,ge} - float4/float8 comparison operations
*/
Datum
float48eq(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float48ne(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float48lt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float48le(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float48gt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float48ge(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
/*
* float84{eq,ne,lt,le,gt,ge} - float8/float4 comparison operations
*/
Datum
float84eq(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(FLOAT_EQ(arg1, arg2));
}
Datum
float84ne(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(FLOAT_NE(arg1, arg2));
}
Datum
float84lt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(FLOAT_LT(arg1, arg2));
}
Datum
float84le(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(FLOAT_LE(arg1, arg2));
}
Datum
float84gt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(FLOAT_GT(arg1, arg2));
}
Datum
float84ge(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(FLOAT_GE(arg1, arg2));
}
/*
* Implements the float8 version of the width_bucket() function
* defined by SQL2003. See also width_bucket_numeric().
*
* 'bound1' and 'bound2' are the lower and upper bounds of the
* histogram's range, respectively. 'count' is the number of buckets
* in the histogram. width_bucket() returns an integer indicating the
* bucket number that 'operand' belongs to in an equiwidth histogram
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index bbd97dc..b67477f 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -80,20 +80,21 @@
#endif
#ifdef HAVE_WCTYPE_H
#include <wctype.h>
#endif
#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
+#include "utils/float.h"
#include "utils/formatting.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"
/* ----------
* Routines type
* ----------
*/
#define DCH_TYPE 1 /* DATE-TIME version */
@@ -106,27 +107,20 @@
#define KeyWord_INDEX_SIZE ('~' - ' ')
#define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
/* ----------
* Maximal length of one node
* ----------
*/
#define DCH_MAX_ITEM_SIZ 12 /* max localized day name */
#define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
-/* ----------
- * More is in float.c
- * ----------
- */
-#define MAXFLOATWIDTH 60
-#define MAXDOUBLEWIDTH 500
-
/* ----------
* Format parser structs
* ----------
*/
typedef struct
{
char *name; /* suffix string */
int len, /* suffix length */
id, /* used in node->suffix */
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 0dff40d..efc81ab 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -14,27 +14,23 @@
*/
#include "postgres.h"
#include <math.h>
#include <limits.h>
#include <float.h>
#include <ctype.h>
#include "libpq/pqformat.h"
#include "miscadmin.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
/*
* Internal routines
*/
enum path_delim
{
PATH_NONE, PATH_OPEN, PATH_CLOSED
};
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 0190156..565a034 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -69,21 +69,21 @@
* src/backend/utils/adt/geo_spgist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
/*
* Comparator for qsort
*
* We don't need to use the floating point macros in here, because this
* is going only going to be used in a place to effect the performance
* of the index, not the correctness.
*/
static int
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 384e672..ece1d4b 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -28,20 +28,21 @@
#include "access/hash.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "lib/hyperloglog.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/guc.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/sortsupport.h"
/* ----------
* Uncomment the following to enable compilation of dump_numeric()
* and dump_var() and to get a dump of any result produced by make_result().
* ----------
#define NUMERIC_DEBUG
diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c
index 01cd234..71dc12e 100644
--- a/src/backend/utils/adt/rangetypes_gist.c
+++ b/src/backend/utils/adt/rangetypes_gist.c
@@ -9,21 +9,21 @@
*
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_gist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist.h"
#include "access/stratnum.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/datum.h"
#include "utils/rangetypes.h"
/*
* Range class properties used to segregate different classes of ranges in
* GiST. Each unique combination of properties is a class. CLS_EMPTY cannot
* be combined with anything else.
*/
#define CLS_NORMAL 0 /* Ordinary finite range (no bits set) */
diff --git a/src/backend/utils/adt/rangetypes_selfuncs.c b/src/backend/utils/adt/rangetypes_selfuncs.c
index 8595d41..2e79ec7 100644
--- a/src/backend/utils/adt/rangetypes_selfuncs.c
+++ b/src/backend/utils/adt/rangetypes_selfuncs.c
@@ -13,21 +13,21 @@
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_selfuncs.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/selfuncs.h"
#include "utils/typcache.h"
static double calc_rangesel(TypeCacheEntry *typcache, VariableStatData *vardata,
RangeType *constval, Oid operator);
static double default_range_selectivity(Oid operator);
static double calc_hist_selectivity(TypeCacheEntry *typcache,
VariableStatData *vardata, RangeType *constval,
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index 56504fc..69f6623 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -19,21 +19,21 @@
*
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_typanalyze.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "commands/vacuum.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
static int float8_qsort_cmp(const void *a1, const void *a2);
static int range_bound_qsort_cmp(const void *a1, const void *a2, void *arg);
static void compute_range_stats(VacAttrStats *stats,
AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows);
/*
* range_typanalyze -- typanalyze function for range columns
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index c1d6f05..792c32a 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -26,20 +26,21 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/scansup.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
+#include "utils/float.h"
/*
* gcc's -ffast-math switch breaks routines that expect exact results from
* expressions like timeval / SECS_PER_HOUR, where timeval is double.
*/
#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c5178f7..1b36555 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -67,20 +67,21 @@
#include "storage/standby.h"
#include "storage/fd.h"
#include "storage/pg_shmem.h"
#include "storage/proc.h"
#include "storage/predicate.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/guc_tables.h"
+#include "utils/float.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/plancache.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/tzparser.h"
#include "utils/xml.h"
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2ae212a..22ba041 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -334,33 +334,20 @@ extern Datum bttextsortsupport(PG_FUNCTION_ARGS);
*/
extern Datum btint2sortsupport(PG_FUNCTION_ARGS);
extern Datum btint4sortsupport(PG_FUNCTION_ARGS);
extern Datum btint8sortsupport(PG_FUNCTION_ARGS);
extern Datum btfloat4sortsupport(PG_FUNCTION_ARGS);
extern Datum btfloat8sortsupport(PG_FUNCTION_ARGS);
extern Datum btoidsortsupport(PG_FUNCTION_ARGS);
extern Datum btnamesortsupport(PG_FUNCTION_ARGS);
/* float.c */
-extern PGDLLIMPORT int extra_float_digits;
-
-extern double get_float8_infinity(void);
-extern float get_float4_infinity(void);
-extern double get_float8_nan(void);
-extern float get_float4_nan(void);
-extern int is_infinite(double val);
-extern double float8in_internal(char *num, char **endptr_p,
- const char *type_name, const char *orig_string);
-extern char *float8out_internal(double num);
-extern int float4_cmp_internal(float4 a, float4 b);
-extern int float8_cmp_internal(float8 a, float8 b);
-
extern Datum float4in(PG_FUNCTION_ARGS);
extern Datum float4out(PG_FUNCTION_ARGS);
extern Datum float4recv(PG_FUNCTION_ARGS);
extern Datum float4send(PG_FUNCTION_ARGS);
extern Datum float8in(PG_FUNCTION_ARGS);
extern Datum float8out(PG_FUNCTION_ARGS);
extern Datum float8recv(PG_FUNCTION_ARGS);
extern Datum float8send(PG_FUNCTION_ARGS);
extern Datum float4abs(PG_FUNCTION_ARGS);
extern Datum float4um(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
new file mode 100644
index 0000000..4eacd56
--- /dev/null
+++ b/src/include/utils/float.h
@@ -0,0 +1,130 @@
+/*-------------------------------------------------------------------------
+ *
+ * float.h
+ * Definitions for the built-in floating-point types
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/include/utils/float.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FLOAT_H
+#define FLOAT_H
+
+#ifndef M_PI
+/* From my RH5.2 gcc math.h file - thomas 2000-04-03 */
+#define M_PI 3.14159265358979323846
+#endif
+
+/* Radians per degree, a.k.a. PI / 180 */
+#define RADIANS_PER_DEGREE 0.0174532925199432957692
+
+/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
+ */
+#if defined(WIN32) && !defined(NAN)
+static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
+
+#define NAN (*(const double *) nan)
+#endif
+
+/*
+ * We are not sure what the following should be, but better to make it
+ * over-sufficient.
+ */
+#define MAXFLOATWIDTH 64
+#define MAXDOUBLEWIDTH 128
+
+/*
+ * Check to see if a float4/8 val has underflowed or overflowed
+ */
+#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \
+do { \
+ if (isinf(val) && !(inf_is_valid)) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
+ errmsg("value out of range: overflow"))); \
+ \
+ if ((val) == 0.0 && !(zero_is_valid)) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
+ errmsg("value out of range: underflow"))); \
+} while(0)
+
+/*
+ * Convenience macros for operations with checking
+ *
+ * There isn't any way to check for underflow of addition/subtraction
+ * because numbers near the underflow value have already been rounded to
+ * the point where we can't detect that the two values were originally
+ * different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
+ * 1.4013e-45.
+ */
+#define FLOAT_PL(result, val1, val2) \
+do { \
+ result = (val1) + (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true); \
+} while(0)
+
+#define FLOAT_MI(result, val1, val2) \
+do { \
+ result = (val1) - (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true); \
+} while(0)
+
+#define FLOAT_MUL(result, val1, val2) \
+do { \
+ result = (val1) * (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), \
+ (val1) == 0.0 || (val2) == 0.0); \
+} while(0)
+
+#define FLOAT_DIV(result, val1, val2) \
+do { \
+ if (val2 == 0.0) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_DIVISION_BY_ZERO), \
+ errmsg("division by zero"))); \
+ \
+ result = (val1) / (val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), \
+ (val1) == 0.0); \
+} while(0)
+
+/*
+ * Convenience macros for NaN-aware comparisons
+ *
+ * We consider all NANs to be equal and larger than any non-NAN. This is
+ * somewhat arbitrary; the important thing is to have a consistent sort
+ * order.
+ */
+#define FLOAT_EQ(a, b) (isnan(a) ? isnan(b) : !isnan(b) && (a) == (b))
+#define FLOAT_NE(a, b) (isnan(a) ? !isnan(b) : isnan(b) || (a) != (b))
+#define FLOAT_LT(a, b) (!isnan(a) && (isnan(b) || (a) < (b)))
+#define FLOAT_LE(a, b) (isnan(b) || (!isnan(a) && (a) <= (b)))
+#define FLOAT_GT(a, b) (!isnan(b) && (isnan(a) || (a) > (b)))
+#define FLOAT_GE(a, b) (isnan(a) || (!isnan(b) && (a) >= (b)))
+#define FLOAT_MAX(a, b) (FLOAT_GT(a, b) ? (a) : (b))
+#define FLOAT_MIN(a, b) (FLOAT_LT(a, b) ? (a) : (b))
+
+extern PGDLLIMPORT int extra_float_digits;
+
+/*
+ * Utility functions in float.c
+ */
+extern float get_float4_infinity(void);
+extern double get_float8_infinity(void);
+extern float get_float4_nan(void);
+extern double get_float8_nan(void);
+extern int is_infinite(double val);
+extern double float8in_internal(char *num, char **endptr_p,
+ const char *type_name, const char *orig_string);
+extern char *float8out_internal(double num);
+extern int float4_cmp_internal(float4 a, float4 b);
+extern int float8_cmp_internal(float8 a, float8 b);
+
+#endif /* FLOAT_H */
--
2.7.4 (Apple Git-66)
0002-geo-ops-fpcomp-v02.patchapplication/octet-stream; name=0002-geo-ops-fpcomp-v02.patchDownload
From d5303716840e8a0bca65b6f8c8445d2a4328b6bd Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Mon, 20 Jun 2016 10:24:32 +0200
Subject: [PATCH 2/2] geo-ops-fpcomp-v02
---
src/backend/access/gist/gistproc.c | 102 +-
src/backend/access/spgist/spgkdtreeproc.c | 25 +-
src/backend/utils/adt/geo_ops.c | 1497 ++++++++++++++++------------
src/backend/utils/adt/geo_spgist.c | 26 +-
src/include/utils/geo_decls.h | 35 +-
src/test/regress/expected/line.out | 30 +-
src/test/regress/expected/point.out | 8 +-
src/test/regress/expected/select_views.out | 335 +------
src/test/regress/regress.c | 11 +-
9 files changed, 944 insertions(+), 1125 deletions(-)
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index c7ac033..7cd55c0 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -859,24 +859,20 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
v->spl_ldatum = PointerGetDatum(leftBox);
v->spl_rdatum = PointerGetDatum(rightBox);
PG_RETURN_POINTER(v);
}
/*
* Equality method
*
* This is used for boxes, points, circles, and polygons, all of which store
* boxes as GiST index entries.
- *
- * Returns true only when boxes are exactly the same. We can't use fuzzy
- * comparisons here without breaking index consistency; therefore, this isn't
- * equivalent to box_same().
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
*result = (FLOAT_EQ(b1->low.x, b2->low.x) &&
@@ -1128,24 +1124,24 @@ gist_circle_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
CIRCLE *in = DatumGetCircleP(entry->key);
BOX *r;
r = (BOX *) palloc(sizeof(BOX));
- r->high.x = in->center.x + in->radius;
- r->low.x = in->center.x - in->radius;
- r->high.y = in->center.y + in->radius;
- r->low.y = in->center.y - in->radius;
+ FLOAT_PL(r->high.x, in->center.x, in->radius);
+ FLOAT_MI(r->low.x, in->center.x, in->radius);
+ FLOAT_PL(r->high.y, in->center.y, in->radius);
+ FLOAT_MI(r->low.y, in->center.y, in->radius);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, FALSE);
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
@@ -1169,24 +1165,24 @@ gist_circle_consistent(PG_FUNCTION_ARGS)
*recheck = true;
if (DatumGetBoxP(entry->key) == NULL || query == NULL)
PG_RETURN_BOOL(FALSE);
/*
* Since the operators require recheck anyway, we can just use
* rtree_internal_consistent even at leaf nodes. (This works in part
* because the index entries are bounding boxes not circles.)
*/
- bbox.high.x = query->center.x + query->radius;
- bbox.low.x = query->center.x - query->radius;
- bbox.high.y = query->center.y + query->radius;
- bbox.low.y = query->center.y - query->radius;
+ FLOAT_PL(bbox.high.x, query->center.x, query->radius);
+ FLOAT_MI(bbox.low.x, query->center.x, query->radius);
+ FLOAT_PL(bbox.high.y, query->center.y, query->radius);
+ FLOAT_MI(bbox.low.y, query->center.y, query->radius);
result = rtree_internal_consistent(DatumGetBoxP(entry->key),
&bbox, strategy);
PG_RETURN_BOOL(result);
}
/**************************************************
* Point ops
**************************************************/
@@ -1247,109 +1243,113 @@ gist_point_fetch(PG_FUNCTION_ARGS)
static double
computeDistance(bool isLeaf, BOX *box, Point *point)
{
double result = 0.0;
if (isLeaf)
{
/* simple point to point distance */
result = point_point_distance(point, &box->low);
}
- else if (point->x <= box->high.x && point->x >= box->low.x &&
- point->y <= box->high.y && point->y >= box->low.y)
+ else if (FLOAT_LE(point->x, box->high.x) &&
+ FLOAT_GE(point->x, box->low.x) &&
+ FLOAT_LE(point->y, box->high.y) &&
+ FLOAT_GE(point->y, box->low.y))
{
/* point inside the box */
result = 0.0;
}
- else if (point->x <= box->high.x && point->x >= box->low.x)
+ else if (FLOAT_LE(point->x, box->high.x) &&
+ FLOAT_GE(point->x, box->low.x))
{
/* point is over or below box */
- Assert(box->low.y <= box->high.y);
- if (point->y > box->high.y)
- result = point->y - box->high.y;
- else if (point->y < box->low.y)
- result = box->low.y - point->y;
+ Assert(FLOAT_LE(box->low.y, box->high.y));
+ if (FLOAT_GT(point->y, box->high.y))
+ FLOAT_MI(result, point->y, box->high.y);
+ else if (FLOAT_LT(point->y, box->low.y))
+ FLOAT_MI(result, box->low.y, point->y);
else
elog(ERROR, "inconsistent point values");
}
- else if (point->y <= box->high.y && point->y >= box->low.y)
+ else if (FLOAT_LE(point->y, box->high.y) &&
+ FLOAT_GE(point->y, box->low.y))
{
/* point is to left or right of box */
- Assert(box->low.x <= box->high.x);
- if (point->x > box->high.x)
- result = point->x - box->high.x;
- else if (point->x < box->low.x)
- result = box->low.x - point->x;
+ Assert(FLOAT_LE(box->low.x, box->high.x));
+ if (FLOAT_GT(point->x, box->high.x))
+ FLOAT_MI(result, point->x, box->high.x);
+ else if (FLOAT_LT(point->x, box->low.x))
+ FLOAT_MI(result, box->low.x, point->x);
else
elog(ERROR, "inconsistent point values");
}
else
{
/* closest point will be a vertex */
Point p;
double subresult;
result = point_point_distance(point, &box->low);
subresult = point_point_distance(point, &box->high);
- if (result > subresult)
+ if (FLOAT_GT(result, subresult))
result = subresult;
p.x = box->low.x;
p.y = box->high.y;
subresult = point_point_distance(point, &p);
- if (result > subresult)
+ if (FLOAT_GT(result, subresult))
result = subresult;
p.x = box->high.x;
p.y = box->low.y;
subresult = point_point_distance(point, &p);
- if (result > subresult)
+ if (FLOAT_GT(result, subresult))
result = subresult;
}
return result;
}
static bool
gist_point_consistent_internal(StrategyNumber strategy,
bool isLeaf, BOX *key, Point *query)
{
bool result = false;
switch (strategy)
{
case RTLeftStrategyNumber:
- result = FPlt(key->low.x, query->x);
+ result = FLOAT_LT(key->low.x, query->x);
break;
case RTRightStrategyNumber:
- result = FPgt(key->high.x, query->x);
+ result = FLOAT_GT(key->high.x, query->x);
break;
case RTAboveStrategyNumber:
- result = FPgt(key->high.y, query->y);
+ result = FLOAT_GT(key->high.y, query->y);
break;
case RTBelowStrategyNumber:
- result = FPlt(key->low.y, query->y);
+ result = FLOAT_LT(key->low.y, query->y);
break;
case RTSameStrategyNumber:
if (isLeaf)
{
/* key.high must equal key.low, so we can disregard it */
- result = (FPeq(key->low.x, query->x) &&
- FPeq(key->low.y, query->y));
+ result = (FLOAT_EQ(key->low.x, query->x) &&
+ FLOAT_EQ(key->low.y, query->y));
}
else
{
- result = (FPle(query->x, key->high.x) &&
- FPge(query->x, key->low.x) &&
- FPle(query->y, key->high.y) &&
- FPge(query->y, key->low.y));
+ result = (FLOAT_LE(query->x, key->high.x) &&
+ FLOAT_GE(query->x, key->low.x) &&
+ FLOAT_LE(query->y, key->high.y) &&
+ FLOAT_GE(query->y, key->low.y));
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
result = false; /* keep compiler quiet */
break;
}
return result;
}
@@ -1376,39 +1376,31 @@ gist_point_consistent(PG_FUNCTION_ARGS)
GIST_LEAF(entry),
DatumGetBoxP(entry->key),
PG_GETARG_POINT_P(1));
*recheck = false;
break;
case BoxStrategyNumberGroup:
{
/*
* The only operator in this group is point <@ box (on_pb), so
* we needn't examine strategy again.
- *
- * For historical reasons, on_pb uses exact rather than fuzzy
- * comparisons. We could use box_overlap when at an internal
- * page, but that would lead to possibly visiting child pages
- * uselessly, because box_overlap uses fuzzy comparisons.
- * Instead we write a non-fuzzy overlap test. The same code
- * will also serve for leaf-page tests, since leaf keys have
- * high == low.
*/
BOX *query,
*key;
query = PG_GETARG_BOX_P(1);
key = DatumGetBoxP(entry->key);
- result = (key->high.x >= query->low.x &&
- key->low.x <= query->high.x &&
- key->high.y >= query->low.y &&
- key->low.y <= query->high.y);
+ result = (FLOAT_GE(key->high.x, query->low.x) &&
+ FLOAT_LE(key->low.x, query->high.x) &&
+ FLOAT_GE(key->high.y, query->low.y) &&
+ FLOAT_LE(key->low.y, query->high.y));
*recheck = false;
}
break;
case PolygonStrategyNumberGroup:
{
POLYGON *query = PG_GETARG_POLYGON_P(1);
result = DatumGetBool(DirectFunctionCall5(
gist_poly_consistent,
PointerGetDatum(entry),
@@ -1417,22 +1409,22 @@ gist_point_consistent(PG_FUNCTION_ARGS)
0, PointerGetDatum(recheck)));
if (GIST_LEAF(entry) && result)
{
/*
* We are on leaf page and quick check shows overlapping
* of polygon's bounding box and point
*/
BOX *box = DatumGetBoxP(entry->key);
- Assert(box->high.x == box->low.x
- && box->high.y == box->low.y);
+ Assert(FLOAT_EQ(box->high.x, box->low.x) &&
+ FLOAT_EQ(box->high.y, box->low.y));
result = DatumGetBool(DirectFunctionCall2(
poly_contain_pt,
PolygonPGetDatum(query),
PointPGetDatum(&box->high)));
*recheck = false;
}
}
break;
case CircleStrategyNumberGroup:
{
@@ -1446,22 +1438,22 @@ gist_point_consistent(PG_FUNCTION_ARGS)
0, PointerGetDatum(recheck)));
if (GIST_LEAF(entry) && result)
{
/*
* We are on leaf page and quick check shows overlapping
* of polygon's bounding box and point
*/
BOX *box = DatumGetBoxP(entry->key);
- Assert(box->high.x == box->low.x
- && box->high.y == box->low.y);
+ Assert(FLOAT_EQ(box->high.x, box->low.x) &&
+ FLOAT_EQ(box->high.y, box->low.y));
result = DatumGetBool(DirectFunctionCall2(
circle_contain_pt,
CirclePGetDatum(query),
PointPGetDatum(&box->high)));
*recheck = false;
}
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
diff --git a/src/backend/access/spgist/spgkdtreeproc.c b/src/backend/access/spgist/spgkdtreeproc.c
index 1ab9335..10a08ed 100644
--- a/src/backend/access/spgist/spgkdtreeproc.c
+++ b/src/backend/access/spgist/spgkdtreeproc.c
@@ -12,20 +12,21 @@
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
Datum
spg_kd_config(PG_FUNCTION_ARGS)
{
/* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */
spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
cfg->prefixType = FLOAT8OID;
@@ -175,72 +176,72 @@ spg_kd_inner_consistent(PG_FUNCTION_ARGS)
which = (1 << 1) | (1 << 2);
for (i = 0; i < in->nkeys; i++)
{
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
BOX *boxQuery;
switch (in->scankeys[i].sk_strategy)
{
case RTLeftStrategyNumber:
- if ((in->level % 2) != 0 && FPlt(query->x, coord))
+ if ((in->level % 2) != 0 && FLOAT_LT(query->x, coord))
which &= (1 << 1);
break;
case RTRightStrategyNumber:
- if ((in->level % 2) != 0 && FPgt(query->x, coord))
+ if ((in->level % 2) != 0 && FLOAT_GT(query->x, coord))
which &= (1 << 2);
break;
case RTSameStrategyNumber:
if ((in->level % 2) != 0)
{
- if (FPlt(query->x, coord))
+ if (FLOAT_LT(query->x, coord))
which &= (1 << 1);
- else if (FPgt(query->x, coord))
+ else if (FLOAT_GT(query->x, coord))
which &= (1 << 2);
}
else
{
- if (FPlt(query->y, coord))
+ if (FLOAT_LT(query->y, coord))
which &= (1 << 1);
- else if (FPgt(query->y, coord))
+ else if (FLOAT_GT(query->y, coord))
which &= (1 << 2);
}
break;
case RTBelowStrategyNumber:
- if ((in->level % 2) == 0 && FPlt(query->y, coord))
+ if ((in->level % 2) == 0 && FLOAT_LT(query->y, coord))
which &= (1 << 1);
break;
case RTAboveStrategyNumber:
- if ((in->level % 2) == 0 && FPgt(query->y, coord))
+ if ((in->level % 2) == 0 && FLOAT_GT(query->y, coord))
which &= (1 << 2);
break;
case RTContainedByStrategyNumber:
/*
* For this operator, the query is a box not a point. We
* cheat to the extent of assuming that DatumGetPointP won't
* do anything that would be bad for a pointer-to-box.
*/
boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
if ((in->level % 2) != 0)
{
- if (FPlt(boxQuery->high.x, coord))
+ if (FLOAT_LT(boxQuery->high.x, coord))
which &= (1 << 1);
- else if (FPgt(boxQuery->low.x, coord))
+ else if (FLOAT_GT(boxQuery->low.x, coord))
which &= (1 << 2);
}
else
{
- if (FPlt(boxQuery->high.y, coord))
+ if (FLOAT_LT(boxQuery->high.y, coord))
which &= (1 << 1);
- else if (FPgt(boxQuery->low.y, coord))
+ else if (FLOAT_GT(boxQuery->low.y, coord))
which &= (1 << 2);
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d",
in->scankeys[i].sk_strategy);
break;
}
if (which == 0)
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index efc81ab..3855c8e 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -36,50 +36,56 @@ enum path_delim
static int point_inside(Point *p, int npts, Point *plist);
static int lseg_crossing(double x, double y, double px, double py);
static BOX *box_construct(double x1, double x2, double y1, double y2);
static BOX *box_copy(BOX *box);
static BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2);
static bool box_ov(BOX *box1, BOX *box2);
static double box_ht(BOX *box);
static double box_wd(BOX *box);
static double circle_ar(CIRCLE *circle);
-static CIRCLE *circle_copy(CIRCLE *circle);
+static CIRCLE *circle_construct(double x, double y, double radius);
static LINE *line_construct_pm(Point *pt, double m);
-static void line_construct_pts(LINE *line, Point *pt1, Point *pt2);
+static LINE *line_construct_pts(Point *pt1, Point *pt2);
static bool lseg_intersect_internal(LSEG *l1, LSEG *l2);
static double lseg_dt(LSEG *l1, LSEG *l2);
static bool on_ps_internal(Point *pt, LSEG *lseg);
+static double point_on_line_value(Point *pt, LINE *line);
+static int point_lseg_cmp(Point *pt, LSEG *lseg);
static void make_bound_box(POLYGON *poly);
static bool plist_same(int npts, Point *p1, Point *p2);
static Point *point_construct(double x, double y);
static Point *point_copy(Point *pt);
+static double point_dt(Point *pt1, Point *pt2);
+static double slope(double x1, double x2, double y1, double y2);
static double single_decode(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
static void single_encode(float8 x, StringInfo str);
static void pair_decode(char *str, double *x, double *y, char **endptr_p,
const char *type_name, const char *orig_string);
static void pair_encode(float8 x, float8 y, StringInfo str);
static int pair_count(char *s, char delim);
static void path_decode(char *str, bool opentype, int npts, Point *p,
bool *isopen, char **endptr_p,
const char *type_name, const char *orig_string);
static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
static void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
static double box_ar(BOX *box);
static void box_cn(Point *center, BOX *box);
static Point *interpt_sl(LSEG *lseg, LINE *line);
static bool has_interpt_sl(LSEG *lseg, LINE *line);
+static Point *close_pl_internal(Point *pt, LINE *line);
static double dist_pl_internal(Point *pt, LINE *line);
static double dist_ps_internal(Point *pt, LSEG *lseg);
static Point *line_interpt_internal(LINE *l1, LINE *l2);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
+static Point *lseg_center_internal(LSEG *lseg);
static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2);
static double dist_ppoly_internal(Point *pt, POLYGON *poly);
/*
* Delimiters for input and output strings.
* LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively.
* LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints.
*/
@@ -349,27 +355,27 @@ box_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
BOX *box = (BOX *) palloc(sizeof(BOX));
bool isopen;
double x,
y;
path_decode(str, false, 2, &(box->high), &isopen, NULL, "box", str);
/* reorder corners if necessary... */
- if (box->high.x < box->low.x)
+ if (FLOAT_LT(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
- if (box->high.y < box->low.y)
+ if (FLOAT_LT(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/* box_out - convert a box to external form.
@@ -394,27 +400,27 @@ box_recv(PG_FUNCTION_ARGS)
y;
box = (BOX *) palloc(sizeof(BOX));
box->high.x = pq_getmsgfloat8(buf);
box->high.y = pq_getmsgfloat8(buf);
box->low.x = pq_getmsgfloat8(buf);
box->low.y = pq_getmsgfloat8(buf);
/* reorder corners if necessary... */
- if (box->high.x < box->low.x)
+ if (FLOAT_LT(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
- if (box->high.y < box->low.y)
+ if (FLOAT_LT(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/*
@@ -444,31 +450,31 @@ box_construct(double x1, double x2, double y1, double y2)
return box_fill(result, x1, x2, y1, y2);
}
/* box_fill - fill in a given box struct
*/
static BOX *
box_fill(BOX *result, double x1, double x2, double y1, double y2)
{
- if (x1 > x2)
+ if (FLOAT_GT(x1, x2))
{
result->high.x = x1;
result->low.x = x2;
}
else
{
result->high.x = x2;
result->low.x = x1;
}
- if (y1 > y2)
+ if (FLOAT_GT(y1, y2))
{
result->high.y = y1;
result->low.y = y2;
}
else
{
result->high.y = y2;
result->low.y = y1;
}
@@ -495,246 +501,246 @@ box_copy(BOX *box)
*---------------------------------------------------------*/
/* box_same - are two boxes identical?
*/
Datum
box_same(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box1->high.x, box2->high.x) &&
- FPeq(box1->low.x, box2->low.x) &&
- FPeq(box1->high.y, box2->high.y) &&
- FPeq(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_EQ(box1->high.x, box2->high.x) &&
+ FLOAT_EQ(box1->low.x, box2->low.x) &&
+ FLOAT_EQ(box1->high.y, box2->high.y) &&
+ FLOAT_EQ(box1->low.y, box2->low.y));
}
/* box_overlap - does box1 overlap box2?
*/
Datum
box_overlap(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
PG_RETURN_BOOL(box_ov(box1, box2));
}
static bool
box_ov(BOX *box1, BOX *box2)
{
- return (FPle(box1->low.x, box2->high.x) &&
- FPle(box2->low.x, box1->high.x) &&
- FPle(box1->low.y, box2->high.y) &&
- FPle(box2->low.y, box1->high.y));
+ return (FLOAT_LE(box1->low.x, box2->high.x) &&
+ FLOAT_LE(box2->low.x, box1->high.x) &&
+ FLOAT_LE(box1->low.y, box2->high.y) &&
+ FLOAT_LE(box2->low.y, box1->high.y));
}
/* box_left - is box1 strictly left of box2?
*/
Datum
box_left(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
+ PG_RETURN_BOOL(FLOAT_LT(box1->high.x, box2->low.x));
}
/* box_overleft - is the right edge of box1 at or left of
* the right edge of box2?
*
* This is "less than or equal" for the end of a time range,
* when time ranges are stored as rectangles.
*/
Datum
box_overleft(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.x, box2->high.x));
}
/* box_right - is box1 strictly right of box2?
*/
Datum
box_right(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
+ PG_RETURN_BOOL(FLOAT_GT(box1->low.x, box2->high.x));
}
/* box_overright - is the left edge of box1 at or right of
* the left edge of box2?
*
* This is "greater than or equal" for time ranges, when time ranges
* are stored as rectangles.
*/
Datum
box_overright(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
+ PG_RETURN_BOOL(FLOAT_GE(box1->low.x, box2->low.x));
}
/* box_below - is box1 strictly below box2?
*/
Datum
box_below(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_LT(box1->high.y, box2->low.y));
}
/* box_overbelow - is the upper edge of box1 at or below
* the upper edge of box2?
*/
Datum
box_overbelow(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.y, box2->high.y));
}
/* box_above - is box1 strictly above box2?
*/
Datum
box_above(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(FLOAT_GT(box1->low.y, box2->high.y));
}
/* box_overabove - is the lower edge of box1 at or above
* the lower edge of box2?
*/
Datum
box_overabove(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_GE(box1->low.y, box2->low.y));
}
/* box_contained - is box1 contained by box2?
*/
Datum
box_contained(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x) &&
- FPge(box1->low.x, box2->low.x) &&
- FPle(box1->high.y, box2->high.y) &&
- FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.x, box2->high.x) &&
+ FLOAT_GE(box1->low.x, box2->low.x) &&
+ FLOAT_LE(box1->high.y, box2->high.y) &&
+ FLOAT_GE(box1->low.y, box2->low.y));
}
/* box_contain - does box1 contain box2?
*/
Datum
box_contain(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_GE(box1->high.x, box2->high.x) &&
+ FLOAT_LE(box1->low.x, box2->low.x) &&
+ FLOAT_GE(box1->high.y, box2->high.y) &&
+ FLOAT_LE(box1->low.y, box2->low.y));
}
/* box_positionop -
* is box1 entirely {above,below} box2?
*
* box_below_eq and box_above_eq are obsolete versions that (probably
* erroneously) accept the equal-boundaries case. Since these are not
* in sync with the box_left and box_right code, they are deprecated and
* not supported in the PG 8.1 rtree operator class extension.
*/
Datum
box_below_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(FLOAT_LE(box1->high.y, box2->low.y));
}
Datum
box_above_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(FLOAT_GE(box1->low.y, box2->high.y));
}
/* box_relop - is area(box1) relop area(box2), within
* our accuracy constraint?
*/
Datum
box_lt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_LT(box_ar(box1), box_ar(box2)));
}
Datum
box_gt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_GT(box_ar(box1), box_ar(box2)));
}
Datum
box_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_EQ(box_ar(box1), box_ar(box2)));
}
Datum
box_le(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_LE(box_ar(box1), box_ar(box2)));
}
Datum
box_ge(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(FLOAT_GE(box_ar(box1), box_ar(box2)));
}
/*----------------------------------------------------------
* "Arithmetic" operators on boxes.
*---------------------------------------------------------*/
/* box_area - returns the area of the box.
*/
Datum
@@ -777,21 +783,21 @@ Datum
box_distance(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
Point a,
b;
box_cn(&a, box1);
box_cn(&b, box2);
- PG_RETURN_FLOAT8(HYPOT(a.x - b.x, a.y - b.y));
+ PG_RETURN_FLOAT8(point_dt(&a, &b));
}
/* box_center - returns the center point of the box.
*/
Datum
box_center(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *result = (Point *) palloc(sizeof(Point));
@@ -809,22 +815,28 @@ box_ar(BOX *box)
{
return box_wd(box) * box_ht(box);
}
/* box_cn - stores the centerpoint of the box into *center.
*/
static void
box_cn(Point *center, BOX *box)
{
- center->x = (box->high.x + box->low.x) / 2.0;
- center->y = (box->high.y + box->low.y) / 2.0;
+ double sumx;
+ double sumy;
+
+ FLOAT_PL(sumx, box->high.x, box->low.x);
+ FLOAT_DIV(center->x, sumx, 2.0);
+
+ FLOAT_PL(sumy, box->high.y, box->low.y);
+ FLOAT_DIV(center->y, sumy, 2.0);
}
/* box_wd - returns the width (length) of the box
* (horizontal magnitude).
*/
static double
box_wd(BOX *box)
{
return box->high.x - box->low.x;
@@ -854,24 +866,24 @@ box_intersect(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
BOX *result;
if (!box_ov(box1, box2))
PG_RETURN_NULL();
result = (BOX *) palloc(sizeof(BOX));
- result->high.x = Min(box1->high.x, box2->high.x);
- result->low.x = Max(box1->low.x, box2->low.x);
- result->high.y = Min(box1->high.y, box2->high.y);
- result->low.y = Max(box1->low.y, box2->low.y);
+ result->high.x = FLOAT_MIN(box1->high.x, box2->high.x);
+ result->low.x = FLOAT_MAX(box1->low.x, box2->low.x);
+ result->high.y = FLOAT_MIN(box1->high.y, box2->high.y);
+ result->low.y = FLOAT_MAX(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(result);
}
/* box_diagonal -
* returns a line segment which happens to be the
* positive-slope diagonal of "box".
*/
Datum
@@ -908,48 +920,51 @@ line_decode(char *s, const char *str, LINE *line)
s++;
if (*s != '\0')
return false;
return true;
}
Datum
line_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- LINE *line = (LINE *) palloc(sizeof(LINE));
+ LINE *line;
LSEG lseg;
bool isopen;
char *s;
s = str;
while (isspace((unsigned char) *s))
s++;
if (*s == '{')
{
+ line = (LINE *) palloc(sizeof(LINE));
+
if (!line_decode(s + 1, str, line))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"line", str)));
- if (FPzero(line->A) && FPzero(line->B))
+ if (FLOAT_EQ(line->A, 0.0) && FLOAT_EQ(line->B, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: A and B cannot both be zero")));
}
else
{
path_decode(s, true, 2, &(lseg.p[0]), &isopen, NULL, "line", str);
- if (FPeq(lseg.p[0].x, lseg.p[1].x) && FPeq(lseg.p[0].y, lseg.p[1].y))
+ if (FLOAT_EQ(lseg.p[0].x, lseg.p[1].x) &&
+ FLOAT_EQ(lseg.p[0].y, lseg.p[1].y))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: must be two distinct points")));
- line_construct_pts(line, &lseg.p[0], &lseg.p[1]);
+ line = line_construct_pts(&lseg.p[0], &lseg.p[1]);
}
PG_RETURN_LINE_P(line);
}
Datum
line_out(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
@@ -968,20 +983,25 @@ line_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
LINE *line;
line = (LINE *) palloc(sizeof(LINE));
line->A = pq_getmsgfloat8(buf);
line->B = pq_getmsgfloat8(buf);
line->C = pq_getmsgfloat8(buf);
+ if (FLOAT_EQ(line->A, 0.0) && FLOAT_EQ(line->B, 0.0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid line specification: A and B cannot both be zero")));
+
PG_RETURN_LINE_P(line);
}
/*
* line_send - converts line to binary format
*/
Datum
line_send(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
@@ -1000,93 +1020,101 @@ line_send(PG_FUNCTION_ARGS)
* Internal form: Ax+By+C=0
*---------------------------------------------------------*/
/* line_construct_pm()
* point-slope
*/
static LINE *
line_construct_pm(Point *pt, double m)
{
LINE *result = (LINE *) palloc(sizeof(LINE));
+ double val;
- if (m == DBL_MAX)
+ if (FLOAT_EQ(m, DBL_MAX))
{
/* vertical - use "x = C" */
- result->A = -1;
- result->B = 0;
+ result->A = -1.0;
+ result->B = 0.0;
result->C = pt->x;
}
else
{
- /* use "mx - y + yinter = 0" */
result->A = m;
result->B = -1.0;
- result->C = pt->y - m * pt->x;
+ FLOAT_MUL(val, m, pt->x);
+ FLOAT_MI(result->C, pt->y, val);
}
return result;
}
/*
* Fill already-allocated LINE struct from two points on the line
*/
-static void
-line_construct_pts(LINE *line, Point *pt1, Point *pt2)
+static LINE *
+line_construct_pts(Point *pt1, Point *pt2)
{
- if (FPeq(pt1->x, pt2->x))
+ LINE *result = (LINE *) palloc(sizeof(LINE));
+
+ if (FLOAT_EQ(pt1->x, pt2->x))
{ /* vertical */
/* use "x = C" */
- line->A = -1;
- line->B = 0;
- line->C = pt1->x;
+ result->A = -1.0;
+ result->B = 0.0;
+ result->C = pt1->x;
#ifdef GEODEBUG
printf("line_construct_pts- line is vertical\n");
#endif
}
- else if (FPeq(pt1->y, pt2->y))
+ else if (FLOAT_EQ(pt1->y, pt2->y))
{ /* horizontal */
/* use "y = C" */
- line->A = 0;
- line->B = -1;
- line->C = pt1->y;
+ result->A = 0.0;
+ result->B = -1.0;
+ result->C = pt1->y;
#ifdef GEODEBUG
printf("line_construct_pts- line is horizontal\n");
#endif
}
else
{
- /* use "mx - y + yinter = 0" */
- line->A = (pt2->y - pt1->y) / (pt2->x - pt1->x);
- line->B = -1.0;
- line->C = pt1->y - line->A * pt1->x;
- /* on some platforms, the preceding expression tends to produce -0 */
- if (line->C == 0.0)
- line->C = 0.0;
+ double mul_x;
+ double mul_y;
+
+ /* The equation is optimized to avoid losing precision by division. */
+ FLOAT_MI(result->A, pt2->y, pt1->y);
+ FLOAT_MI(result->B, pt1->x, pt2->x);
+ FLOAT_MUL(mul_x, pt1->x, result->A);
+ FLOAT_MUL(mul_y, pt1->y, result->B);
+ FLOAT_PL(result->C, mul_x, mul_y);
+ if (FLOAT_NE(result->C, 0.0)) /* We don't want to get -0. */
+ result->C = -result->C;
+
#ifdef GEODEBUG
printf("line_construct_pts- line is neither vertical nor horizontal (diffs x=%.*g, y=%.*g\n",
DBL_DIG, (pt2->x - pt1->x), DBL_DIG, (pt2->y - pt1->y));
#endif
}
+
+ return result;
}
/* line_construct_pp()
* two points
*/
Datum
line_construct_pp(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- LINE *result = (LINE *) palloc(sizeof(LINE));
- line_construct_pts(result, pt1, pt2);
- PG_RETURN_LINE_P(result);
+ PG_RETURN_LINE_P(line_construct_pts(pt1, pt2));
}
/*----------------------------------------------------------
* Relative position routines.
*---------------------------------------------------------*/
Datum
line_intersect(PG_FUNCTION_ARGS)
{
@@ -1096,102 +1124,139 @@ line_intersect(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(!DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))));
}
Datum
line_parallel(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
+ double mul12;
+ double mul21;
- if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->B));
+ if (FLOAT_EQ(l1->A, 0.0)) /* horizontal? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->A, 0.0));
+ if (FLOAT_EQ(l1->B, 0.0)) /* vertical? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->B, 0.0));
- PG_RETURN_BOOL(FPeq(l2->A, l1->A * (l2->B / l1->B)));
+ if (FLOAT_EQ(l2->A, 0.0) || FLOAT_EQ(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(mul12, l1->A, l2->B);
+ FLOAT_MUL(mul21, l2->A, l1->B);
+
+ PG_RETURN_BOOL(FLOAT_EQ(mul12, mul21));
}
Datum
line_perp(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
+ double val1;
+ double val2;
- if (FPzero(l1->A))
- PG_RETURN_BOOL(FPzero(l2->B));
- else if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->A));
+ if (FLOAT_EQ(l1->A, 0.0)) /* horizontal? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->B, 0.0));
+ if (FLOAT_EQ(l1->B, 0.0)) /* vertical? */
+ PG_RETURN_BOOL(FLOAT_EQ(l2->A, 0.0));
- PG_RETURN_BOOL(FPeq(((l1->A * l2->B) / (l1->B * l2->A)), -1.0));
+ if (FLOAT_EQ(l2->A, 0.0) || FLOAT_EQ(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(val1, l1->A, l2->B);
+ FLOAT_MUL(val2, l2->A, l1->B);
+
+ PG_RETURN_BOOL(FLOAT_EQ(val1, -val2));
}
Datum
line_vertical(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->B));
+ PG_RETURN_BOOL(FLOAT_EQ(line->B, 0.0));
}
Datum
line_horizontal(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->A));
+ PG_RETURN_BOOL(FLOAT_EQ(line->A, 0.0));
}
Datum
line_eq(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- double k;
+ double mul12;
+ double mul21;
- if (!FPzero(l2->A))
- k = l1->A / l2->A;
- else if (!FPzero(l2->B))
- k = l1->B / l2->B;
- else if (!FPzero(l2->C))
- k = l1->C / l2->C;
- else
- k = 1.0;
+ FLOAT_MUL(mul12, l1->A, l2->B);
+ FLOAT_MUL(mul21, l2->A, l1->B);
+ if (FLOAT_NE(mul12, mul21))
+ PG_RETURN_BOOL(false);
- PG_RETURN_BOOL(FPeq(l1->A, k * l2->A) &&
- FPeq(l1->B, k * l2->B) &&
- FPeq(l1->C, k * l2->C));
+ if (FLOAT_NE(l1->A, 0.0))
+ {
+ if (FLOAT_EQ(l2->A, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(mul12, l1->A, l2->C);
+ FLOAT_MUL(mul21, l2->A, l1->C);
+ }
+ else /* (l1->A == 0.0 => l1->B != 0.0 => l2->A == 0.0) */
+ {
+ if (FLOAT_EQ(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ FLOAT_MUL(mul12, l1->B, l2->C);
+ FLOAT_MUL(mul21, l2->B, l1->C);
+ }
+
+ PG_RETURN_BOOL(FLOAT_EQ(mul12, mul21));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* line_distance()
* Distance between two lines.
*/
Datum
line_distance(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- float8 result;
- Point *tmp;
+ double result;
+ double y;
if (!DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))))
PG_RETURN_FLOAT8(0.0);
- if (FPzero(l1->B)) /* vertical? */
- PG_RETURN_FLOAT8(fabs(l1->C - l2->C));
- tmp = point_construct(0.0, l1->C);
- result = dist_pl_internal(tmp, l2);
+
+ if (FLOAT_EQ(l1->A, 0.0) || FLOAT_EQ(l1->B, 0.0)) /* horizontal or vertical? */
+ {
+ FLOAT_MI(result, l1->C, l2->C);
+ }
+ else
+ {
+ FLOAT_DIV(y, -l1->C, l1->B);
+ result = dist_pl_internal(point_construct(0.0, y), l2);
+ }
+
PG_RETURN_FLOAT8(result);
}
/* line_interpt()
* Point where two lines l1, l2 intersect (if any)
*/
Datum
line_interpt(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
@@ -1207,48 +1272,69 @@ line_interpt(PG_FUNCTION_ARGS)
/*
* Internal version of line_interpt
*
* returns a NULL pointer if no intersection point
*/
static Point *
line_interpt_internal(LINE *l1, LINE *l2)
{
Point *result;
+ double val1;
+ double val2;
double x,
y;
/*
* NOTE: if the lines are identical then we will find they are parallel
* and report "no intersection". This is a little weird, but since
* there's no *unique* intersection, maybe it's appropriate behavior.
*/
if (DatumGetBool(DirectFunctionCall2(line_parallel,
LinePGetDatum(l1),
LinePGetDatum(l2))))
return NULL;
- if (FPzero(l1->B)) /* l1 vertical? */
+ if (FLOAT_EQ(l1->B, 0.0)) /* l1 vertical? */
{
- x = l1->C;
- y = (l2->A * x + l2->C);
- }
- else if (FPzero(l2->B)) /* l2 vertical? */
- {
- x = l2->C;
- y = (l1->A * x + l1->C);
+ FLOAT_DIV(x, -l1->C, l1->A);
+ FLOAT_MUL(val1, l2->A, x);
+ FLOAT_PL(val2, val1, l2->C);
+ FLOAT_DIV(y, -val2, l2->B);
}
else
{
- x = (l1->C - l2->C) / (l2->A - l1->A);
- y = (l1->A * x + l1->C);
+ if (FLOAT_EQ(l2->B, 0.0)) /* l2 vertical? */
+ {
+ FLOAT_DIV(x, -l2->C, l2->A);
+ }
+ else
+ {
+ double mul12;
+ double mul21;
+
+ FLOAT_MUL(mul12, l1->B, l2->C);
+ FLOAT_MUL(mul21, l2->B, l1->C);
+ FLOAT_MI(val1, mul12, mul21);
+
+ FLOAT_MUL(mul12, l1->A, l2->B);
+ FLOAT_MUL(mul21, l2->A, l1->B);
+ FLOAT_MI(val2, mul12, mul21);
+
+ FLOAT_DIV(x, val1, val2);
+ }
+
+ FLOAT_MUL(val1, l1->A, x);
+ FLOAT_PL(val2, val1, l1->C);
+ FLOAT_DIV(y, -val2, l1->B);
}
+
result = point_construct(x, y);
#ifdef GEODEBUG
printf("line_interpt- lines are A=%.*g, B=%.*g, C=%.*g, A=%.*g, B=%.*g, C=%.*g\n",
DBL_DIG, l1->A, DBL_DIG, l1->B, DBL_DIG, l1->C, DBL_DIG, l2->A, DBL_DIG, l2->B, DBL_DIG, l2->C);
printf("line_interpt- lines intersect at (%.*g,%.*g)\n", DBL_DIG, x, DBL_DIG, y);
#endif
return result;
}
@@ -1274,35 +1360,40 @@ line_interpt_internal(LINE *l1, LINE *l2)
* "[xcoord, ycoord,... ]"
* Also support older format:
* "(closed, npts, xcoord, ycoord,... )"
*---------------------------------------------------------*/
Datum
path_area(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0);
double area = 0.0;
+ double mulxy;
+ double mulyx;
+ double new_area;
int i,
j;
if (!path->closed)
PG_RETURN_NULL();
for (i = 0; i < path->npts; i++)
{
j = (i + 1) % path->npts;
- area += path->p[i].x * path->p[j].y;
- area -= path->p[i].y * path->p[j].x;
+ FLOAT_MUL(mulxy, path->p[i].x, path->p[j].y);
+ FLOAT_MUL(mulyx, path->p[i].y, path->p[j].x);
+ FLOAT_PL(new_area, area, mulxy);
+ FLOAT_MI(area, new_area, mulyx);
}
- area *= 0.5;
- PG_RETURN_FLOAT8(area < 0.0 ? -area : area);
+ FLOAT_MUL(new_area, area, 0.5);
+ PG_RETURN_FLOAT8(fabs(new_area));
}
Datum
path_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
PATH *path;
bool isopen;
char *s;
@@ -1322,21 +1413,21 @@ path_in(PG_FUNCTION_ARGS)
s++;
/* skip single leading paren */
if ((*s == LDELIM) && (strrchr(s, LDELIM) == s))
{
s++;
depth++;
}
base_size = sizeof(path->p[0]) * npts;
- size = offsetof(PATH, p) +base_size;
+ size = offsetof(PATH, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
@@ -1392,21 +1483,21 @@ path_recv(PG_FUNCTION_ARGS)
int32 i;
int size;
closed = pq_getmsgbyte(buf);
npts = pq_getmsgint(buf, sizeof(int32));
if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(PATH, p)) / sizeof(Point)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of points in external \"path\" value")));
- size = offsetof(PATH, p) +sizeof(path->p[0]) * npts;
+ size = offsetof(PATH, p) + sizeof(path->p[0]) * npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = npts;
path->closed = (closed ? 1 : 0);
/* prevent instability in unused pad bytes */
path->dummy = 0;
for (i = 0; i < npts; i++)
{
@@ -1519,31 +1610,31 @@ path_npoints(PG_FUNCTION_ARGS)
PG_RETURN_INT32(path->npts);
}
Datum
path_close(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
- path->closed = TRUE;
+ path->closed = true;
PG_RETURN_PATH_P(path);
}
Datum
path_open(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
- path->closed = FALSE;
+ path->closed = false;
PG_RETURN_PATH_P(path);
}
/* path_inter -
* Does p1 intersect p2 at any point?
* Use bounding boxes for a quick (O(n)) check, then do a
* O(n^2) iterative edge check.
*/
@@ -1559,33 +1650,33 @@ path_inter(PG_FUNCTION_ARGS)
LSEG seg1,
seg2;
if (p1->npts <= 0 || p2->npts <= 0)
PG_RETURN_BOOL(false);
b1.high.x = b1.low.x = p1->p[0].x;
b1.high.y = b1.low.y = p1->p[0].y;
for (i = 1; i < p1->npts; i++)
{
- b1.high.x = Max(p1->p[i].x, b1.high.x);
- b1.high.y = Max(p1->p[i].y, b1.high.y);
- b1.low.x = Min(p1->p[i].x, b1.low.x);
- b1.low.y = Min(p1->p[i].y, b1.low.y);
+ b1.high.x = FLOAT_MAX(p1->p[i].x, b1.high.x);
+ b1.high.y = FLOAT_MAX(p1->p[i].y, b1.high.y);
+ b1.low.x = FLOAT_MIN(p1->p[i].x, b1.low.x);
+ b1.low.y = FLOAT_MIN(p1->p[i].y, b1.low.y);
}
b2.high.x = b2.low.x = p2->p[0].x;
b2.high.y = b2.low.y = p2->p[0].y;
for (i = 1; i < p2->npts; i++)
{
- b2.high.x = Max(p2->p[i].x, b2.high.x);
- b2.high.y = Max(p2->p[i].y, b2.high.y);
- b2.low.x = Min(p2->p[i].x, b2.low.x);
- b2.low.y = Min(p2->p[i].y, b2.low.y);
+ b2.high.x = FLOAT_MAX(p2->p[i].x, b2.high.x);
+ b2.high.y = FLOAT_MAX(p2->p[i].y, b2.high.y);
+ b2.low.x = FLOAT_MIN(p2->p[i].x, b2.low.x);
+ b2.low.y = FLOAT_MIN(p2->p[i].y, b2.low.y);
}
if (!box_ov(&b1, &b2))
PG_RETURN_BOOL(false);
/* pairwise check lseg intersections */
for (i = 0; i < p1->npts; i++)
{
int iprev;
if (i > 0)
@@ -1663,21 +1754,21 @@ path_distance(PG_FUNCTION_ARGS)
continue;
jprev = p2->npts - 1; /* include the closure segment */
}
statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
tmp = DatumGetFloat8(DirectFunctionCall2(lseg_distance,
LsegPGetDatum(&seg1),
LsegPGetDatum(&seg2)));
- if (!have_min || tmp < min)
+ if (!have_min || FLOAT_LT(tmp, min))
{
min = tmp;
have_min = true;
}
}
}
if (!have_min)
PG_RETURN_NULL();
@@ -1686,38 +1777,43 @@ path_distance(PG_FUNCTION_ARGS)
/*----------------------------------------------------------
* "Arithmetic" operations.
*---------------------------------------------------------*/
Datum
path_length(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0);
+ bool has_inf = false;
+ float8 distance;
float8 result = 0.0;
int i;
for (i = 0; i < path->npts; i++)
{
int iprev;
if (i > 0)
iprev = i - 1;
else
{
if (!path->closed)
continue;
iprev = path->npts - 1; /* include the closure segment */
}
- result += point_dt(&path->p[iprev], &path->p[i]);
+ distance = point_dt(&path->p[iprev], &path->p[i]);
+ has_inf |= isinf(distance);
+ result += distance;
}
+ CHECKFLOATVAL(result, has_inf, true);
PG_RETURN_FLOAT8(result);
}
/***********************************************************************
**
** Routines for 2D points.
**
***********************************************************************/
@@ -1746,26 +1842,23 @@ point_out(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(path_encode(PATH_NONE, 1, pt));
}
/*
* point_recv - converts external binary format to point
*/
Datum
point_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- Point *point;
- point = (Point *) palloc(sizeof(Point));
- point->x = pq_getmsgfloat8(buf);
- point->y = pq_getmsgfloat8(buf);
- PG_RETURN_POINT_P(point);
+ PG_RETURN_POINT_P(point_construct(pq_getmsgfloat8(buf),
+ pq_getmsgfloat8(buf)));
}
/*
* point_send - converts point to binary format
*/
Datum
point_send(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
StringInfoData buf;
@@ -1784,153 +1877,162 @@ point_construct(double x, double y)
result->x = x;
result->y = y;
return result;
}
static Point *
point_copy(Point *pt)
{
- Point *result;
-
- if (!PointerIsValid(pt))
- return NULL;
-
- result = (Point *) palloc(sizeof(Point));
-
- result->x = pt->x;
- result->y = pt->y;
- return result;
+ return point_construct(pt->x, pt->y);
}
/*----------------------------------------------------------
* Relational operators for Points.
* Since we do have a sense of coordinates being
* "equal" to a given accuracy (point_vert, point_horiz),
* the other ops must preserve that sense. This means
* that results may, strictly speaking, be a lie (unless
* EPSILON = 0.0).
*---------------------------------------------------------*/
Datum
point_left(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(FLOAT_LT(pt1->x, pt2->x));
}
Datum
point_right(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(FLOAT_GT(pt1->x, pt2->x));
}
Datum
point_above(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_GT(pt1->y, pt2->y));
}
Datum
point_below(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_LT(pt1->y, pt2->y));
}
Datum
point_vert(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
+ PG_RETURN_BOOL(FLOAT_EQ(pt1->x, pt2->x));
}
Datum
point_horiz(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_EQ(pt1->y, pt2->y));
}
Datum
point_eq(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_EQ(pt1->x, pt2->x) && FLOAT_EQ(pt1->y, pt2->y));
}
Datum
point_ne(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPne(pt1->x, pt2->x) || FPne(pt1->y, pt2->y));
+ PG_RETURN_BOOL(FLOAT_NE(pt1->x, pt2->x) || FLOAT_NE(pt1->y, pt2->y));
}
/*----------------------------------------------------------
* "Arithmetic" operators on points.
*---------------------------------------------------------*/
Datum
point_distance(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_FLOAT8(HYPOT(pt1->x - pt2->x, pt1->y - pt2->y));
+ PG_RETURN_FLOAT8(point_dt(pt1, pt2));
}
-double
+static double
point_dt(Point *pt1, Point *pt2)
{
+ double xdiff,
+ ydiff,
+ result;
+
+ FLOAT_MI(xdiff, pt1->x, pt2->x);
+ FLOAT_MI(ydiff, pt1->y, pt2->y);
+ FLOAT_HYPOT(result, xdiff, ydiff);
+
#ifdef GEODEBUG
printf("point_dt- segment (%f,%f),(%f,%f) length is %f\n",
- pt1->x, pt1->y, pt2->x, pt2->y, HYPOT(pt1->x - pt2->x, pt1->y - pt2->y));
+ pt1->x, pt1->y, pt2->x, pt2->y, result);
#endif
- return HYPOT(pt1->x - pt2->x, pt1->y - pt2->y);
+
+ return result;
}
Datum
point_slope(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_FLOAT8(point_sl(pt1, pt2));
+ PG_RETURN_FLOAT8(slope(pt1->x, pt2->x, pt1->y, pt2->y));
}
double
-point_sl(Point *pt1, Point *pt2)
+slope(double x1, double x2, double y1, double y2)
{
- return (FPeq(pt1->x, pt2->x)
- ? (double) DBL_MAX
- : (pt1->y - pt2->y) / (pt1->x - pt2->x));
+ double xdiff;
+ double ydiff;
+ double result;
+
+ if (FLOAT_EQ(x1, x2))
+ return DBL_MAX;
+
+ FLOAT_MI(xdiff, x1, x2);
+ FLOAT_MI(ydiff, y1, y2);
+ FLOAT_DIV(result, ydiff, xdiff);
+
+ return result;
}
/***********************************************************************
**
** Routines for 2D line segments.
**
***********************************************************************/
/*----------------------------------------------------------
@@ -2002,24 +2104,21 @@ lseg_send(PG_FUNCTION_ARGS)
/* lseg_construct -
* form a LSEG from two Points.
*/
Datum
lseg_construct(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
LSEG *result = (LSEG *) palloc(sizeof(LSEG));
- result->p[0].x = pt1->x;
- result->p[0].y = pt1->y;
- result->p[1].x = pt2->x;
- result->p[1].y = pt2->y;
+ statlseg_construct(result, pt1, pt2);
PG_RETURN_LSEG_P(result);
}
/* like lseg_construct, but assume space already allocated */
static void
statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
{
lseg->p[0].x = pt1->x;
lseg->p[0].y = pt1->y;
@@ -2048,154 +2147,146 @@ lseg_intersect(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
PG_RETURN_BOOL(lseg_intersect_internal(l1, l2));
}
static bool
lseg_intersect_internal(LSEG *l1, LSEG *l2)
{
- LINE ln;
Point *interpt;
- bool retval;
- line_construct_pts(&ln, &l2->p[0], &l2->p[1]);
- interpt = interpt_sl(l1, &ln);
+ interpt = line_interpt_internal(line_construct_pts(&l1->p[0], &l1->p[1]),
+ line_construct_pts(&l2->p[0], &l2->p[1]));
- if (interpt != NULL && on_ps_internal(interpt, l2))
- retval = true; /* interpt on l1 and l2 */
- else
- retval = false;
- return retval;
+ return (interpt != NULL &&
+ point_lseg_cmp(interpt, l1) == 0 &&
+ point_lseg_cmp(interpt, l2) == 0);
}
Datum
lseg_parallel(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(point_sl(&l1->p[0], &l1->p[1]),
- point_sl(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_EQ(slope(l1->p[0].x, l1->p[1].x, l1->p[0].y, l1->p[1].y),
+ slope(l2->p[0].x, l2->p[1].x, l2->p[0].y, l2->p[1].y)));
}
/* lseg_perp()
* Determine if two line segments are perpendicular.
*
* This code did not get the correct answer for
* '((0,0),(0,1))'::lseg ?-| '((0,0),(1,0))'::lseg
* So, modified it to check explicitly for slope of vertical line
- * returned by point_sl() and the results seem better.
+ * returned by slope() and the results seem better.
* - thomas 1998-01-31
*/
Datum
lseg_perp(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
double m1,
m2;
- m1 = point_sl(&(l1->p[0]), &(l1->p[1]));
- m2 = point_sl(&(l2->p[0]), &(l2->p[1]));
+ m1 = slope(l1->p[0].x, l1->p[1].x, l1->p[0].y, l1->p[1].y);
+ m2 = slope(l2->p[0].x, l2->p[1].x, l2->p[0].y, l2->p[1].y);
#ifdef GEODEBUG
printf("lseg_perp- slopes are %g and %g\n", m1, m2);
#endif
- if (FPzero(m1))
- PG_RETURN_BOOL(FPeq(m2, DBL_MAX));
- else if (FPzero(m2))
- PG_RETURN_BOOL(FPeq(m1, DBL_MAX));
- PG_RETURN_BOOL(FPeq(m1 / m2, -1.0));
+ PG_RETURN_BOOL(FLOAT_EQ(m1, -m2));
}
Datum
lseg_vertical(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
+ PG_RETURN_BOOL(FLOAT_EQ(lseg->p[0].x, lseg->p[1].x));
}
Datum
lseg_horizontal(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
+ PG_RETURN_BOOL(FLOAT_EQ(lseg->p[0].y, lseg->p[1].y));
}
Datum
lseg_eq(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(l1->p[0].x, l2->p[0].x) &&
- FPeq(l1->p[0].y, l2->p[0].y) &&
- FPeq(l1->p[1].x, l2->p[1].x) &&
- FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(FLOAT_EQ(l1->p[0].x, l2->p[0].x) &&
+ FLOAT_EQ(l1->p[0].y, l2->p[0].y) &&
+ FLOAT_EQ(l1->p[1].x, l2->p[1].x) &&
+ FLOAT_EQ(l1->p[1].y, l2->p[1].y));
}
Datum
lseg_ne(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(!FPeq(l1->p[0].x, l2->p[0].x) ||
- !FPeq(l1->p[0].y, l2->p[0].y) ||
- !FPeq(l1->p[1].x, l2->p[1].x) ||
- !FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(FLOAT_NE(l1->p[0].x, l2->p[0].x) ||
+ FLOAT_NE(l1->p[0].y, l2->p[0].y) ||
+ FLOAT_NE(l1->p[1].x, l2->p[1].x) ||
+ FLOAT_NE(l1->p[1].y, l2->p[1].y));
}
Datum
lseg_lt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_LT(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_le(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_gt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_GT(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_ge(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(FLOAT_GE(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* lseg_distance -
* If two segments don't intersect, then the closest
* point will be from one of the endpoints to the other
@@ -2217,92 +2308,78 @@ lseg_distance(PG_FUNCTION_ARGS)
*/
static double
lseg_dt(LSEG *l1, LSEG *l2)
{
double result,
d;
if (lseg_intersect_internal(l1, l2))
return 0.0;
- d = dist_ps_internal(&l1->p[0], l2);
- result = d;
+ result = dist_ps_internal(&l1->p[0], l2);
d = dist_ps_internal(&l1->p[1], l2);
- result = Min(result, d);
+ if (FLOAT_LT(d, result))
+ result = d;
d = dist_ps_internal(&l2->p[0], l1);
- result = Min(result, d);
+ if (FLOAT_LT(d, result))
+ result = d;
d = dist_ps_internal(&l2->p[1], l1);
- result = Min(result, d);
+ if (FLOAT_LT(d, result))
+ result = d;
return result;
}
Datum
lseg_center(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- Point *result;
- result = (Point *) palloc(sizeof(Point));
+ PG_RETURN_POINT_P(lseg_center_internal(lseg));
+}
- result->x = (lseg->p[0].x + lseg->p[1].x) / 2.0;
- result->y = (lseg->p[0].y + lseg->p[1].y) / 2.0;
+static Point *
+lseg_center_internal(LSEG *lseg)
+{
+ double xsum;
+ double ysum;
+ double x;
+ double y;
- PG_RETURN_POINT_P(result);
+ FLOAT_PL(xsum, lseg->p[0].x, lseg->p[1].x);
+ FLOAT_PL(ysum, lseg->p[0].y, lseg->p[1].y);
+ FLOAT_DIV(x, xsum, 2.0);
+ FLOAT_DIV(y, ysum, 2.0);
+
+ return point_construct(x, y);
}
static Point *
lseg_interpt_internal(LSEG *l1, LSEG *l2)
{
Point *result;
- LINE tmp1,
- tmp2;
- /*
- * Find the intersection of the appropriate lines, if any.
- */
- line_construct_pts(&tmp1, &l1->p[0], &l1->p[1]);
- line_construct_pts(&tmp2, &l2->p[0], &l2->p[1]);
- result = line_interpt_internal(&tmp1, &tmp2);
+ /* Find the intersection of the appropriate lines, if any */
+ result = line_interpt_internal(line_construct_pts(&l1->p[0], &l1->p[1]),
+ line_construct_pts(&l2->p[0], &l2->p[1]));
if (!PointerIsValid(result))
return NULL;
- /*
- * If the line intersection point isn't within l1 (or equivalently l2),
- * there is no valid segment intersection point at all.
- */
- if (!on_ps_internal(result, l1) ||
- !on_ps_internal(result, l2))
+ /* Test if the point extend the segments of the lines */
+ if (point_lseg_cmp(result, l1) != 0 ||
+ point_lseg_cmp(result, l2) != 0)
{
pfree(result);
return NULL;
}
- /*
- * If there is an intersection, then check explicitly for matching
- * endpoints since there may be rounding effects with annoying lsb
- * residue. - tgl 1997-07-09
- */
- if ((FPeq(l1->p[0].x, l2->p[0].x) && FPeq(l1->p[0].y, l2->p[0].y)) ||
- (FPeq(l1->p[0].x, l2->p[1].x) && FPeq(l1->p[0].y, l2->p[1].y)))
- {
- result->x = l1->p[0].x;
- result->y = l1->p[0].y;
- }
- else if ((FPeq(l1->p[1].x, l2->p[0].x) && FPeq(l1->p[1].y, l2->p[0].y)) ||
- (FPeq(l1->p[1].x, l2->p[1].x) && FPeq(l1->p[1].y, l2->p[1].y)))
- {
- result->x = l1->p[1].x;
- result->y = l1->p[1].y;
- }
-
return result;
}
/* lseg_interpt -
* Find the intersection point of two segments (if any).
*/
Datum
lseg_interpt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
@@ -2336,22 +2413,29 @@ dist_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
PG_RETURN_FLOAT8(dist_pl_internal(pt, line));
}
static double
dist_pl_internal(Point *pt, LINE *line)
{
- return fabs((line->A * pt->x + line->B * pt->y + line->C) /
- HYPOT(line->A, line->B));
+ double val;
+ double hyp;
+ double result;
+
+ val = point_on_line_value(pt, line);
+ FLOAT_HYPOT(hyp, line->A, line->B);
+ FLOAT_DIV(result, val, hyp);
+
+ return fabs(result);
}
/*
* Distance from a point to a lseg
*/
Datum
dist_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
@@ -2365,69 +2449,65 @@ dist_ps_internal(Point *pt, LSEG *lseg)
double m; /* slope of perp. */
LINE *ln;
double result,
tmpdist;
Point *ip;
/*
* Construct a line perpendicular to the input segment and through the
* input point
*/
- if (lseg->p[1].x == lseg->p[0].x)
- m = 0;
- else if (lseg->p[1].y == lseg->p[0].y)
- m = (double) DBL_MAX; /* slope is infinite */
- else
- m = (lseg->p[0].x - lseg->p[1].x) / (lseg->p[1].y - lseg->p[0].y);
+ m = slope(lseg->p[1].y, lseg->p[0].y, lseg->p[0].x, lseg->p[1].x);
ln = line_construct_pm(pt, m);
#ifdef GEODEBUG
printf("dist_ps- line is A=%g B=%g C=%g from (point) slope (%f,%f) %g\n",
ln->A, ln->B, ln->C, pt->x, pt->y, m);
#endif
/*
* Calculate distance to the line segment or to the nearest endpoint of
* the segment.
*/
/* intersection is on the line segment? */
- if ((ip = interpt_sl(lseg, ln)) != NULL)
+ ip = interpt_sl(lseg, ln);
+ if (ip != NULL)
{
/* yes, so use distance to the intersection point */
result = point_dt(pt, ip);
#ifdef GEODEBUG
printf("dist_ps- distance is %f to intersection point is (%f,%f)\n",
result, ip->x, ip->y);
#endif
}
else
{
/* no, so use distance to the nearer endpoint */
result = point_dt(pt, &lseg->p[0]);
tmpdist = point_dt(pt, &lseg->p[1]);
- if (tmpdist < result)
+ if (FLOAT_LT(tmpdist, result))
result = tmpdist;
}
return result;
}
/*
* Distance from a point to a path
*/
Datum
dist_ppath(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
PATH *path = PG_GETARG_PATH_P(1);
- float8 result = 0.0; /* keep compiler quiet */
+ float8 result;
bool have_min = false;
float8 tmp;
int i;
LSEG lseg;
switch (path->npts)
{
case 0:
/* no points in path? then result is undefined... */
PG_RETURN_NULL();
@@ -2436,36 +2516,37 @@ dist_ppath(PG_FUNCTION_ARGS)
result = point_dt(pt, &path->p[0]);
break;
default:
/* make sure the path makes sense... */
Assert(path->npts > 1);
/*
* the distance from a point to a path is the smallest distance
* from the point to any of its constituent segments.
*/
+ result = 0.0;
for (i = 0; i < path->npts; i++)
{
int iprev;
if (i > 0)
iprev = i - 1;
else
{
if (!path->closed)
continue;
iprev = path->npts - 1; /* include the closure segment */
}
statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
tmp = dist_ps_internal(pt, &lseg);
- if (!have_min || tmp < result)
+ if (!have_min || FLOAT_LT(tmp, result))
{
result = tmp;
have_min = true;
}
}
break;
}
PG_RETURN_FLOAT8(result);
}
@@ -2498,22 +2579,21 @@ dist_sl(PG_FUNCTION_ARGS)
LINE *line = PG_GETARG_LINE_P(1);
float8 result,
d2;
if (has_interpt_sl(lseg, line))
result = 0.0;
else
{
result = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- /* XXX shouldn't we take the min not max? */
- if (d2 > result)
+ if (FLOAT_LT(d2, result))
result = d2;
}
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a lseg to a box
*/
Datum
@@ -2554,28 +2634,28 @@ dist_lb(PG_FUNCTION_ARGS)
}
/*
* Distance from a circle to a polygon
*/
Datum
dist_cpoly(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
POLYGON *poly = PG_GETARG_POLYGON_P(1);
+ float8 dist;
float8 result;
/* calculate distance to center, and subtract radius */
- result = dist_ppoly_internal(&circle->center, poly);
-
- result -= circle->radius;
- if (result < 0)
- result = 0;
+ dist = dist_ppoly_internal(&circle->center, poly);
+ FLOAT_MI(result, dist, circle->radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a point to a polygon
*/
Datum
dist_ppoly(PG_FUNCTION_ARGS)
{
@@ -2630,56 +2710,54 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
for (i = 0; (i < poly->npts - 1); i++)
{
seg.p[0].x = poly->p[i].x;
seg.p[0].y = poly->p[i].y;
seg.p[1].x = poly->p[i + 1].x;
seg.p[1].y = poly->p[i + 1].y;
d = dist_ps_internal(pt, &seg);
#ifdef GEODEBUG
printf("dist_ppoly_internal- segment %d distance is %f\n", (i + 1), d);
#endif
- if (d < result)
+ if (FLOAT_LT(d, result))
result = d;
}
return result;
}
/*---------------------------------------------------------------------
* interpt_
* Intersection point of objects.
* We choose to ignore the "point" of intersection between
* lines and boxes, since there are typically two.
*-------------------------------------------------------------------*/
/* Get intersection point of lseg and line; returns NULL if no intersection */
static Point *
interpt_sl(LSEG *lseg, LINE *line)
{
- LINE tmp;
Point *p;
- line_construct_pts(&tmp, &lseg->p[0], &lseg->p[1]);
- p = line_interpt_internal(&tmp, line);
+ line_construct_pts(&lseg->p[0], &lseg->p[1]);
+ p = line_interpt_internal(line_construct_pts(&lseg->p[0], &lseg->p[1]),
+ line);
#ifdef GEODEBUG
printf("interpt_sl- segment is (%.*g %.*g) (%.*g %.*g)\n",
DBL_DIG, lseg->p[0].x, DBL_DIG, lseg->p[0].y, DBL_DIG, lseg->p[1].x, DBL_DIG, lseg->p[1].y);
- printf("interpt_sl- segment becomes line A=%.*g B=%.*g C=%.*g\n",
- DBL_DIG, tmp.A, DBL_DIG, tmp.B, DBL_DIG, tmp.C);
#endif
if (PointerIsValid(p))
{
#ifdef GEODEBUG
printf("interpt_sl- intersection point is (%.*g %.*g)\n", DBL_DIG, p->x, DBL_DIG, p->y);
#endif
- if (on_ps_internal(p, lseg))
+ if (point_lseg_cmp(p, lseg) == 0)
{
#ifdef GEODEBUG
printf("interpt_sl- intersection point is on segment\n");
#endif
}
else
p = NULL;
}
return p;
@@ -2704,174 +2782,81 @@ has_interpt_sl(LSEG *lseg, LINE *line)
/* close_pl -
* The intersection point of a perpendicular of the line
* through the point.
*/
Datum
close_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
+
+ PG_RETURN_POINT_P(close_pl_internal(pt, line));
+}
+
+static Point *
+close_pl_internal(Point *pt, LINE *line)
+{
Point *result;
- LINE *tmp;
- double invm;
+ double val;
- result = (Point *) palloc(sizeof(Point));
-
- if (FPzero(line->B)) /* vertical? */
+ if (FLOAT_EQ(line->B, 0.0)) /* vertical? */
{
- result->x = line->C;
- result->y = pt->y;
- PG_RETURN_POINT_P(result);
+ FLOAT_DIV(val, -line->C, line->A);
+ result = point_construct(val, pt->y);
}
- if (FPzero(line->A)) /* horizontal? */
+ else if (FLOAT_EQ(line->A, 0.0)) /* horizontal? */
{
- result->x = pt->x;
- result->y = line->C;
- PG_RETURN_POINT_P(result);
+ FLOAT_DIV(val, -line->C, line->B);
+ result = point_construct(val, pt->y);
+ }
+ else
+ {
+ /*
+ * Drop a perpendicular and find the intersection point
+ *
+ * We need to invert and flip the sign on the slope to get
+ * the perpendicular. line_interpt_internal() returns NULL,
+ * when the lines are parallel. We might lose some precision
+ * on the division, but it cannot be as much to turn the line.
+ */
+ FLOAT_DIV(val, line->B, line->A);
+ result = line_interpt_internal(line_construct_pm(pt, val), line);
+ Assert(result != NULL);
}
- /* drop a perpendicular and find the intersection point */
- /* invert and flip the sign on the slope to get a perpendicular */
- invm = line->B / line->A;
- tmp = line_construct_pm(pt, invm);
- result = line_interpt_internal(tmp, line);
- Assert(result != NULL);
- PG_RETURN_POINT_P(result);
+ return result;
}
-/* close_ps()
- * Closest point on line segment to specified point.
- * Take the closest endpoint if the point is left, right,
- * above, or below the segment, otherwise find the intersection
- * point of the segment and its perpendicular through the point.
+/*
+ * Closest point on line segment to specified point
*
- * Some tricky code here, relying on boolean expressions
- * evaluating to only zero or one to use as an array index.
- * bug fixes by gthaker@atl.lmco.com; May 1, 1998
+ * We find the closest point to the line, and then test if the point is
+ * on the line segment. If it is not, we return the closest endpoint
+ * of the line segment.
*/
Datum
close_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
- Point *result = NULL;
- LINE *tmp;
- double invm;
- int xh,
- yh;
+ Point *result;
+ int order;
-#ifdef GEODEBUG
- printf("close_sp:pt->x %f pt->y %f\nlseg(0).x %f lseg(0).y %f lseg(1).x %f lseg(1).y %f\n",
- pt->x, pt->y, lseg->p[0].x, lseg->p[0].y,
- lseg->p[1].x, lseg->p[1].y);
-#endif
+ result = close_pl_internal(pt, line_construct_pts(&lseg->p[0], &lseg->p[1]));
+ order = point_lseg_cmp(result, lseg);
- /* xh (or yh) is the index of upper x( or y) end point of lseg */
- /* !xh (or !yh) is the index of lower x( or y) end point of lseg */
- xh = lseg->p[0].x < lseg->p[1].x;
- yh = lseg->p[0].y < lseg->p[1].y;
-
- if (FPeq(lseg->p[0].x, lseg->p[1].x)) /* vertical? */
- {
-#ifdef GEODEBUG
- printf("close_ps- segment is vertical\n");
-#endif
- /* first check if point is below or above the entire lseg. */
- if (pt->y < lseg->p[!yh].y)
- result = point_copy(&lseg->p[!yh]); /* below the lseg */
- else if (pt->y > lseg->p[yh].y)
- result = point_copy(&lseg->p[yh]); /* above the lseg */
- if (result != NULL)
- PG_RETURN_POINT_P(result);
-
- /* point lines along (to left or right) of the vertical lseg. */
-
- result = (Point *) palloc(sizeof(Point));
- result->x = lseg->p[0].x;
- result->y = pt->y;
- PG_RETURN_POINT_P(result);
- }
- else if (FPeq(lseg->p[0].y, lseg->p[1].y)) /* horizontal? */
- {
-#ifdef GEODEBUG
- printf("close_ps- segment is horizontal\n");
-#endif
- /* first check if point is left or right of the entire lseg. */
- if (pt->x < lseg->p[!xh].x)
- result = point_copy(&lseg->p[!xh]); /* left of the lseg */
- else if (pt->x > lseg->p[xh].x)
- result = point_copy(&lseg->p[xh]); /* right of the lseg */
- if (result != NULL)
- PG_RETURN_POINT_P(result);
-
- /* point lines along (at top or below) the horiz. lseg. */
- result = (Point *) palloc(sizeof(Point));
- result->x = pt->x;
- result->y = lseg->p[0].y;
- PG_RETURN_POINT_P(result);
- }
-
- /*
- * vert. and horiz. cases are down, now check if the closest point is one
- * of the end points or someplace on the lseg.
- */
-
- invm = -1.0 / point_sl(&(lseg->p[0]), &(lseg->p[1]));
- tmp = line_construct_pm(&lseg->p[!yh], invm); /* lower edge of the
- * "band" */
- if (pt->y < (tmp->A * pt->x + tmp->C))
- { /* we are below the lower edge */
- result = point_copy(&lseg->p[!yh]); /* below the lseg, take lower
- * end pt */
-#ifdef GEODEBUG
- printf("close_ps below: tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- PG_RETURN_POINT_P(result);
- }
- tmp = line_construct_pm(&lseg->p[yh], invm); /* upper edge of the
- * "band" */
- if (pt->y > (tmp->A * pt->x + tmp->C))
- { /* we are below the lower edge */
- result = point_copy(&lseg->p[yh]); /* above the lseg, take higher
- * end pt */
-#ifdef GEODEBUG
- printf("close_ps above: tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- PG_RETURN_POINT_P(result);
- }
-
- /*
- * at this point the "normal" from point will hit lseg. The closest point
- * will be somewhere on the lseg
- */
- tmp = line_construct_pm(pt, invm);
-#ifdef GEODEBUG
- printf("close_ps- tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- result = interpt_sl(lseg, tmp);
-
- /*
- * ordinarily we should always find an intersection point, but that could
- * fail in the presence of NaN coordinates, and perhaps even from simple
- * roundoff issues. Return a SQL NULL if so.
- */
- if (result == NULL)
- PG_RETURN_NULL();
-
-#ifdef GEODEBUG
- printf("close_ps- result.x %f result.y %f\n", result->x, result->y);
-#endif
+ if (order < 0)
+ PG_RETURN_POINT_P(point_copy(&lseg->p[0]));
+ if (order > 0)
+ PG_RETURN_POINT_P(point_copy(&lseg->p[1]));
PG_RETURN_POINT_P(result);
}
/* close_lseg()
* Closest point to l1 on l2.
*/
Datum
close_lseg(PG_FUNCTION_ARGS)
{
@@ -2879,38 +2864,39 @@ close_lseg(PG_FUNCTION_ARGS)
LSEG *l2 = PG_GETARG_LSEG_P(1);
Point *result = NULL;
Point point;
double dist;
double d;
d = dist_ps_internal(&l1->p[0], l2);
dist = d;
memcpy(&point, &l1->p[0], sizeof(Point));
- if ((d = dist_ps_internal(&l1->p[1], l2)) < dist)
+ d = dist_ps_internal(&l1->p[1], l2);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&point, &l1->p[1], sizeof(Point));
}
- if (dist_ps_internal(&l2->p[0], l1) < dist)
+ if (FLOAT_LT(dist_ps_internal(&l2->p[0], l1), dist))
{
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&l2->p[0]),
LsegPGetDatum(l1)));
memcpy(&point, result, sizeof(Point));
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&point),
LsegPGetDatum(l2)));
}
- if (dist_ps_internal(&l2->p[1], l1) < dist)
+ if (FLOAT_LT(dist_ps_internal(&l2->p[1], l1), dist))
{
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&l2->p[1]),
LsegPGetDatum(l1)));
memcpy(&point, result, sizeof(Point));
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&point),
LsegPGetDatum(l2)));
}
@@ -2939,37 +2925,40 @@ close_pb(PG_FUNCTION_ARGS)
BoxPGetDatum(box))))
PG_RETURN_POINT_P(pt);
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&lseg, &box->low, &point);
dist = dist_ps_internal(pt, &lseg);
statlseg_construct(&seg, &box->high, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&seg, &box->low, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
statlseg_construct(&seg, &box->high, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
PG_RETURN_DATUM(DirectFunctionCall2(close_ps,
PointPGetDatum(pt),
LsegPGetDatum(&lseg)));
}
@@ -2991,21 +2980,21 @@ close_sl(PG_FUNCTION_ARGS)
Point *result;
float8 d1,
d2;
result = interpt_sl(lseg, line);
if (result)
PG_RETURN_POINT_P(result);
d1 = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- if (d1 < d2)
+ if (FLOAT_LT(d1, d2))
result = point_copy(&lseg->p[0]);
else
result = point_copy(&lseg->p[1]);
PG_RETURN_POINT_P(result);
#endif
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function \"close_sl\" not implemented")));
@@ -3024,21 +3013,21 @@ close_ls(PG_FUNCTION_ARGS)
Point *result;
float8 d1,
d2;
result = interpt_sl(lseg, line);
if (result)
PG_RETURN_POINT_P(result);
d1 = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- if (d1 < d2)
+ if (FLOAT_LT(d1, d2))
result = point_copy(&lseg->p[0]);
else
result = point_copy(&lseg->p[1]);
PG_RETURN_POINT_P(result);
}
/* close_sb()
* Closest point on or in box to line segment.
*/
@@ -3064,37 +3053,40 @@ close_sb(PG_FUNCTION_ARGS)
LsegPGetDatum(lseg)));
}
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&bseg, &box->low, &point);
dist = lseg_dt(lseg, &bseg);
statlseg_construct(&seg, &box->high, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (FLOAT_LT(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&seg, &box->low, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (FLOAT_LE(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
statlseg_construct(&seg, &box->high, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (FLOAT_LE(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
/* OK, we now have the closest line segment on the box boundary */
PG_RETURN_DATUM(DirectFunctionCall2(close_lseg,
LsegPGetDatum(lseg),
LsegPGetDatum(&bseg)));
}
@@ -3113,71 +3105,152 @@ close_lb(PG_FUNCTION_ARGS)
errmsg("function \"close_lb\" not implemented")));
PG_RETURN_NULL();
}
/*---------------------------------------------------------------------
* on_
* Whether one object lies completely within another.
*-------------------------------------------------------------------*/
-/* on_pl -
- * Does the point satisfy the equation?
+/*
+ * Test if the point satisfies the line equation
*/
Datum
on_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
- PG_RETURN_BOOL(FPzero(line->A * pt->x + line->B * pt->y + line->C));
+ PG_RETURN_BOOL(point_on_line_value(pt, line) == 0);
}
-
-/* on_ps -
- * Determine colinearity by detecting a triangle inequality.
- * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
- */
Datum
on_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
PG_RETURN_BOOL(on_ps_internal(pt, lseg));
}
+/*
+ * Test if the point is on the line segment
+ *
+ * We are first comparing the point with the line segment, even though
+ * this comparison makes no sense when the point is not on the line.
+ * The reason of it is that this check is cheaper than constructing
+ * the line and validating the line equation.
+ */
static bool
on_ps_internal(Point *pt, LSEG *lseg)
{
- return FPeq(point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1]),
- point_dt(&lseg->p[0], &lseg->p[1]));
+ return (point_lseg_cmp(pt, lseg) == 0 &&
+ FLOAT_EQ(point_on_line_value(pt,
+ line_construct_pts(&lseg->p[0], &lseg->p[1])),
+ 0.0));
+}
+
+/*
+ * Calculate the line equation for a point
+ *
+ * This returns the result of the line equation Ax + By + C. The result
+ * needs to be 0 for the point to be on the line.
+ */
+static double
+point_on_line_value(Point *pt, LINE *line)
+{
+ double mulx;
+ double muly;
+ double sumxy;
+ double result;
+
+ FLOAT_MUL(mulx, line->A, pt->x);
+ FLOAT_MUL(muly, line->B, pt->y);
+ FLOAT_PL(sumxy, mulx, muly);
+ FLOAT_PL(result, sumxy, line->C);
+
+ return result;
+}
+
+/*
+ * Compare a point with a line segment
+ *
+ * This function is useful when the point and line segment are on
+ * the same line. It returns
+ *
+ * * -1, if the point extends the first edge of the segment
+ * * 0, if the point is on the segment
+ * * 1, if the point extends the last edge of the segment.
+ *
+ * This check is not as trivial as checking if a point is inside a box,
+ * because the edges of the line segments are not normalised. We are
+ * doing the same checks for both x and y coordinates, to be correct
+ * when one of the coordinates of the point is on one edge of the segment.
+ */
+static int
+point_lseg_cmp(Point *pt, LSEG *lseg)
+{
+ if (FLOAT_LT(lseg->p[0].x, lseg->p[1].x))
+ {
+ if (FLOAT_LT(pt->x, lseg->p[0].x))
+ return -1;
+ if (FLOAT_GT(pt->x, lseg->p[1].x))
+ return 1;
+ }
+ else if (FLOAT_GT(lseg->p[0].x, lseg->p[1].x))
+ {
+ if (FLOAT_GT(pt->x, lseg->p[0].x))
+ return -1;
+ if (FLOAT_LT(pt->x, lseg->p[1].x))
+ return 1;
+ }
+ else if (FLOAT_LE(lseg->p[0].y, lseg->p[1].y))
+ {
+ if (FLOAT_LT(pt->y, lseg->p[0].y))
+ return -1;
+ if (FLOAT_GT(pt->y, lseg->p[1].y))
+ return 1;
+ }
+ else
+ {
+ if (FLOAT_GT(pt->y, lseg->p[0].y))
+ return -1;
+ if (FLOAT_LT(pt->y, lseg->p[1].y))
+ return 1;
+ }
+
+ return 0;
}
Datum
on_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
BOX *box = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(pt->x <= box->high.x && pt->x >= box->low.x &&
- pt->y <= box->high.y && pt->y >= box->low.y);
+ PG_RETURN_BOOL(FLOAT_LE(pt->x, box->high.x) &&
+ FLOAT_GE(pt->x, box->low.x) &&
+ FLOAT_LE(pt->y, box->high.y) &&
+ FLOAT_GE(pt->y, box->low.y));
}
Datum
box_contain_pt(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *pt = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(pt->x <= box->high.x && pt->x >= box->low.x &&
- pt->y <= box->high.y && pt->y >= box->low.y);
+ PG_RETURN_BOOL(FLOAT_LE(pt->x, box->high.x) &&
+ FLOAT_GE(pt->x, box->low.x) &&
+ FLOAT_LE(pt->y, box->high.y) &&
+ FLOAT_GE(pt->y, box->low.y));
}
/* on_ppath -
* Whether a point lies within (on) a polyline.
* If open, we have to (groan) check each segment.
* (uses same algorithm as for point intersecting segment - tgl 1997-07-09)
* If closed, we use the old O(n) ray method for point-in-polygon.
* The ray is horizontal, from pt out to the right.
* Each segment that crosses the ray counts as an
* intersection; note that an endpoint or edge may touch
@@ -3195,22 +3268,21 @@ on_ppath(PG_FUNCTION_ARGS)
b;
/*-- OPEN --*/
if (!path->closed)
{
n = path->npts - 1;
a = point_dt(pt, &path->p[0]);
for (i = 0; i < n; i++)
{
b = point_dt(pt, &path->p[i + 1]);
- if (FPeq(a + b,
- point_dt(&path->p[i], &path->p[i + 1])))
+ if (FLOAT_EQ(a + b, point_dt(&path->p[i], &path->p[i + 1])))
PG_RETURN_BOOL(true);
a = b;
}
PG_RETURN_BOOL(false);
}
/*-- CLOSED --*/
PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
}
@@ -3268,24 +3340,24 @@ inter_sl(PG_FUNCTION_ARGS)
*/
Datum
inter_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
BOX *box = PG_GETARG_BOX_P(1);
BOX lbox;
LSEG bseg;
Point point;
- lbox.low.x = Min(lseg->p[0].x, lseg->p[1].x);
- lbox.low.y = Min(lseg->p[0].y, lseg->p[1].y);
- lbox.high.x = Max(lseg->p[0].x, lseg->p[1].x);
- lbox.high.y = Max(lseg->p[0].y, lseg->p[1].y);
+ lbox.low.x = FLOAT_MIN(lseg->p[0].x, lseg->p[1].x);
+ lbox.low.y = FLOAT_MIN(lseg->p[0].y, lseg->p[1].y);
+ lbox.high.x = FLOAT_MAX(lseg->p[0].x, lseg->p[1].x);
+ lbox.high.y = FLOAT_MAX(lseg->p[0].y, lseg->p[1].y);
/* nothing close to overlap? then not going to intersect */
if (!box_ov(&lbox, box))
PG_RETURN_BOOL(false);
/* an endpoint of segment is inside box? then clearly intersects */
if (DatumGetBool(DirectFunctionCall2(on_pb,
PointPGetDatum(&lseg->p[0]),
BoxPGetDatum(box))) ||
DatumGetBool(DirectFunctionCall2(on_pb,
@@ -3377,27 +3449,27 @@ make_bound_box(POLYGON *poly)
y1,
x2,
y2;
if (poly->npts > 0)
{
x2 = x1 = poly->p[0].x;
y2 = y1 = poly->p[0].y;
for (i = 1; i < poly->npts; i++)
{
- if (poly->p[i].x < x1)
+ if (FLOAT_LT(poly->p[i].x, x1))
x1 = poly->p[i].x;
- if (poly->p[i].x > x2)
+ if (FLOAT_GT(poly->p[i].x, x2))
x2 = poly->p[i].x;
- if (poly->p[i].y < y1)
+ if (FLOAT_LT(poly->p[i].y, y1))
y1 = poly->p[i].y;
- if (poly->p[i].y > y2)
+ if (FLOAT_GT(poly->p[i].y, y2))
y2 = poly->p[i].y;
}
box_fill(&(poly->boundbox), x1, x2, y1, y2);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot create bounding box for empty polygon")));
}
@@ -3420,21 +3492,21 @@ poly_in(PG_FUNCTION_ARGS)
int base_size;
bool isopen;
if ((npts = pair_count(str, ',')) <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"polygon", str)));
base_size = sizeof(poly->p[0]) * npts;
- size = offsetof(POLYGON, p) +base_size;
+ size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
@@ -3475,21 +3547,21 @@ poly_recv(PG_FUNCTION_ARGS)
int32 npts;
int32 i;
int size;
npts = pq_getmsgint(buf, sizeof(int32));
if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(POLYGON, p)) / sizeof(Point)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of points in external \"polygon\" value")));
- size = offsetof(POLYGON, p) +sizeof(poly->p[0]) * npts;
+ size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * npts;
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
for (i = 0; i < npts; i++)
{
poly->p[i].x = pq_getmsgfloat8(buf);
poly->p[i].y = pq_getmsgfloat8(buf);
}
@@ -3525,21 +3597,21 @@ poly_send(PG_FUNCTION_ARGS)
* the right most point of A left of the left most point
* of B?
*-------------------------------------------------------*/
Datum
poly_left(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.x < polyb->boundbox.low.x;
+ result = FLOAT_LT(polya->boundbox.high.x, polyb->boundbox.low.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3548,21 +3620,21 @@ poly_left(PG_FUNCTION_ARGS)
* the right most point of A at or left of the right most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overleft(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.x <= polyb->boundbox.high.x;
+ result = FLOAT_LE(polya->boundbox.high.x, polyb->boundbox.high.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3571,21 +3643,21 @@ poly_overleft(PG_FUNCTION_ARGS)
* the left most point of A right of the right most point
* of B?
*-------------------------------------------------------*/
Datum
poly_right(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.x > polyb->boundbox.high.x;
+ result = FLOAT_GT(polya->boundbox.low.x, polyb->boundbox.high.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3594,21 +3666,21 @@ poly_right(PG_FUNCTION_ARGS)
* the left most point of A at or right of the left most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overright(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.x >= polyb->boundbox.low.x;
+ result = FLOAT_GE(polya->boundbox.low.x, polyb->boundbox.low.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3617,21 +3689,21 @@ poly_overright(PG_FUNCTION_ARGS)
* the upper most point of A below the lower most point
* of B?
*-------------------------------------------------------*/
Datum
poly_below(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.y < polyb->boundbox.low.y;
+ result = FLOAT_LT(polya->boundbox.high.y, polyb->boundbox.low.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3640,21 +3712,21 @@ poly_below(PG_FUNCTION_ARGS)
* the upper most point of A at or below the upper most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overbelow(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.y <= polyb->boundbox.high.y;
+ result = FLOAT_LE(polya->boundbox.high.y, polyb->boundbox.high.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3663,21 +3735,21 @@ poly_overbelow(PG_FUNCTION_ARGS)
* the lower most point of A above the upper most point
* of B?
*-------------------------------------------------------*/
Datum
poly_above(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.y > polyb->boundbox.high.y;
+ result = FLOAT_GT(polya->boundbox.low.y, polyb->boundbox.high.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3686,21 +3758,21 @@ poly_above(PG_FUNCTION_ARGS)
* the lower most point of A at or above the lower most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overabove(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.y >= polyb->boundbox.low.y;
+ result = FLOAT_GE(polya->boundbox.low.y, polyb->boundbox.low.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3738,21 +3810,21 @@ poly_same(PG_FUNCTION_ARGS)
*-----------------------------------------------------------------*/
Datum
poly_overlap(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
/* Quick check by bounding box */
result = (polya->npts > 0 && polyb->npts > 0 &&
- box_ov(&polya->boundbox, &polyb->boundbox)) ? true : false;
+ box_ov(&polya->boundbox, &polyb->boundbox));
/*
* Brute-force algorithm - try to find intersected edges, if so then
* polygons are overlapped else check is one polygon inside other or not
* by testing single point of them.
*/
if (result)
{
int ia,
ib;
@@ -3779,22 +3851,21 @@ poly_overlap(PG_FUNCTION_ARGS)
}
/*
* move current endpoint to the first point of next edge
*/
sa.p[0] = sa.p[1];
}
if (result == false)
{
- result = (point_inside(polya->p, polyb->npts, polyb->p)
- ||
+ result = (point_inside(polya->p, polyb->npts, polyb->p) ||
point_inside(polyb->p, polya->npts, polya->p));
}
}
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
@@ -3814,21 +3885,21 @@ poly_overlap(PG_FUNCTION_ARGS)
static bool
touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
{
/* point a is on s, b is not */
LSEG t;
t.p[0] = *a;
t.p[1] = *b;
-#define POINTEQ(pt1, pt2) (FPeq((pt1)->x, (pt2)->x) && FPeq((pt1)->y, (pt2)->y))
+#define POINTEQ(pt1, pt2) (FLOAT_EQ((pt1)->x, (pt2)->x) && FLOAT_EQ((pt1)->y, (pt2)->y))
if (POINTEQ(a, s->p))
{
if (on_ps_internal(s->p + 1, &t))
return lseg_inside_poly(b, s->p + 1, poly, start);
}
else if (POINTEQ(a, s->p + 1))
{
if (on_ps_internal(s->p, &t))
return lseg_inside_poly(b, s->p, poly, start);
}
@@ -3894,30 +3965,25 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
if (res)
res = lseg_inside_poly(t.p + 1, interpt, poly, i + 1);
pfree(interpt);
}
s.p[0] = s.p[1];
}
if (res && !intersection)
{
- Point p;
-
/*
* if X-intersection wasn't found then check central point of tested
* segment. In opposite case we already check all subsegments
*/
- p.x = (t.p[0].x + t.p[1].x) / 2.0;
- p.y = (t.p[0].y + t.p[1].y) / 2.0;
-
- res = point_inside(&p, poly->npts, poly->p);
+ res = point_inside(lseg_center_internal(&t), poly->npts, poly->p);
}
return res;
}
/*-----------------------------------------------------------------
* Determine if polygon A contains polygon B.
*-----------------------------------------------------------------*/
Datum
poly_contain(PG_FUNCTION_ARGS)
@@ -4013,92 +4079,102 @@ poly_distance(PG_FUNCTION_ARGS)
/***********************************************************************
**
** Routines for 2D points.
**
***********************************************************************/
Datum
construct_point(PG_FUNCTION_ARGS)
{
- float8 x = PG_GETARG_FLOAT8(0);
- float8 y = PG_GETARG_FLOAT8(1);
+ double x = PG_GETARG_FLOAT8(0);
+ double y = PG_GETARG_FLOAT8(1);
PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_add(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_PL(x, p1->x, p2->x);
+ FLOAT_PL(y, p1->y, p2->y);
- result->x = (p1->x + p2->x);
- result->y = (p1->y + p2->y);
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_sub(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_MI(x, p1->x, p2->x);
+ FLOAT_MI(y, p1->y, p2->y);
- result->x = (p1->x - p2->x);
- result->y = (p1->y - p2->y);
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_mul(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double val1;
+ double val2;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_MUL(val1, p1->x, p2->x);
+ FLOAT_MUL(val2, p1->y, p2->y);
+ FLOAT_MI(x, val1, val2);
- result->x = (p1->x * p2->x) - (p1->y * p2->y);
- result->y = (p1->x * p2->y) + (p1->y * p2->x);
+ FLOAT_MUL(val1, p1->x, p2->y);
+ FLOAT_MUL(val2, p1->y, p2->x);
+ FLOAT_PL(y, val1, val2);
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
Datum
point_div(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
+ double mul1;
+ double mul2;
double div;
+ double val;
+ double x;
+ double y;
- result = (Point *) palloc(sizeof(Point));
+ FLOAT_MUL(mul1, p2->x, p2->x);
+ FLOAT_MUL(mul2, p2->y, p2->y);
+ FLOAT_PL(div, mul1, mul2);
- div = (p2->x * p2->x) + (p2->y * p2->y);
+ FLOAT_MUL(mul1, p1->x, p2->x);
+ FLOAT_MUL(mul2, p1->y, p2->y);
+ FLOAT_PL(val, mul1, mul2);
+ FLOAT_DIV(x, val, div);
- if (div == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
+ FLOAT_MUL(mul1, p1->y, p2->x);
+ FLOAT_MUL(mul2, p1->x, p2->y);
+ FLOAT_MI(val, mul1, mul2);
+ FLOAT_DIV(y, val, div);
- result->x = ((p1->x * p2->x) + (p1->y * p2->y)) / div;
- result->y = ((p2->x * p1->y) - (p2->y * p1->x)) / div;
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_construct(x, y));
}
/***********************************************************************
**
** Routines for 2D boxes.
**
***********************************************************************/
Datum
@@ -4108,37 +4184,49 @@ points_box(PG_FUNCTION_ARGS)
Point *p2 = PG_GETARG_POINT_P(1);
PG_RETURN_BOX_P(box_construct(p1->x, p2->x, p1->y, p2->y));
}
Datum
box_add(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
+ double highx;
+ double lowx;
+ double highy;
+ double lowy;
- PG_RETURN_BOX_P(box_construct((box->high.x + p->x),
- (box->low.x + p->x),
- (box->high.y + p->y),
- (box->low.y + p->y)));
+ FLOAT_PL(highx, box->high.x, p->x);
+ FLOAT_PL(lowx, box->low.x, p->x);
+ FLOAT_PL(highy, box->high.y, p->y);
+ FLOAT_PL(lowy, box->low.y, p->y);
+
+ PG_RETURN_BOX_P(box_construct(highx, lowx, highy, lowy));
}
Datum
box_sub(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
+ double highx;
+ double lowx;
+ double highy;
+ double lowy;
- PG_RETURN_BOX_P(box_construct((box->high.x - p->x),
- (box->low.x - p->x),
- (box->high.y - p->y),
- (box->low.y - p->y)));
+ FLOAT_MI(highx, box->high.x, p->x);
+ FLOAT_MI(lowx, box->low.x, p->x);
+ FLOAT_MI(highy, box->high.y, p->y);
+ FLOAT_MI(lowy, box->low.y, p->y);
+
+ PG_RETURN_BOX_P(box_construct(highx, lowx, highy, lowy));
}
Datum
box_mul(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
BOX *result;
Point *high,
*low;
@@ -4200,24 +4288,24 @@ point_box(PG_FUNCTION_ARGS)
*/
Datum
boxes_bound_box(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0),
*box2 = PG_GETARG_BOX_P(1),
*container;
container = (BOX *) palloc(sizeof(BOX));
- container->high.x = Max(box1->high.x, box2->high.x);
- container->low.x = Min(box1->low.x, box2->low.x);
- container->high.y = Max(box1->high.y, box2->high.y);
- container->low.y = Min(box1->low.y, box2->low.y);
+ container->high.x = FLOAT_MAX(box1->high.x, box2->high.x);
+ container->low.x = FLOAT_MIN(box1->low.x, box2->low.x);
+ container->high.y = FLOAT_MAX(box1->high.y, box2->high.y);
+ container->low.y = FLOAT_MIN(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(container);
}
/***********************************************************************
**
** Routines for 2D paths.
**
***********************************************************************/
@@ -4272,41 +4360,49 @@ path_add(PG_FUNCTION_ARGS)
/* path_add_pt()
* Translation operators.
*/
Datum
path_add_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
int i;
+ double val;
for (i = 0; i < path->npts; i++)
{
- path->p[i].x += point->x;
- path->p[i].y += point->y;
+ FLOAT_PL(val, path->p[i].x, point->x);
+ path->p[i].x = val;
+
+ FLOAT_PL(val, path->p[i].y, point->y);
+ path->p[i].y = val;
}
PG_RETURN_PATH_P(path);
}
Datum
path_sub_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
int i;
+ double val;
for (i = 0; i < path->npts; i++)
{
- path->p[i].x -= point->x;
- path->p[i].y -= point->y;
+ FLOAT_MI(val, path->p[i].x, point->x);
+ path->p[i].x = val;
+
+ FLOAT_MI(val, path->p[i].y, point->y);
+ path->p[i].y = val;
}
PG_RETURN_PATH_P(path);
}
/* path_mul_pt()
* Rotation and scaling operators.
*/
Datum
path_mul_pt(PG_FUNCTION_ARGS)
@@ -4374,21 +4470,21 @@ path_poly(PG_FUNCTION_ARGS)
/* This is not very consistent --- other similar cases return NULL ... */
if (!path->closed)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("open path cannot be converted to polygon")));
/*
* Never overflows: the old size fit in MaxAllocSize, and the new size is
* just a small constant larger.
*/
- size = offsetof(POLYGON, p) +sizeof(poly->p[0]) * path->npts;
+ size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * path->npts;
poly = (POLYGON *) palloc(size);
SET_VARSIZE(poly, size);
poly->npts = path->npts;
for (i = 0; i < path->npts; i++)
{
poly->p[i].x = path->p[i].x;
poly->p[i].y = path->p[i].y;
}
@@ -4483,26 +4579,26 @@ poly_path(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0);
PATH *path;
int size;
int i;
/*
* Never overflows: the old size fit in MaxAllocSize, and the new size is
* smaller by a small constant.
*/
- size = offsetof(PATH, p) +sizeof(path->p[0]) * poly->npts;
+ size = offsetof(PATH, p) + sizeof(path->p[0]) * poly->npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = poly->npts;
- path->closed = TRUE;
+ path->closed = true;
/* prevent instability in unused pad bytes */
path->dummy = 0;
for (i = 0; i < poly->npts; i++)
{
path->p[i].x = poly->p[i].x;
path->p[i].y = poly->p[i].y;
}
PG_RETURN_PATH_P(path);
@@ -4546,30 +4642,29 @@ circle_in(PG_FUNCTION_ARGS)
if (*cp == LDELIM)
s = cp;
}
pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle", str);
if (*s == DELIM)
s++;
circle->radius = single_decode(s, &s, "circle", str);
- if (circle->radius < 0)
+ if (FLOAT_LT(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"circle", str)));
while (depth > 0)
{
- if ((*s == RDELIM)
- || ((*s == RDELIM_C) && (depth == 1)))
+ if ((*s == RDELIM) || ((*s == RDELIM_C) && (depth == 1)))
{
depth--;
s++;
while (isspace((unsigned char) *s))
s++;
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
@@ -4614,21 +4709,21 @@ circle_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
CIRCLE *circle;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
circle->center.x = pq_getmsgfloat8(buf);
circle->center.y = pq_getmsgfloat8(buf);
circle->radius = pq_getmsgfloat8(buf);
- if (circle->radius < 0)
+ if (FLOAT_LT(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid radius in external \"circle\" value")));
PG_RETURN_CIRCLE_P(circle);
}
/*
* circle_send - converts circle to binary format
*/
@@ -4652,334 +4747,371 @@ circle_send(PG_FUNCTION_ARGS)
*---------------------------------------------------------*/
/* circles identical?
*/
Datum
circle_same(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle1->radius, circle2->radius) &&
- FPeq(circle1->center.x, circle2->center.x) &&
- FPeq(circle1->center.y, circle2->center.y));
+ PG_RETURN_BOOL(FLOAT_EQ(circle1->radius, circle2->radius) &&
+ FLOAT_EQ(circle1->center.x, circle2->center.x) &&
+ FLOAT_EQ(circle1->center.y, circle2->center.y));
}
/* circle_overlap - does circle1 overlap circle2?
*/
Datum
circle_overlap(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double sum_radius;
- PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
- circle1->radius + circle2->radius));
+ FLOAT_PL(sum_radius, circle1->radius, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&circle1->center, &circle2->center),
+ sum_radius));
}
/* circle_overleft - is the right edge of circle1 at or left of
* the right edge of circle2?
*/
Datum
circle_overleft(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPle((circle1->center.x + circle1->radius),
- (circle2->center.x + circle2->radius)));
+ FLOAT_PL(x1, circle1->center.x, circle1->radius);
+ FLOAT_PL(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(x1, x2));
}
/* circle_left - is circle1 strictly left of circle2?
*/
Datum
circle_left(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPlt((circle1->center.x + circle1->radius),
- (circle2->center.x - circle2->radius)));
+ FLOAT_PL(x1, circle1->center.x, circle1->radius);
+ FLOAT_MI(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LT(x1, x2));
}
/* circle_right - is circle1 strictly right of circle2?
*/
Datum
circle_right(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPgt((circle1->center.x - circle1->radius),
- (circle2->center.x + circle2->radius)));
+ FLOAT_MI(x1, circle1->center.x, circle1->radius);
+ FLOAT_PL(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GT(x1, x2));
}
/* circle_overright - is the left edge of circle1 at or right of
* the left edge of circle2?
*/
Datum
circle_overright(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double x1;
+ double x2;
- PG_RETURN_BOOL(FPge((circle1->center.x - circle1->radius),
- (circle2->center.x - circle2->radius)));
+ FLOAT_MI(x1, circle1->center.x, circle1->radius);
+ FLOAT_MI(x2, circle2->center.x, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GE(x1, x2));
}
/* circle_contained - is circle1 contained by circle2?
*/
Datum
circle_contained(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double radius_diff;
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle1->radius), circle2->radius));
+ FLOAT_MI(radius_diff, circle2->radius, circle1->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&circle1->center, &circle2->center),
+ radius_diff));
}
/* circle_contain - does circle1 contain circle2?
*/
Datum
circle_contain(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double radius_diff;
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle2->radius), circle1->radius));
+ FLOAT_MI(radius_diff, circle1->radius, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(point_dt(&circle1->center, &circle2->center),
+ radius_diff));
}
/* circle_below - is circle1 strictly below circle2?
*/
Datum
circle_below(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPlt((circle1->center.y + circle1->radius),
- (circle2->center.y - circle2->radius)));
+ FLOAT_PL(y1, circle1->center.y, circle1->radius);
+ FLOAT_MI(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LT(y1, y2));
}
/* circle_above - is circle1 strictly above circle2?
*/
Datum
circle_above(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPgt((circle1->center.y - circle1->radius),
- (circle2->center.y + circle2->radius)));
+ FLOAT_MI(y1, circle1->center.y, circle1->radius);
+ FLOAT_PL(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GT(y1, y2));
}
/* circle_overbelow - is the upper edge of circle1 at or below
* the upper edge of circle2?
*/
Datum
circle_overbelow(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPle((circle1->center.y + circle1->radius),
- (circle2->center.y + circle2->radius)));
+ FLOAT_PL(y1, circle1->center.y, circle1->radius);
+ FLOAT_PL(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_LE(y1, y2));
}
/* circle_overabove - is the lower edge of circle1 at or above
* the lower edge of circle2?
*/
Datum
circle_overabove(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
+ double y1;
+ double y2;
- PG_RETURN_BOOL(FPge((circle1->center.y - circle1->radius),
- (circle2->center.y - circle2->radius)));
+ FLOAT_MI(y1, circle1->center.y, circle1->radius);
+ FLOAT_MI(y2, circle2->center.y, circle2->radius);
+
+ PG_RETURN_BOOL(FLOAT_GE(y1, y2));
}
/* circle_relop - is area(circle1) relop area(circle2), within
* our accuracy constraint?
*/
Datum
circle_eq(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_EQ(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ne(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_NE(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_lt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_LT(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_gt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_GT(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_le(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_LE(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ge(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(FLOAT_GE(circle_ar(circle1), circle_ar(circle2)));
}
/*----------------------------------------------------------
* "Arithmetic" operators on circles.
*---------------------------------------------------------*/
static CIRCLE *
-circle_copy(CIRCLE *circle)
+circle_construct(double x, double y, double radius)
{
CIRCLE *result;
- if (!PointerIsValid(circle))
- return NULL;
-
result = (CIRCLE *) palloc(sizeof(CIRCLE));
- memcpy((char *) result, (char *) circle, sizeof(CIRCLE));
+ result->center.x = x;
+ result->center.y = y;
+ result->radius = radius;
+
return result;
}
/* circle_add_pt()
* Translation operator.
*/
Datum
circle_add_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
- result = circle_copy(circle);
-
- result->center.x += point->x;
- result->center.y += point->y;
-
- PG_RETURN_CIRCLE_P(result);
+ PG_RETURN_CIRCLE_P(circle_construct(point->x, point->y, circle->radius));
}
Datum
circle_sub_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
+ double x;
+ double y;
- result = circle_copy(circle);
+ FLOAT_MI(x, circle->center.x, point->x);
+ FLOAT_MI(y, circle->center.y, point->y);
- result->center.x -= point->x;
- result->center.y -= point->y;
-
- PG_RETURN_CIRCLE_P(result);
+ PG_RETURN_CIRCLE_P(circle_construct(x, y, circle->radius));
}
/* circle_mul_pt()
* Rotation and scaling operators.
*/
Datum
circle_mul_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
Point *p;
-
- result = circle_copy(circle);
+ double hyp;
+ double radius;
p = DatumGetPointP(DirectFunctionCall2(point_mul,
PointPGetDatum(&circle->center),
PointPGetDatum(point)));
- result->center.x = p->x;
- result->center.y = p->y;
- result->radius *= HYPOT(point->x, point->y);
- PG_RETURN_CIRCLE_P(result);
+ FLOAT_HYPOT(hyp, point->x, point->y);
+ FLOAT_MUL(radius, circle->radius, hyp);
+
+ PG_RETURN_CIRCLE_P(circle_construct(p->x, p->y, radius));
}
Datum
circle_div_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- CIRCLE *result;
Point *p;
-
- result = circle_copy(circle);
+ double hyp;
+ double radius;
p = DatumGetPointP(DirectFunctionCall2(point_div,
PointPGetDatum(&circle->center),
PointPGetDatum(point)));
- result->center.x = p->x;
- result->center.y = p->y;
- result->radius /= HYPOT(point->x, point->y);
- PG_RETURN_CIRCLE_P(result);
+ FLOAT_HYPOT(hyp, point->x, point->y);
+ FLOAT_DIV(radius, circle->radius, hyp);
+
+ PG_RETURN_CIRCLE_P(circle_construct(p->x, p->y, radius));
}
/* circle_area - returns the area of the circle.
*/
Datum
circle_area(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
PG_RETURN_FLOAT8(circle_ar(circle));
}
/* circle_diameter - returns the diameter of the circle.
*/
Datum
circle_diameter(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
+ double result;
- PG_RETURN_FLOAT8(2 * circle->radius);
+ FLOAT_MUL(result, circle->radius, 2.0);
+
+ PG_RETURN_FLOAT8(result);
}
/* circle_radius - returns the radius of the circle.
*/
Datum
circle_radius(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
@@ -4988,253 +5120,284 @@ circle_radius(PG_FUNCTION_ARGS)
/* circle_distance - returns the distance between
* two circles.
*/
Datum
circle_distance(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- float8 result;
+ double d;
+ double sum_radius;
+ double result;
+
+ d = point_dt(&circle1->center, &circle2->center);
+ FLOAT_PL(sum_radius, circle1->radius, circle2->radius);
+ FLOAT_MI(result, d, sum_radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
- result = point_dt(&circle1->center, &circle2->center)
- - (circle1->radius + circle2->radius);
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
Datum
circle_contain_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
double d;
d = point_dt(&circle->center, point);
- PG_RETURN_BOOL(d <= circle->radius);
+ PG_RETURN_BOOL(FLOAT_LE(d, circle->radius));
}
Datum
pt_contained_circle(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
double d;
d = point_dt(&circle->center, point);
- PG_RETURN_BOOL(d <= circle->radius);
+ PG_RETURN_BOOL(FLOAT_LE(d, circle->radius));
}
/* dist_pc - returns the distance between
* a point and a circle.
*/
Datum
dist_pc(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
- float8 result;
+ double d;
+ double result;
+
+ d = point_dt(point, &circle->center);
+ FLOAT_MI(result, d, circle->radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
- result = point_dt(point, &circle->center) - circle->radius;
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a circle to a point
*/
Datum
dist_cpoint(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- float8 result;
+ double d;
+ double result;
+
+ d = point_dt(point, &circle->center);
+ FLOAT_MI(result, d, circle->radius);
+ if (FLOAT_LT(result, 0.0))
+ result = 0.0;
- result = point_dt(point, &circle->center) - circle->radius;
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
/* circle_center - returns the center point of the circle.
*/
Datum
circle_center(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
- Point *result;
- result = (Point *) palloc(sizeof(Point));
- result->x = circle->center.x;
- result->y = circle->center.y;
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_copy(&circle->center));
}
/* circle_ar - returns the area of the circle.
*/
static double
circle_ar(CIRCLE *circle)
{
- return M_PI * (circle->radius * circle->radius);
+ double radius2;
+ double result;
+
+ FLOAT_MUL(radius2, circle->radius, circle->radius);
+ FLOAT_MUL(result, radius2, M_PI);
+
+ return result;
}
/*----------------------------------------------------------
* Conversion operators.
*---------------------------------------------------------*/
Datum
cr_circle(PG_FUNCTION_ARGS)
{
Point *center = PG_GETARG_POINT_P(0);
- float8 radius = PG_GETARG_FLOAT8(1);
- CIRCLE *result;
+ double radius = PG_GETARG_FLOAT8(1);
- result = (CIRCLE *) palloc(sizeof(CIRCLE));
-
- result->center.x = center->x;
- result->center.y = center->y;
- result->radius = radius;
-
- PG_RETURN_CIRCLE_P(result);
+ PG_RETURN_CIRCLE_P(circle_construct(center->x, center->y, radius));
}
Datum
circle_box(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
BOX *box;
double delta;
box = (BOX *) palloc(sizeof(BOX));
- delta = circle->radius / sqrt(2.0);
+ FLOAT_DIV(delta, circle->radius, sqrt(2.0));
- box->high.x = circle->center.x + delta;
- box->low.x = circle->center.x - delta;
- box->high.y = circle->center.y + delta;
- box->low.y = circle->center.y - delta;
+ FLOAT_PL(box->high.x, circle->center.x, delta);
+ FLOAT_MI(box->low.x, circle->center.x, delta);
+ FLOAT_PL(box->high.y, circle->center.y, delta);
+ FLOAT_MI(box->low.y, circle->center.y, delta);
PG_RETURN_BOX_P(box);
}
/* box_circle()
* Convert a box to a circle.
*/
Datum
box_circle(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
CIRCLE *circle;
+ double sum;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
- circle->center.x = (box->high.x + box->low.x) / 2;
- circle->center.y = (box->high.y + box->low.y) / 2;
+ FLOAT_PL(sum, box->high.x, box->low.x);
+ FLOAT_DIV(circle->center.x, sum, 2.0);
+
+ FLOAT_PL(sum, box->high.y, box->low.y);
+ FLOAT_DIV(circle->center.y, sum, 2.0);
circle->radius = point_dt(&circle->center, &box->high);
PG_RETURN_CIRCLE_P(circle);
}
Datum
circle_poly(PG_FUNCTION_ARGS)
{
int32 npts = PG_GETARG_INT32(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
POLYGON *poly;
int base_size,
size;
int i;
double angle;
double anglestep;
+ double mul;
- if (FPzero(circle->radius))
+ if (circle->radius == 0.0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert circle with radius zero to polygon")));
if (npts < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must request at least 2 points")));
base_size = sizeof(poly->p[0]) * npts;
- size = offsetof(POLYGON, p) +base_size;
+ size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
- anglestep = (2.0 * M_PI) / npts;
+ FLOAT_DIV(anglestep, 2.0 * M_PI, npts);
for (i = 0; i < npts; i++)
{
- angle = i * anglestep;
- poly->p[i].x = circle->center.x - (circle->radius * cos(angle));
- poly->p[i].y = circle->center.y + (circle->radius * sin(angle));
+ FLOAT_MUL(angle, anglestep, i);
+
+ FLOAT_MUL(mul, circle->radius, cos(angle));
+ FLOAT_MI(poly->p[i].x, circle->center.x, mul);
+
+ FLOAT_MUL(mul, circle->radius, sin(angle));
+ FLOAT_PL(poly->p[i].y, circle->center.y, mul);
}
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
}
/* poly_circle - convert polygon to circle
*
* XXX This algorithm should use weighted means of line segments
* rather than straight average values of points - tgl 97/01/21.
*/
Datum
poly_circle(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0);
CIRCLE *circle;
+ bool has_inf;
int i;
+ double sum;
+ double dist;
if (poly->npts < 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot convert empty polygon to circle")));
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
- circle->center.x = 0;
- circle->center.y = 0;
- circle->radius = 0;
-
+ has_inf = false;
+ sum = 0.0;
for (i = 0; i < poly->npts; i++)
{
- circle->center.x += poly->p[i].x;
- circle->center.y += poly->p[i].y;
+ has_inf |= isinf(poly->p[i].x);
+ sum += poly->p[i].x;
}
- circle->center.x /= poly->npts;
- circle->center.y /= poly->npts;
+ CHECKFLOATVAL(sum, has_inf, true);
+ FLOAT_DIV(circle->center.x, sum, poly->npts);
+ has_inf = false;
+ sum = 0.0;
for (i = 0; i < poly->npts; i++)
- circle->radius += point_dt(&poly->p[i], &circle->center);
- circle->radius /= poly->npts;
+ {
+ has_inf |= isinf(poly->p[i].y);
+ sum += poly->p[i].y;
+ }
+ CHECKFLOATVAL(sum, has_inf, true);
+ FLOAT_DIV(circle->center.y, sum, poly->npts);
+
+ has_inf = false;
+ sum = 0.0;
+ for (i = 0; i < poly->npts; i++)
+ {
+ dist = point_dt(&poly->p[i], &circle->center);
+ has_inf |= isinf(dist);
+ sum += dist;
+ }
+ CHECKFLOATVAL(sum, has_inf, true);
+ FLOAT_DIV(circle->radius, sum, poly->npts);
PG_RETURN_CIRCLE_P(circle);
}
/***********************************************************************
**
** Private routines for multiple types.
**
***********************************************************************/
@@ -5262,31 +5425,31 @@ point_inside(Point *p, int npts, Point *plist)
int i = 0;
double x,
y;
int cross,
total_cross = 0;
if (npts <= 0)
return 0;
/* compute first polygon point relative to single point */
- x0 = plist[0].x - p->x;
- y0 = plist[0].y - p->y;
+ FLOAT_MI(x0, plist[0].x, p->x);
+ FLOAT_MI(y0, plist[0].y, p->y);
prev_x = x0;
prev_y = y0;
/* loop over polygon points and aggregate total_cross */
for (i = 1; i < npts; i++)
{
/* compute next polygon point relative to single point */
- x = plist[i].x - p->x;
- y = plist[i].y - p->y;
+ FLOAT_MI(x, plist[i].x, p->x);
+ FLOAT_MI(y, plist[i].y, p->y);
/* compute previous to current point crossing */
if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
return 2;
total_cross += cross;
prev_x = x;
prev_y = y;
}
@@ -5306,129 +5469,139 @@ point_inside(Point *p, int npts, Point *plist)
* Returns +/-1 if one point is on the positive X-axis.
* Returns 0 if both points are on the positive X-axis, or there is no crossing.
* Returns POINT_ON_POLYGON if the segment contains (0,0).
* Wow, that is one confusing API, but it is used above, and when summed,
* can tell is if a point is in a polygon.
*/
static int
lseg_crossing(double x, double y, double prev_x, double prev_y)
{
- double z;
int y_sign;
+ double xdiff;
+ double ydiff;
+ double mulxy;
+ double mulyx;
- if (FPzero(y))
+ if (FLOAT_EQ(y, 0.0))
{ /* y == 0, on X axis */
- if (FPzero(x)) /* (x,y) is (0,0)? */
+ if (FLOAT_EQ(x, 0.0)) /* (x,y) is (0,0)? */
return POINT_ON_POLYGON;
- else if (FPgt(x, 0))
+ else if (FLOAT_GT(x, 0.0))
{ /* x > 0 */
- if (FPzero(prev_y)) /* y and prev_y are zero */
+ if (FLOAT_EQ(prev_y, 0.0)) /* y and prev_y are zero */
/* prev_x > 0? */
- return FPgt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
- return FPlt(prev_y, 0) ? 1 : -1;
+ return FLOAT_GT(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
+ return FLOAT_LT(prev_y, 0.0) ? 1 : -1;
}
else
{ /* x < 0, x not on positive X axis */
- if (FPzero(prev_y))
+ if (FLOAT_EQ(prev_y, 0.0))
/* prev_x < 0? */
- return FPlt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
+ return FLOAT_LT(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
return 0;
}
}
else
{ /* y != 0 */
/* compute y crossing direction from previous point */
- y_sign = FPgt(y, 0) ? 1 : -1;
+ y_sign = FLOAT_GT(y, 0.0) ? 1 : -1;
- if (FPzero(prev_y))
+ if (FLOAT_EQ(prev_y, 0.0))
/* previous point was on X axis, so new point is either off or on */
- return FPlt(prev_x, 0) ? 0 : y_sign;
- else if (FPgt(y_sign * prev_y, 0))
+ return prev_x < 0.0 ? 0 : y_sign;
+ else if ((y_sign < 0 && FLOAT_LT(prev_y, 0.0)) ||
+ (y_sign > 0 && FLOAT_GT(prev_y, 0.0)))
/* both above or below X axis */
return 0; /* same sign */
else
{ /* y and prev_y cross X-axis */
- if (FPge(x, 0) && FPgt(prev_x, 0))
+ if (FLOAT_GE(x, 0.0) && FLOAT_GT(prev_x, 0.0))
/* both non-negative so cross positive X-axis */
return 2 * y_sign;
- if (FPlt(x, 0) && FPle(prev_x, 0))
+ if (FLOAT_LT(x, 0.0) && FLOAT_LE(prev_x, 0.0))
/* both non-positive so do not cross positive X-axis */
return 0;
/* x and y cross axises, see URL above point_inside() */
- z = (x - prev_x) * y - (y - prev_y) * x;
- if (FPzero(z))
+ FLOAT_MI(xdiff, x, prev_x);
+ FLOAT_MI(ydiff, y, prev_y);
+ FLOAT_MUL(mulxy, xdiff, y);
+ FLOAT_MUL(mulyx, ydiff, x);
+ if (FLOAT_EQ(mulxy, mulyx))
return POINT_ON_POLYGON;
- return FPgt((y_sign * z), 0) ? 0 : 2 * y_sign;
+ if ((y_sign < 0 && FLOAT_LT(mulxy, mulyx)) ||
+ (y_sign > 0 && FLOAT_GT(mulxy, mulyx)))
+ return 0;
+
+ return 2 * y_sign;
}
}
}
static bool
plist_same(int npts, Point *p1, Point *p2)
{
int i,
ii,
j;
/* find match for first point */
for (i = 0; i < npts; i++)
{
- if ((FPeq(p2[i].x, p1[0].x))
- && (FPeq(p2[i].y, p1[0].y)))
+ if (FLOAT_EQ(p2[i].x, p1[0].x) && FLOAT_EQ(p2[i].y, p1[0].y))
{
/* match found? then look forward through remaining points */
for (ii = 1, j = i + 1; ii < npts; ii++, j++)
{
if (j >= npts)
j = 0;
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (FLOAT_NE(p2[j].x, p1[ii].x) ||
+ FLOAT_NE(p2[j].y, p1[ii].y))
{
#ifdef GEODEBUG
printf("plist_same- %d failed forward match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after forward match\n", ii, npts);
#endif
if (ii == npts)
- return TRUE;
+ return true;
/* match not found forwards? then look backwards */
for (ii = 1, j = i - 1; ii < npts; ii++, j--)
{
if (j < 0)
j = (npts - 1);
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (FLOAT_NE(p2[j].x, p1[ii].x) ||
+ FLOAT_NE(p2[j].y, p1[ii].y))
{
#ifdef GEODEBUG
printf("plist_same- %d failed reverse match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after reverse match\n", ii, npts);
#endif
if (ii == npts)
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/*-------------------------------------------------------------------------
* Determine the hypotenuse.
*
* If required, x and y are swapped to make x the larger number. The
* traditional formula of x^2+y^2 is rearranged to factor x outside the
* sqrt. This allows computation of the hypotenuse for significantly
* larger values, and with a higher precision than when using the naive
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 565a034..537414c 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -76,21 +76,21 @@
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/float.h"
#include "utils/geo_decls.h"
/*
* Comparator for qsort
*
* We don't need to use the floating point macros in here, because this
- * is going only going to be used in a place to effect the performance
+ * is only going to be used in a place to effect the performance
* of the index, not the correctness.
*/
static int
compareDoubles(const void *a, const void *b)
{
double x = *(double *) a;
double y = *(double *) b;
if (x == y)
return 0;
@@ -225,80 +225,80 @@ nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant)
else
next_rect_box->range_box_y.right.high = centroid->right.high;
return next_rect_box;
}
/* Can any range from range_box overlap with this argument? */
static bool
overlap2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->low) &&
- FPle(range_box->left.low, query->high);
+ return range_box->right.high >= query->low &&
+ range_box->left.low <= query->high;
}
/* Can any rectangle from rect_box overlap with this argument? */
static bool
overlap4D(RectBox *rect_box, RangeBox *query)
{
return overlap2D(&rect_box->range_box_x, &query->left) &&
overlap2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box contain this argument? */
static bool
contain2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->high) &&
- FPle(range_box->left.low, query->low);
+ return range_box->right.high >= query->high &&
+ range_box->left.low <= query->low;
}
/* Can any rectangle from rect_box contain this argument? */
static bool
contain4D(RectBox *rect_box, RangeBox *query)
{
return contain2D(&rect_box->range_box_x, &query->left) &&
contain2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box be contained by this argument? */
static bool
contained2D(RangeBox *range_box, Range *query)
{
- return FPle(range_box->left.low, query->high) &&
- FPge(range_box->left.high, query->low) &&
- FPle(range_box->right.low, query->high) &&
- FPge(range_box->right.high, query->low);
+ return range_box->left.low <= query->high &&
+ range_box->left.high >= query->low &&
+ range_box->right.low <= query->high &&
+ range_box->right.high >= query->low;
}
/* Can any rectangle from rect_box be contained by this argument? */
static bool
contained4D(RectBox *rect_box, RangeBox *query)
{
return contained2D(&rect_box->range_box_x, &query->left) &&
contained2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box to be lower than this argument? */
static bool
lower2D(RangeBox *range_box, Range *query)
{
- return FPlt(range_box->left.low, query->low) &&
- FPlt(range_box->right.low, query->low);
+ return range_box->left.low < query->low &&
+ range_box->right.low < query->low;
}
/* Can any range from range_box to be higher than this argument? */
static bool
higher2D(RangeBox *range_box, Range *query)
{
- return FPgt(range_box->left.high, query->high) &&
- FPgt(range_box->right.high, query->high);
+ return range_box->left.high > query->high &&
+ range_box->right.high > query->high;
}
/* Can any rectangle from rect_box be left of this argument? */
static bool
left4D(RectBox *rect_box, RangeBox *query)
{
return lower2D(&rect_box->range_box_x, &query->left);
}
/* Can any rectangle from rect_box does not extend the right of this argument? */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index fe9bc60..7a6f34b 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -1,63 +1,42 @@
/*-------------------------------------------------------------------------
*
* geo_decls.h - Declarations for various 2D constructs.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/geo_decls.h
*
- * NOTE
- * These routines do *not* use the float types from adt/.
- *
- * XXX These routines were not written by a numerical analyst.
- *
* XXX I have made some attempt to flesh out the operators
* and data types. There are still some more to do. - tgl 97/04/19
*
*-------------------------------------------------------------------------
*/
#ifndef GEO_DECLS_H
#define GEO_DECLS_H
#include <math.h>
#include "fmgr.h"
/*--------------------------------------------------------------------
* Useful floating point utilities and constants.
*-------------------------------------------------------------------*/
-
-#define EPSILON 1.0E-06
-
-#ifdef EPSILON
-#define FPzero(A) (fabs(A) <= EPSILON)
-#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
-#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
-#define FPlt(A,B) ((B) - (A) > EPSILON)
-#define FPle(A,B) ((A) - (B) <= EPSILON)
-#define FPgt(A,B) ((A) - (B) > EPSILON)
-#define FPge(A,B) ((B) - (A) <= EPSILON)
-#else
-#define FPzero(A) ((A) == 0)
-#define FPeq(A,B) ((A) == (B))
-#define FPne(A,B) ((A) != (B))
-#define FPlt(A,B) ((A) < (B))
-#define FPle(A,B) ((A) <= (B))
-#define FPgt(A,B) ((A) > (B))
-#define FPge(A,B) ((A) >= (B))
-#endif
-
-#define HYPOT(A, B) pg_hypot(A, B)
+#define FLOAT_HYPOT(result, val1, val2) \
+do { \
+ result = pg_hypot(val1, val2); \
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), \
+ (val1) == 0.0 && (val2) == 0.0); \
+} while(0)
/*---------------------------------------------------------------------
* Point - (x,y)
*-------------------------------------------------------------------*/
typedef struct
{
double x,
y;
} Point;
@@ -193,22 +172,20 @@ extern Datum point_horiz(PG_FUNCTION_ARGS);
extern Datum point_eq(PG_FUNCTION_ARGS);
extern Datum point_ne(PG_FUNCTION_ARGS);
extern Datum point_distance(PG_FUNCTION_ARGS);
extern Datum point_slope(PG_FUNCTION_ARGS);
extern Datum point_add(PG_FUNCTION_ARGS);
extern Datum point_sub(PG_FUNCTION_ARGS);
extern Datum point_mul(PG_FUNCTION_ARGS);
extern Datum point_div(PG_FUNCTION_ARGS);
/* private routines */
-extern double point_dt(Point *pt1, Point *pt2);
-extern double point_sl(Point *pt1, Point *pt2);
extern double pg_hypot(double x, double y);
/* public lseg routines */
extern Datum lseg_in(PG_FUNCTION_ARGS);
extern Datum lseg_out(PG_FUNCTION_ARGS);
extern Datum lseg_recv(PG_FUNCTION_ARGS);
extern Datum lseg_send(PG_FUNCTION_ARGS);
extern Datum lseg_intersect(PG_FUNCTION_ARGS);
extern Datum lseg_parallel(PG_FUNCTION_ARGS);
extern Datum lseg_perp(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/line.out b/src/test/regress/expected/line.out
index f20abdc..16ff522 100644
--- a/src/test/regress/expected/line.out
+++ b/src/test/regress/expected/line.out
@@ -32,56 +32,56 @@ LINE 1: INSERT INTO LINE_TBL VALUES ('[1,2,3, 4');
^
INSERT INTO LINE_TBL VALUES ('[(,2),(3,4)]');
ERROR: invalid input syntax for type line: "[(,2),(3,4)]"
LINE 1: INSERT INTO LINE_TBL VALUES ('[(,2),(3,4)]');
^
INSERT INTO LINE_TBL VALUES ('[(1,2),(3,4)');
ERROR: invalid input syntax for type line: "[(1,2),(3,4)"
LINE 1: INSERT INTO LINE_TBL VALUES ('[(1,2),(3,4)');
^
select * from LINE_TBL;
- s
----------------------------------------------
+ s
+--------------------------
{1,-1,1}
- {1,-1,0}
- {-0.4,-1,-6}
- {-0.000184615384615385,-1,15.3846153846154}
- {1,-1,11}
+ {6,-6,0}
+ {6,15,90}
+ {-240,-1300000,20000000}
+ {22,-22,242}
{0,-1,3}
{-1,0,3}
(7 rows)
-- functions and operators
SELECT * FROM LINE_TBL WHERE (s <-> line '[(1,2),(3,4)]') < 10;
- s
----------------------------------------------
+ s
+--------------------------
{1,-1,1}
- {1,-1,0}
- {-0.4,-1,-6}
- {-0.000184615384615385,-1,15.3846153846154}
- {1,-1,11}
+ {6,-6,0}
+ {6,15,90}
+ {-240,-1300000,20000000}
+ {22,-22,242}
{0,-1,3}
{-1,0,3}
(7 rows)
SELECT * FROM LINE_TBL WHERE (point '(0.1,0.1)' <-> s) < 1;
s
----------
{1,-1,1}
- {1,-1,0}
+ {6,-6,0}
(2 rows)
SELECT * FROM LINE_TBL WHERE (lseg '[(0.1,0.1),(0.2,0.2)]' <-> s) < 1;
s
----------
{1,-1,1}
- {1,-1,0}
+ {6,-6,0}
(2 rows)
SELECT line '[(1,1),(2,1)]' <-> line '[(-1,-1),(-2,-1)]';
?column?
----------
2
(1 row)
SELECT lseg '[(1,1),(2,1)]' <-> line '[(-1,-1),(-2,-1)]';
?column?
@@ -247,21 +247,21 @@ SELECT ?| line '[(0,0),(0,1)]'; -- true
SELECT ?| line '[(0,0),(1,1)]'; -- false
?column?
----------
f
(1 row)
SELECT line(point '(1,2)', point '(3,4)');
line
----------
- {1,-1,1}
+ {2,-2,2}
(1 row)
SELECT line '[(1,2),(3,4)]' = line '[(3,4),(4,5)]'; -- true
?column?
----------
t
(1 row)
SELECT line '[(1,2),(3,4)]' = line '[(3,4),(4,4)]'; -- false
?column?
diff --git a/src/test/regress/expected/point.out b/src/test/regress/expected/point.out
index bfc0962..6319652 100644
--- a/src/test/regress/expected/point.out
+++ b/src/test/regress/expected/point.out
@@ -249,49 +249,49 @@ SELECT '' AS three, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS dista
CREATE TEMP TABLE point_gist_tbl(f1 point);
INSERT INTO point_gist_tbl SELECT '(0,0)' FROM generate_series(0,1000);
CREATE INDEX point_gist_tbl_index ON point_gist_tbl USING gist (f1);
INSERT INTO point_gist_tbl VALUES ('(0.0000009,0.0000009)');
SET enable_seqscan TO true;
SET enable_indexscan TO false;
SET enable_bitmapscan TO false;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
SET enable_seqscan TO false;
SET enable_indexscan TO true;
SET enable_bitmapscan TO true;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index 8780353..851d03c 100644
--- a/src/test/regress/expected/select_views.out
+++ b/src/test/regress/expected/select_views.out
@@ -2,21 +2,20 @@
-- SELECT_VIEWS
-- test the views defined in CREATE_VIEWS
--
SELECT * FROM street;
name | thepath | cname
------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------
Access Rd 25 | [(-121.9283,37.894),(-121.9283,37.9)] | Oakland
Ada St | [(-122.2487,37.398),(-122.2496,37.401)] | Lafayette
Agua Fria Creek | [(-121.9254,37.922),(-121.9281,37.889)] | Oakland
Allen Ct | [(-122.0131,37.602),(-122.0117,37.597)] | Berkeley
- Alvarado Niles Road | [(-122.0325,37.903),(-122.0316,37.9)] | Berkeley
Andrea Cir | [(-121.733218,37.88641),(-121.733286,37.90617)] | Oakland
Apricot Lane | [(-121.9471,37.401),(-121.9456,37.392)] | Oakland
Apricot Lane | [(-121.9471,37.401),(-121.9456,37.392)] | Oakland
Arden Road | [(-122.0978,37.177),(-122.1,37.177)] | Oakland
Arizona St | [(-122.0381,37.901),(-122.0367,37.898)] | Berkeley
Arlington Dr | [(-121.8802,37.408),(-121.8807,37.394)] | Oakland
Arlington Dr | [(-121.8802,37.408),(-121.8807,37.394)] | Oakland
Arlington Road | [(-121.7957,37.898),(-121.7956,37.906)] | Oakland
Arroyo Las Positas | [(-121.7973,37.997),(-121.7957,37.005)] | Oakland
Arroyo Las Positas | [(-121.7973,37.997),(-121.7957,37.005)] | Oakland
@@ -41,21 +40,20 @@ SELECT * FROM street;
Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Oakland
Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Berkeley
C St | [(-122.1768,37.46),(-122.1749,37.435)] | Oakland
Calaveras Creek | [(-121.8203,37.035),(-121.8207,37.931)] | Oakland
Calaveras Creek | [(-121.8203,37.035),(-121.8207,37.931)] | Oakland
California St | [(-122.2032,37.005),(-122.2016,37.996)] | Berkeley
California St | [(-122.2032,37.005),(-122.2016,37.996)] | Lafayette
Cameron Ave | [(-122.1316,37.502),(-122.1327,37.481)] | Oakland
Campus Dr | [(-122.1704,37.905),(-122.1678,37.868),(-122.1671,37.865)] | Berkeley
Capricorn Ave | [(-122.2176,37.404),(-122.2164,37.384)] | Lafayette
- Carson St | [(-122.1846,37.9),(-122.1843,37.901)] | Berkeley
Cedar Blvd | [(-122.0282,37.446),(-122.0265,37.43)] | Oakland
Cedar St | [(-122.3011,37.737),(-122.2999,37.739)] | Berkeley
Celia St | [(-122.0611,37.3),(-122.0616,37.299)] | Oakland
Central Ave | [(-122.2343,37.602),(-122.2331,37.595)] | Berkeley
Chambers Dr | [(-122.2004,37.352),(-122.1972,37.368)] | Lafayette
Chambers Lane | [(-122.2001,37.359),(-122.1975,37.371)] | Lafayette
Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Berkeley
Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Lafayette
Chapman Dr | [(-122.0421,37.504),(-122.0414,37.498)] | Oakland
Charles St | [(-122.0255,37.505),(-122.0252,37.499)] | Oakland
@@ -66,21 +64,20 @@ SELECT * FROM street;
Coliseum Way | [(-122.2001,37.47),(-122.1978,37.516)] | Oakland
Coliseum Way | [(-122.2113,37.626),(-122.2085,37.592),(-122.2063,37.568)] | Berkeley
Coolidge Ave | [(-122.2007,37.058),(-122.1992,37.06)] | Lafayette
Cornell Ave | [(-122.2956,37.925),(-122.2949,37.906),(-122.2939,37.875)] | Berkeley
Corriea Way | [(-121.9501,37.402),(-121.9505,37.398)] | Oakland
Corriea Way | [(-121.9501,37.402),(-121.9505,37.398)] | Oakland
Cowing Road | [(-122.0002,37.934),(-121.9772,37.782)] | Oakland
Creston Road | [(-122.2639,37.002),(-122.2613,37.986),(-122.2602,37.978),(-122.2598,37.973)] | Berkeley
Creston Road | [(-122.2639,37.002),(-122.2613,37.986),(-122.2602,37.978),(-122.2598,37.973)] | Lafayette
Crow Canyon Creek | [(-122.043,37.905),(-122.0368,37.71)] | Berkeley
- Crystaline Dr | [(-121.925856,37),(-121.925869,37.00527)] | Oakland
Cull Canyon Road | [(-122.0536,37.435),(-122.0499,37.315)] | Oakland
Cull Creek | [(-122.0624,37.875),(-122.0582,37.527)] | Berkeley
D St | [(-122.1811,37.505),(-122.1805,37.497)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Berkeley
Deering St | [(-122.2146,37.904),(-122.2126,37.897)] | Berkeley
Dimond Ave | [(-122.2167,37.994),(-122.2162,37.006)] | Berkeley
Dimond Ave | [(-122.2167,37.994),(-122.2162,37.006)] | Lafayette
Donna Way | [(-122.1333,37.606),(-122.1316,37.599)] | Berkeley
@@ -182,21 +179,20 @@ SELECT * FROM street;
I- 880 Ramp | [(-122.0618,37.011),(-122.0631,37.982),(-122.0585,37.967)] | Oakland
I- 880 Ramp | [(-122.0618,37.011),(-122.0631,37.982),(-122.0585,37.967)] | Berkeley
I- 880 Ramp | [(-122.085,37.34),(-122.0801,37.316),(-122.081,37.285)] | Oakland
I- 880 Ramp | [(-122.085,37.34),(-122.0801,37.316),(-122.081,37.285)] | Oakland
I- 880 Ramp | [(-122.085,37.34),(-122.0866,37.316),(-122.0819,37.296)] | Oakland
I- 880 Ramp | [(-122.085,37.34),(-122.0866,37.316),(-122.0819,37.296)] | Oakland
I- 880 Ramp | [(-122.1029,37.61),(-122.1013,37.587),(-122.0999,37.569)] | Berkeley
I- 880 Ramp | [(-122.1379,37.891),(-122.1383,37.897),(-122.1377,37.902)] | Berkeley
I- 880 Ramp | [(-122.1379,37.931),(-122.137597,37.92736),(-122.1374,37.925),(-122.1373,37.924),(-122.1369,37.914),(-122.1358,37.905),(-122.1365,37.908),(-122.1358,37.898)] | Berkeley
I- 880 Ramp | [(-122.2536,37.898),(-122.254,37.902)] | Berkeley
- I- 880 Ramp | [(-122.2771,37.002),(-122.278,37)] | Lafayette
Indian Way | [(-122.2066,37.398),(-122.2045,37.411)] | Lafayette
Jackson St | [(-122.0845,37.6),(-122.0842,37.606)] | Berkeley
Johnson Dr | [(-121.9145,37.901),(-121.915,37.877)] | Oakland
Joyce St | [(-122.0792,37.604),(-122.0774,37.581)] | Berkeley
Juniper St | [(-121.7823,37.897),(-121.7815,37.9)] | Oakland
Kaiser Dr | [(-122.067163,37.47821),(-122.060402,37.51961)] | Oakland
Keeler Ave | [(-122.2578,37.906),(-122.2579,37.899)] | Berkeley
Kildare Road | [(-122.0968,37.016),(-122.0959,37)] | Oakland
La Playa Dr | [(-122.1039,37.545),(-122.101,37.493)] | Oakland
Laguna Ave | [(-122.2099,37.989),(-122.2089,37)] | Berkeley
@@ -234,21 +230,20 @@ SELECT * FROM street;
Mission Blvd | [(-122.0006,37.896),(-121.9989,37.88)] | Berkeley
Moores Ave | [(-122.0087,37.301),(-122.0094,37.292)] | Oakland
National Ave | [(-122.1192,37.5),(-122.1281,37.489)] | Oakland
Navajo Ct | [(-121.8779,37.901),(-121.8783,37.9)] | Oakland
Newark Blvd | [(-122.0352,37.438),(-122.0341,37.423)] | Oakland
Oakland Inner Harbor | [(-122.2625,37.913),(-122.260016,37.89484)] | Berkeley
Oakridge Road | [(-121.8316,37.049),(-121.828382,37)] | Oakland
Oneil Ave | [(-122.076754,37.62476),(-122.0745,37.595)] | Berkeley
Parkridge Dr | [(-122.1438,37.884),(-122.1428,37.9)] | Berkeley
Parkside Dr | [(-122.0475,37.603),(-122.0443,37.596)] | Berkeley
- Paseo Padre Pkwy | [(-121.9143,37.005),(-121.913522,37)] | Oakland
Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Oakland
Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Berkeley
Pearl St | [(-122.2383,37.594),(-122.2366,37.615)] | Berkeley
Periwinkle Road | [(-122.0451,37.301),(-122.044758,37.29844)] | Oakland
Pimlico Dr | [(-121.8616,37.998),(-121.8618,37.008)] | Oakland
Pimlico Dr | [(-121.8616,37.998),(-121.8618,37.008)] | Oakland
Portsmouth Ave | [(-122.1064,37.315),(-122.1064,37.308)] | Oakland
Proctor Ave | [(-122.2267,37.406),(-122.2251,37.386)] | Lafayette
Railroad Ave | [(-122.0245,37.013),(-122.0234,37.003),(-122.0223,37.993)] | Oakland
Railroad Ave | [(-122.0245,37.013),(-122.0234,37.003),(-122.0223,37.993)] | Oakland
@@ -263,39 +258,36 @@ SELECT * FROM street;
Rosedale Ct | [(-121.9232,37.9),(-121.924,37.897)] | Oakland
Sacramento St | [(-122.2799,37.606),(-122.2797,37.597)] | Berkeley
Saddle Brook Dr | [(-122.1478,37.909),(-122.1454,37.904),(-122.1451,37.888)] | Berkeley
Saginaw Ct | [(-121.8803,37.898),(-121.8806,37.901)] | Oakland
San Andreas Dr | [(-122.0609,37.9),(-122.0614,37.895)] | Berkeley
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Berkeley
Shattuck Ave | [(-122.2686,37.904),(-122.2686,37.897)] | Berkeley
Sheridan Road | [(-122.2279,37.425),(-122.2253,37.411),(-122.2223,37.377)] | Lafayette
- Shoreline Dr | [(-122.2657,37.603),(-122.2648,37.6)] | Berkeley
Skyline Blvd | [(-122.1738,37.01),(-122.1714,37.996)] | Oakland
Skyline Blvd | [(-122.1738,37.01),(-122.1714,37.996)] | Berkeley
- Skyline Dr | [(-122.0277,37.5),(-122.0284,37.498)] | Oakland
Skywest Dr | [(-122.1161,37.62),(-122.1123,37.586)] | Berkeley
Southern Pacific Railroad | [(-122.3002,37.674),(-122.2999,37.661)] | Berkeley
Sp Railroad | [(-121.893564,37.99009),(-121.897,37.016)] | Oakland
Sp Railroad | [(-121.893564,37.99009),(-121.897,37.016)] | Oakland
Sp Railroad | [(-121.9565,37.898),(-121.9562,37.9)] | Oakland
Sp Railroad | [(-122.0734,37.001),(-122.0734,37.997)] | Oakland
Sp Railroad | [(-122.0734,37.001),(-122.0734,37.997)] | Oakland
Sp Railroad | [(-122.0734,37.001),(-122.0734,37.997)] | Berkeley
Sp Railroad | [(-122.0914,37.601),(-122.087,37.56),(-122.086408,37.5551)] | Berkeley
Sp Railroad | [(-122.137792,37.003),(-122.1365,37.992),(-122.131257,37.94612)] | Oakland
Sp Railroad | [(-122.137792,37.003),(-122.1365,37.992),(-122.131257,37.94612)] | Berkeley
Sp Railroad | [(-122.1947,37.497),(-122.193328,37.4848)] | Oakland
Stanton Ave | [(-122.100392,37.0697),(-122.099513,37.06052)] | Oakland
State Hwy 123 | [(-122.3004,37.986),(-122.2998,37.969),(-122.2995,37.962),(-122.2992,37.952),(-122.299,37.942),(-122.2987,37.935),(-122.2984,37.924),(-122.2982,37.92),(-122.2976,37.904),(-122.297,37.88),(-122.2966,37.869),(-122.2959,37.848),(-122.2961,37.843)] | Berkeley
- State Hwy 13 | [(-122.1797,37.943),(-122.179871,37.91849),(-122.18,37.9),(-122.179023,37.86615),(-122.1787,37.862),(-122.1781,37.851),(-122.1777,37.845),(-122.1773,37.839),(-122.177,37.833)] | Berkeley
State Hwy 13 | [(-122.2049,37.2),(-122.20328,37.17975),(-122.1989,37.125),(-122.198078,37.11641),(-122.1975,37.11)] | Lafayette
State Hwy 13 Ramp | [(-122.2244,37.427),(-122.223,37.414),(-122.2214,37.396),(-122.2213,37.388)] | Lafayette
State Hwy 238 | ((-122.098,37.908),(-122.0983,37.907),(-122.099,37.905),(-122.101,37.898),(-122.101535,37.89711),(-122.103173,37.89438),(-122.1046,37.892),(-122.106,37.89)) | Berkeley
State Hwy 238 Ramp | [(-122.1288,37.9),(-122.1293,37.895),(-122.1296,37.906)] | Berkeley
State Hwy 24 | [(-122.2674,37.246),(-122.2673,37.248),(-122.267,37.261),(-122.2668,37.271),(-122.2663,37.298),(-122.2659,37.315),(-122.2655,37.336),(-122.265007,37.35882),(-122.264443,37.37286),(-122.2641,37.381),(-122.2638,37.388),(-122.2631,37.396),(-122.2617,37.405),(-122.2615,37.407),(-122.2605,37.412)] | Lafayette
State Hwy 84 | [(-121.9565,37.898),(-121.956589,37.89911),(-121.9569,37.903),(-121.956,37.91),(-121.9553,37.919)] | Oakland
State Hwy 84 | [(-122.0671,37.426),(-122.07,37.402),(-122.074,37.37),(-122.0773,37.338)] | Oakland
State Hwy 92 | [(-122.1085,37.326),(-122.1095,37.322),(-122.1111,37.316),(-122.1119,37.313),(-122.1125,37.311),(-122.1131,37.308),(-122.1167,37.292),(-122.1187,37.285),(-122.12,37.28)] | Oakland
State Hwy 92 Ramp | [(-122.1086,37.321),(-122.1089,37.315),(-122.1111,37.316)] | Oakland
Stuart St | [(-122.2518,37.6),(-122.2507,37.601),(-122.2491,37.606)] | Berkeley
@@ -331,148 +323,104 @@ SELECT * FROM street;
19th Ave | [(-122.2366,37.897),(-122.2359,37.905)] | Berkeley
1st St | [(-121.75508,37.89294),(-121.753581,37.90031)] | Oakland
5th St | [(-122.278,37),(-122.2792,37.005),(-122.2803,37.009)] | Lafayette
5th St | [(-122.296,37.615),(-122.2953,37.598)] | Berkeley
82nd Ave | [(-122.1695,37.596),(-122.1681,37.603)] | Berkeley
85th Ave | [(-122.1877,37.466),(-122.186,37.476)] | Oakland
89th Ave | [(-122.1822,37.459),(-122.1803,37.471)] | Oakland
98th Ave | [(-122.1568,37.498),(-122.1558,37.502)] | Oakland
98th Ave | [(-122.1693,37.438),(-122.1682,37.444)] | Oakland
98th Ave | [(-122.2001,37.258),(-122.1974,37.27)] | Lafayette
-(333 rows)
+(325 rows)
SELECT name, #thepath FROM iexit ORDER BY 1, 2;
name | ?column?
------------------------------------+----------
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 2
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
- I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 4
I- 580 | 4
- I- 580 | 4
- I- 580 | 4
I- 580 | 5
I- 580 | 5
I- 580 | 5
I- 580 | 5
- I- 580 | 5
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
- I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 6
I- 580 | 7
I- 580 | 7
I- 580 | 7
I- 580 | 7
- I- 580 | 7
- I- 580 | 7
- I- 580 | 7
- I- 580 | 8
- I- 580 | 8
- I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 8
I- 580 | 9
I- 580 | 9
- I- 580 | 9
- I- 580 | 9
- I- 580 | 9
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
I- 580 | 12
- I- 580 | 12
- I- 580 | 12
- I- 580 | 12
- I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 13
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 14
I- 580 | 18
I- 580 | 18
I- 580 | 18
I- 580 | 18
I- 580 | 18
- I- 580 | 18
I- 580 | 21
I- 580 | 21
I- 580 | 21
I- 580 | 21
I- 580 | 21
I- 580 | 21
- I- 580 | 21
- I- 580 | 21
- I- 580 | 21
- I- 580 | 21
- I- 580 | 22
I- 580 | 22
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
@@ -502,50 +450,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
@@ -587,90 +505,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 3
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
- I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
@@ -698,336 +546,231 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
- I- 580 Ramp | 5
- I- 580 Ramp | 5
- I- 580 Ramp | 6
- I- 580 Ramp | 6
I- 580 Ramp | 6
I- 580 Ramp | 7
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 2
I- 580/I-680 Ramp | 4
I- 580/I-680 Ramp | 4
I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
I- 580/I-680 Ramp | 5
I- 580/I-680 Ramp | 6
I- 580/I-680 Ramp | 6
- I- 580/I-680 Ramp | 6
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
- I- 680 | 2
I- 680 | 2
I- 680 | 3
- I- 680 | 3
- I- 680 | 3
- I- 680 | 4
- I- 680 | 4
I- 680 | 4
I- 680 | 5
I- 680 | 5
- I- 680 | 5
- I- 680 | 7
I- 680 | 7
I- 680 | 7
I- 680 | 7
I- 680 | 8
I- 680 | 8
I- 680 | 8
- I- 680 | 8
- I- 680 | 10
- I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 10
I- 680 | 16
I- 680 | 16
I- 680 | 16
I- 680 | 16
I- 680 | 16
- I- 680 | 16
- I- 680 | 16
- I- 680 | 16
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 2
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 4
I- 680 Ramp | 4
- I- 680 Ramp | 4
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 6
I- 680 Ramp | 6
- I- 680 Ramp | 6
- I- 680 Ramp | 6
- I- 680 Ramp | 7
I- 680 Ramp | 7
I- 680 Ramp | 7
I- 680 Ramp | 7
I- 680 Ramp | 8
I- 680 Ramp | 8
I- 680 Ramp | 8
- I- 680 Ramp | 8
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 2
- I- 80 | 3
- I- 80 | 3
I- 80 | 3
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
I- 80 | 5
- I- 80 | 5
- I- 80 | 11
- I- 80 | 11
I- 80 | 11
I- 80 | 11
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 2
- I- 80 Ramp | 3
- I- 80 Ramp | 3
- I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 3
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
- I- 80 Ramp | 5
- I- 80 Ramp | 5
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 880 | 2
I- 880 | 2
- I- 880 | 2
- I- 880 | 2
- I- 880 | 2
- I- 880 | 5
- I- 880 | 5
- I- 880 | 5
I- 880 | 5
I- 880 | 5
I- 880 | 5
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
I- 880 | 6
- I- 880 | 6
- I- 880 | 6
- I- 880 | 6
- I- 880 | 7
- I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 7
I- 880 | 9
I- 880 | 9
I- 880 | 9
- I- 880 | 9
- I- 880 | 9
- I- 880 | 9
- I- 880 | 9
- I- 880 | 10
- I- 880 | 10
- I- 880 | 10
- I- 880 | 10
- I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 10
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
I- 880 | 12
- I- 880 | 12
- I- 880 | 13
- I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 13
I- 880 | 14
I- 880 | 14
I- 880 | 14
I- 880 | 14
- I- 880 | 14
- I- 880 | 14
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
- I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 17
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
I- 880 | 19
- I- 880 | 19
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
@@ -1039,58 +782,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
- I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
@@ -1138,31 +843,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 4
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
@@ -1172,81 +866,56 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 6
I- 880 Ramp | 8
I- 880 Ramp | 8
- I- 880 Ramp | 8
I- 980 | 2
I- 980 | 2
I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 2
- I- 980 | 3
- I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 3
I- 980 | 4
I- 980 | 4
I- 980 | 5
I- 980 | 5
I- 980 | 7
I- 980 | 7
- I- 980 | 7
- I- 980 | 7
I- 980 | 12
I- 980 Ramp | 3
- I- 980 Ramp | 3
- I- 980 Ramp | 3
- I- 980 Ramp | 7
-(896 rows)
+(573 rows)
SELECT * FROM toyemp WHERE name = 'sharon';
name | age | location | annualsal
--------+-----+----------+-----------
sharon | 25 | (15,12) | 12000
(1 row)
--
-- Test for Leaky view scenario
--
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index e7826a4..3cd8427 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -66,21 +66,23 @@ regress_dist_ptpath(PG_FUNCTION_ARGS)
float8 result = 0.0; /* keep compiler quiet */
float8 tmp;
int i;
LSEG lseg;
switch (path->npts)
{
case 0:
PG_RETURN_NULL();
case 1:
- result = point_dt(pt, &path->p[0]);
+ result = DatumGetFloat8(DirectFunctionCall2(point_distance,
+ PointPGetDatum(pt),
+ PointPGetDatum(&path->p[0])));
break;
default:
/*
* the distance from a point to a path is the smallest distance
* from the point to any of its constituent segments.
*/
Assert(path->npts > 1);
for (i = 0; i < path->npts - 1; ++i)
{
@@ -280,22 +282,27 @@ widget_out(WIDGET *widget)
widget->center.x, widget->center.y, widget->radius);
}
PG_FUNCTION_INFO_V1(pt_in_widget);
Datum
pt_in_widget(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(1);
+ float8 distance;
- PG_RETURN_BOOL(point_dt(point, &widget->center) < widget->radius);
+ distance = DatumGetFloat8(DirectFunctionCall2(point_distance,
+ PointPGetDatum(point),
+ PointPGetDatum(&widget->center)));
+
+ PG_RETURN_BOOL(distance < widget->radius);
}
PG_FUNCTION_INFO_V1(boxarea);
Datum
boxarea(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
double width,
height;
--
2.7.4 (Apple Git-66)
On Sun, Sep 4, 2016 at 12:42 PM, Emre Hasegeli <emre@hasegeli.com> wrote:
The first patch fails to apply due to bit-rot. That's easy enough
to correct, but then it runs into warnings on make:Rebased and fixed.
These patches apply and build on top of 5c609a74 with no problems,
but `make check` finds differences per the attached. Please
investigate why the regression tests are failing and what the
appropriate response is.
Something to consider before posting new version -- should we
change some of those macros to static inline (in the .h files) to
avoid double-evaluation hazards? They might perform as well or
even better that way, and remove a subtle programmer foot-gun.One reason to keep them as macros is that they are currently
supporting both float4 and float8. Currently they look like this:FLOAT_EQ(val1, val2)
FLOAT_PL(result, val1, val2)I guess if we would use inline functions, they would look like:
FLOAT8_EQ(val1, val2)
result = FLOAT8_PL(val1, val2)Result would need to be defined again in the function to be checked
for overflow. I think this way would be more developer-friendly. I
have gone some trouble to allocate the result to be able to use the
macros. Do you know if the performance of both would be the same?
In one case where I was concerned that a static inline definition
would not perform as well as a macro, I went so far as to compare
the compiled code for both at a number of call sites in an
optimized build and found that a reasonably current gcc generated
*identical* code for both. Of course, this will not always be the
case -- in particular for macros which require multiple evaluations
of one or more parameters and they are not simple literals. In
such cases, the static inline often benchmarks faster because the
results from the potentially expensive expression can be stored on
the stack or in a register, and just referenced again.
I am not much experienced in C. If you think that inline functions
are better suited, I can rework the patches.
I suspect that they will be as fast or faster, and they eliminate
the hazard of multiple evaluation, where a programmer might not be
aware of the multiple evaluation or of some side-effect of an
argument.
--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
regression.diffsapplication/octet-stream; name=regression.diffsDownload
*** /home/kgrittn/pg/master/src/test/regress/expected/misc.out 2016-09-09 16:07:39.813690449 -0500
--- /home/kgrittn/pg/master/src/test/regress/results/misc.out 2016-09-09 16:07:53.509496156 -0500
***************
*** 66,74 ****
SELECT * FROM stud_emp;
name | age | location | salary | manager | gpa | percent
-------+-----+------------+--------+---------+-----+---------
! jeff | 23 | (8,7.7) | 600 | sharon | 3.5 |
! cim | 30 | (10.5,4.7) | 400 | | 3.4 |
! linda | 19 | (0.9,6.1) | 100 | | 2.9 |
(3 rows)
-- COPY aggtest FROM stdin;
--- 66,74 ----
SELECT * FROM stud_emp;
name | age | location | salary | manager | gpa | percent
-------+-----+------------+--------+---------+-----+---------
! jeff | 23 | (7.7,8) | 600 | sharon | 3.5 |
! cim | 30 | (4.7,10.5) | 400 | | 3.4 |
! linda | 19 | (6.1,0.9) | 100 | | 2.9 |
(3 rows)
-- COPY aggtest FROM stdin;
======================================================================
*** /home/kgrittn/pg/master/src/test/regress/expected/select_views.out 2016-09-09 16:05:18.683691625 -0500
--- /home/kgrittn/pg/master/src/test/regress/results/select_views.out 2016-09-09 16:07:54.313484749 -0500
***************
*** 415,420 ****
--- 415,432 ----
I- 580 | 21
I- 580 | 21
I- 580 | 22
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 5
+ I- 580/I-680 Ramp | 6
+ I- 580/I-680 Ramp | 6
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
***************
*** 561,578 ****
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 5
- I- 580/I-680 Ramp | 6
- I- 580/I-680 Ramp | 6
I- 680 | 2
I- 680 | 3
I- 680 | 4
--- 573,578 ----
======================================================================
On Fri, Sep 9, 2016 at 4:25 PM, Kevin Grittner <kgrittn@gmail.com> wrote:
On Sun, Sep 4, 2016 at 12:42 PM, Emre Hasegeli <emre@hasegeli.com> wrote:
These patches apply and build on top of 5c609a74 with no problems,
but `make check` finds differences per the attached. Please
investigate why the regression tests are failing and what the
appropriate response is.
I am not much experienced in C. If you think that inline functions
are better suited, I can rework the patches.I suspect that they will be as fast or faster, and they eliminate
the hazard of multiple evaluation, where a programmer might not be
aware of the multiple evaluation or of some side-effect of an
argument.
Emre, are you going to address the above? It would have to be Real
Soon Now.
--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Emre, are you going to address the above? It would have to be Real
Soon Now.
Yes, I am working on it. I found more problems, replaced more
algorithms. That took a lot of time. I will post the new version
really soon. I wouldn't feel bad, if you wouldn't have enough time to
review it in this commitfest.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
These patches apply and build on top of 5c609a74 with no problems,
but `make check` finds differences per the attached. Please
investigate why the regression tests are failing and what the
appropriate response is.
I fixed the first one and workaround the second with COLLATE "C". I
have how my changes caused this regression.
"select_views" test runs "SELECT name, #thepath FROM iexit ORDER BY 1,
2" and expects to get rows in this order:
I- 580 Ramp | 8
I- 580/I-680 Ramp | 2
With the collation on my laptop, this is actually true:
regression=# select 'I- 580/I-680 Ramp' < 'I- 580 Ramp';
?column?
----------
t
(1 row)
However, on the Linux server, I am testing it is not:
regression=# select 'I- 580 Ramp' < 'I- 580/I-680 Ramp';
?column?
----------
f
(1 row)
Do you know how it is not failing on the master?
I suspect that they will be as fast or faster, and they eliminate
the hazard of multiple evaluation, where a programmer might not be
aware of the multiple evaluation or of some side-effect of an
argument.
I reworked the the patches to use inline functions and fixed the
problems I found. The new versions are attached.
Attachments:
0001-float-header-v03.patchapplication/octet-stream; name=0001-float-header-v03.patchDownload
From e4c7c470a30aca71bc317d098f3256ac222d7d7a Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Sat, 28 May 2016 18:16:05 +0200
Subject: [PATCH 1/2] float-header-v03
---
contrib/btree_gin/btree_gin.c | 1 +
contrib/btree_gist/btree_ts.c | 2 +-
contrib/cube/cube.c | 2 +-
contrib/postgres_fdw/postgres_fdw.c | 1 +
src/backend/access/gist/gistget.c | 2 +-
src/backend/access/gist/gistproc.c | 55 +--
src/backend/access/gist/gistutil.c | 2 +-
src/backend/utils/adt/float.c | 591 ++++++--------------------
src/backend/utils/adt/formatting.c | 8 +-
src/backend/utils/adt/geo_ops.c | 5 -
src/backend/utils/adt/geo_spgist.c | 1 -
src/backend/utils/adt/numeric.c | 1 +
src/backend/utils/adt/rangetypes_gist.c | 2 +-
src/backend/utils/adt/rangetypes_selfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_typanalyze.c | 2 +-
src/backend/utils/adt/timestamp.c | 1 +
src/backend/utils/misc/guc.c | 1 +
src/include/utils/builtins.h | 13 -
src/include/utils/float.h | 383 +++++++++++++++++
src/include/utils/geo_decls.h | 1 +
20 files changed, 554 insertions(+), 522 deletions(-)
create mode 100644 src/include/utils/float.h
diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index 030b610..560ce85 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -3,20 +3,21 @@
*/
#include "postgres.h"
#include <limits.h>
#include "access/stratnum.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/cash.h"
#include "utils/date.h"
+#include "utils/float.h"
#include "utils/inet.h"
#include "utils/numeric.h"
#include "utils/timestamp.h"
#include "utils/varbit.h"
PG_MODULE_MAGIC;
typedef struct QueryInfo
{
StrategyNumber strategy;
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index ab22b27..5fb0a9f 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -1,19 +1,19 @@
/*
* contrib/btree_gist/btree_ts.c
*/
#include "postgres.h"
#include "btree_gist.h"
#include "btree_utils_num.h"
-#include "utils/builtins.h"
#include "utils/datetime.h"
+#include "utils/float.h"
typedef struct
{
Timestamp lower;
Timestamp upper;
} tsKEY;
/*
** timestamp ops
*/
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index 2bb2ed0..431495c 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -7,21 +7,21 @@
******************************************************************************/
#include "postgres.h"
#include <float.h>
#include <math.h>
#include "access/gist.h"
#include "access/stratnum.h"
#include "utils/array.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "cubedata.h"
PG_MODULE_MAGIC;
/*
* Taken from the intarray contrib header
*/
#define ARRPTR(x) ( (double *) ARR_DATA_PTR(x) )
#define ARRNELEMS(x) ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x))
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index daf0438..0bd98a1 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -26,20 +26,21 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
PG_MODULE_MAGIC;
/* Default CPU cost to start up a foreign query. */
#define DEFAULT_FDW_STARTUP_COST 100.0
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 5ba7d0a..eda48ef 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -13,21 +13,21 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist_private.h"
#include "access/relscan.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "lib/pairingheap.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/memutils.h"
#include "utils/rel.h"
/*
* gistkillitems() -- set LP_DEAD state for items an indexscan caller has
* told us were killed.
*
* We re-read page here, so it's important to check page LSN. If the page
* has been modified since the last read (as determined by LSN), we cannot
* flag any entries because it is possible that the old entry was vacuumed
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index d47211a..24b262d 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -26,62 +26,53 @@
static bool gist_box_leaf_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
static bool rtree_internal_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
/* Minimum accepted ratio of split */
#define LIMIT_RATIO 0.3
-/* Convenience macros for NaN-aware comparisons */
-#define FLOAT8_EQ(a,b) (float8_cmp_internal(a, b) == 0)
-#define FLOAT8_LT(a,b) (float8_cmp_internal(a, b) < 0)
-#define FLOAT8_LE(a,b) (float8_cmp_internal(a, b) <= 0)
-#define FLOAT8_GT(a,b) (float8_cmp_internal(a, b) > 0)
-#define FLOAT8_GE(a,b) (float8_cmp_internal(a, b) >= 0)
-#define FLOAT8_MAX(a,b) (FLOAT8_GT(a, b) ? (a) : (b))
-#define FLOAT8_MIN(a,b) (FLOAT8_LT(a, b) ? (a) : (b))
-
/**************************************************
* Box ops
**************************************************/
/*
* Calculates union of two boxes, a and b. The result is stored in *n.
*/
static void
rt_box_union(BOX *n, const BOX *a, const BOX *b)
{
- n->high.x = FLOAT8_MAX(a->high.x, b->high.x);
- n->high.y = FLOAT8_MAX(a->high.y, b->high.y);
- n->low.x = FLOAT8_MIN(a->low.x, b->low.x);
- n->low.y = FLOAT8_MIN(a->low.y, b->low.y);
+ n->high.x = float8_max(a->high.x, b->high.x);
+ n->high.y = float8_max(a->high.y, b->high.y);
+ n->low.x = float8_min(a->low.x, b->low.x);
+ n->low.y = float8_min(a->low.y, b->low.y);
}
/*
* Size of a BOX for penalty-calculation purposes.
* The result can be +Infinity, but not NaN.
*/
static double
size_box(const BOX *box)
{
/*
* Check for zero-width cases. Note that we define the size of a zero-
* by-infinity box as zero. It's important to special-case this somehow,
* as naively multiplying infinity by zero will produce NaN.
*
* The less-than cases should not happen, but if they do, say "zero".
*/
- if (FLOAT8_LE(box->high.x, box->low.x) ||
- FLOAT8_LE(box->high.y, box->low.y))
+ if (float8_le(box->high.x, box->low.x) ||
+ float8_le(box->high.y, box->low.y))
return 0.0;
/*
* We treat NaN as larger than +Infinity, so any distance involving a NaN
* and a non-NaN is infinite. Note the previous check eliminated the
* possibility that the low fields are NaNs.
*/
if (isnan(box->high.x) || isnan(box->high.y))
return get_float8_infinity();
return (box->high.x - box->low.x) * (box->high.y - box->low.y);
@@ -136,27 +127,27 @@ gist_box_consistent(PG_FUNCTION_ARGS)
query,
strategy));
}
/*
* Increase BOX b to include addon.
*/
static void
adjustBox(BOX *b, const BOX *addon)
{
- if (FLOAT8_LT(b->high.x, addon->high.x))
+ if (float8_lt(b->high.x, addon->high.x))
b->high.x = addon->high.x;
- if (FLOAT8_GT(b->low.x, addon->low.x))
+ if (float8_gt(b->low.x, addon->low.x))
b->low.x = addon->low.x;
- if (FLOAT8_LT(b->high.y, addon->high.y))
+ if (float8_lt(b->high.y, addon->high.y))
b->high.y = addon->high.y;
- if (FLOAT8_GT(b->low.y, addon->low.y))
+ if (float8_gt(b->low.y, addon->low.y))
b->low.y = addon->low.y;
}
/*
* The GiST Union method for boxes
*
* returns the minimal bounding box that encloses all the entries in entryvec
*/
Datum
gist_box_union(PG_FUNCTION_ARGS)
@@ -636,36 +627,36 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
i1 = 0;
i2 = 0;
rightLower = intervalsLower[i1].lower;
leftUpper = intervalsUpper[i2].lower;
while (true)
{
/*
* Find next lower bound of right group.
*/
while (i1 < nentries &&
- FLOAT8_EQ(rightLower, intervalsLower[i1].lower))
+ float8_eq(rightLower, intervalsLower[i1].lower))
{
- if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper))
+ if (float8_lt(leftUpper, intervalsLower[i1].upper))
leftUpper = intervalsLower[i1].upper;
i1++;
}
if (i1 >= nentries)
break;
rightLower = intervalsLower[i1].lower;
/*
* Find count of intervals which anyway should be placed to the
* left group.
*/
while (i2 < nentries &&
- FLOAT8_LE(intervalsUpper[i2].upper, leftUpper))
+ float8_le(intervalsUpper[i2].upper, leftUpper))
i2++;
/*
* Consider found split.
*/
g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
}
/*
* Iterate over upper bound of left group finding greatest possible
@@ -673,35 +664,35 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
*/
i1 = nentries - 1;
i2 = nentries - 1;
rightLower = intervalsLower[i1].upper;
leftUpper = intervalsUpper[i2].upper;
while (true)
{
/*
* Find next upper bound of left group.
*/
- while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper))
+ while (i2 >= 0 && float8_eq(leftUpper, intervalsUpper[i2].upper))
{
- if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower))
+ if (float8_gt(rightLower, intervalsUpper[i2].lower))
rightLower = intervalsUpper[i2].lower;
i2--;
}
if (i2 < 0)
break;
leftUpper = intervalsUpper[i2].upper;
/*
* Find count of intervals which anyway should be placed to the
* right group.
*/
- while (i1 >= 0 && FLOAT8_GE(intervalsLower[i1].lower, rightLower))
+ while (i1 >= 0 && float8_ge(intervalsLower[i1].lower, rightLower))
i1--;
/*
* Consider found split.
*/
g_box_consider_split(&context, dim,
rightLower, i1 + 1, leftUpper, i2 + 1);
}
}
@@ -775,42 +766,42 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
{
lower = box->low.x;
upper = box->high.x;
}
else
{
lower = box->low.y;
upper = box->high.y;
}
- if (FLOAT8_LE(upper, context.leftUpper))
+ if (float8_le(upper, context.leftUpper))
{
/* Fits to the left group */
- if (FLOAT8_GE(lower, context.rightLower))
+ if (float8_ge(lower, context.rightLower))
{
/* Fits also to the right group, so "common entry" */
commonEntries[commonEntriesCount++].index = i;
}
else
{
/* Doesn't fit to the right group, so join to the left group */
PLACE_LEFT(box, i);
}
}
else
{
/*
* Each entry should fit on either left or right group. Since this
* entry didn't fit on the left group, it better fit in the right
* group.
*/
- Assert(FLOAT8_GE(lower, context.rightLower));
+ Assert(float8_ge(lower, context.rightLower));
/* Doesn't fit to the left group, so join to the right group */
PLACE_RIGHT(box, i);
}
}
/*
* Distribute "common entries", if any.
*/
if (commonEntriesCount > 0)
@@ -880,24 +871,24 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
* equivalent to box_same().
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
- *result = (FLOAT8_EQ(b1->low.x, b2->low.x) &&
- FLOAT8_EQ(b1->low.y, b2->low.y) &&
- FLOAT8_EQ(b1->high.x, b2->high.x) &&
- FLOAT8_EQ(b1->high.y, b2->high.y));
+ *result = (float8_eq(b1->low.x, b2->low.x) &&
+ float8_eq(b1->low.y, b2->low.y) &&
+ float8_eq(b1->high.x, b2->high.x) &&
+ float8_eq(b1->high.y, b2->high.y));
else
*result = (b1 == NULL && b2 == NULL);
PG_RETURN_POINTER(result);
}
/*
* Leaf-level consistency for boxes: just apply the query operator
*/
static bool
gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 887c58b..db9bea5 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -14,21 +14,21 @@
#include "postgres.h"
#include <math.h>
#include "access/gist_private.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/syscache.h"
/*
* Write itup vector to page, has no control of free space.
*/
void
gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
{
OffsetNumber l = InvalidOffsetNumber;
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 8aa17e1..16dc2ff 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -16,61 +16,24 @@
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <limits.h>
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/sortsupport.h"
-#ifndef M_PI
-/* from my RH5.2 gcc math.h file - thomas 2000-04-03 */
-#define M_PI 3.14159265358979323846
-#endif
-
-/* Radians per degree, a.k.a. PI / 180 */
-#define RADIANS_PER_DEGREE 0.0174532925199432957692
-
-/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
- * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
- */
-#if defined(WIN32) && !defined(NAN)
-static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
-
-#define NAN (*(const double *) nan)
-#endif
-
-/* not sure what the following should be, but better to make it over-sufficient */
-#define MAXFLOATWIDTH 64
-#define MAXDOUBLEWIDTH 128
-
-/*
- * check to see if a float4/8 val has underflowed or overflowed
- */
-#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \
-do { \
- if (isinf(val) && !(inf_is_valid)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
- errmsg("value out of range: overflow"))); \
- \
- if ((val) == 0.0 && !(zero_is_valid)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
- errmsg("value out of range: underflow"))); \
-} while(0)
-
-
/* Configurable GUC parameter */
int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */
/* Cached constants for degree-based trig functions */
static bool degree_consts_set = false;
static float8 sin_30 = 0;
static float8 one_minus_cos_60 = 0;
static float8 asin_0_5 = 0;
static float8 acos_0_5 = 0;
static float8 atan_1_0 = 0;
@@ -102,100 +65,20 @@ static void init_degree_constants(void);
* their compilers spit up at the mismatch between extern declaration
* and static definition. We work around that here by the expedient
* of a #define to make the actual name of the static function different.
*/
#define cbrt my_cbrt
static double cbrt(double x);
#endif /* HAVE_CBRT */
/*
- * Routines to provide reasonably platform-independent handling of
- * infinity and NaN. We assume that isinf() and isnan() are available
- * and work per spec. (On some platforms, we have to supply our own;
- * see src/port.) However, generating an Infinity or NaN in the first
- * place is less well standardized; pre-C99 systems tend not to have C99's
- * INFINITY and NAN macros. We centralize our workarounds for this here.
- */
-
-double
-get_float8_infinity(void)
-{
-#ifdef INFINITY
- /* C99 standard way */
- return (double) INFINITY;
-#else
-
- /*
- * On some platforms, HUGE_VAL is an infinity, elsewhere it's just the
- * largest normal double. We assume forcing an overflow will get us a
- * true infinity.
- */
- return (double) (HUGE_VAL * HUGE_VAL);
-#endif
-}
-
-/*
-* The funny placements of the two #pragmas is necessary because of a
-* long lived bug in the Microsoft compilers.
-* See http://support.microsoft.com/kb/120968/en-us for details
-*/
-#if (_MSC_VER >= 1800)
-#pragma warning(disable:4756)
-#endif
-float
-get_float4_infinity(void)
-{
-#ifdef INFINITY
- /* C99 standard way */
- return (float) INFINITY;
-#else
-#if (_MSC_VER >= 1800)
-#pragma warning(default:4756)
-#endif
-
- /*
- * On some platforms, HUGE_VAL is an infinity, elsewhere it's just the
- * largest normal double. We assume forcing an overflow will get us a
- * true infinity.
- */
- return (float) (HUGE_VAL * HUGE_VAL);
-#endif
-}
-
-double
-get_float8_nan(void)
-{
- /* (double) NAN doesn't work on some NetBSD/MIPS releases */
-#if defined(NAN) && !(defined(__NetBSD__) && defined(__mips__))
- /* C99 standard way */
- return (double) NAN;
-#else
- /* Assume we can get a NAN via zero divide */
- return (double) (0.0 / 0.0);
-#endif
-}
-
-float
-get_float4_nan(void)
-{
-#ifdef NAN
- /* C99 standard way */
- return (float) NAN;
-#else
- /* Assume we can get a NAN via zero divide */
- return (float) (0.0 / 0.0);
-#endif
-}
-
-
-/*
* Returns -1 if 'val' represents negative infinity, 1 if 'val'
* represents (positive) infinity, and 0 otherwise. On some platforms,
* this is equivalent to the isinf() macro, but not everywhere: C99
* does not specify that isinf() needs to distinguish between positive
* and negative infinity.
*/
int
is_infinite(double val)
{
int inf = isinf(val);
@@ -339,21 +222,21 @@ float4in(PG_FUNCTION_ARGS)
if (*endptr != '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type real: \"%s\"",
orig_num)));
/*
* if we get here, we have a legal double, still need to check to see if
* it's a legal float4
*/
- CHECKFLOATVAL((float4) val, isinf(val), val == 0);
+ check_float4_val((float4) val, isinf(val), val == 0);
PG_RETURN_FLOAT4((float4) val);
}
/*
* float4out - converts a float4 number to a string
* using a standard output format
*/
Datum
float4out(PG_FUNCTION_ARGS)
@@ -689,35 +572,35 @@ float4up(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT4(arg);
}
Datum
float4larger(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (float4_cmp_internal(arg1, arg2) > 0)
+ if (float4_gt(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT4(result);
}
Datum
float4smaller(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
float4 result;
- if (float4_cmp_internal(arg1, arg2) < 0)
+ if (float4_lt(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT4(result);
}
/*
* ======================
* FLOAT8 BASE OPERATIONS
* ======================
@@ -756,35 +639,35 @@ float8up(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(arg);
}
Datum
float8larger(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (float8_cmp_internal(arg1, arg2) > 0)
+ if (float8_gt(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT8(result);
}
Datum
float8smaller(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
float8 result;
- if (float8_cmp_internal(arg1, arg2) < 0)
+ if (float8_lt(arg1, arg2))
result = arg1;
else
result = arg2;
PG_RETURN_FLOAT8(result);
}
/*
* ====================
* ARITHMETIC OPERATORS
@@ -795,234 +678,165 @@ float8smaller(PG_FUNCTION_ARGS)
* float4pl - returns arg1 + arg2
* float4mi - returns arg1 - arg2
* float4mul - returns arg1 * arg2
* float4div - returns arg1 / arg2
*/
Datum
float4pl(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float4 result;
- result = arg1 + arg2;
-
- /*
- * There isn't any way to check for underflow of addition/subtraction
- * because numbers near the underflow value have already been rounded to
- * the point where we can't detect that the two values were originally
- * different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
- * 1.4013e-45.
- */
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT4(result);
+ PG_RETURN_FLOAT4(float4_pl(arg1, arg2));
}
Datum
float4mi(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float4 result;
- result = arg1 - arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT4(result);
+ PG_RETURN_FLOAT4(float4_mi(arg1, arg2));
}
Datum
float4mul(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float4 result;
- result = arg1 * arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
- PG_RETURN_FLOAT4(result);
+ PG_RETURN_FLOAT4(float4_mul(arg1, arg2));
}
Datum
float4div(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float4 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
-
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
- PG_RETURN_FLOAT4(result);
+ PG_RETURN_FLOAT4(float4_div(arg1, arg2));
}
/*
* float8pl - returns arg1 + arg2
* float8mi - returns arg1 - arg2
* float8mul - returns arg1 * arg2
* float8div - returns arg1 / arg2
*/
Datum
float8pl(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- result = arg1 + arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_pl(arg1, arg2));
}
Datum
float8mi(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- result = arg1 - arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mi(arg1, arg2));
}
Datum
float8mul(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- result = arg1 * arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mul(arg1, arg2));
}
Datum
float8div(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
-
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_div(arg1, arg2));
}
/*
* ====================
* COMPARISON OPERATORS
* ====================
*/
/*
* float4{eq,ne,lt,le,gt,ge} - float4/float4 comparison operations
*/
int
float4_cmp_internal(float4 a, float4 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
- if (isnan(a))
- {
- if (isnan(b))
- return 0; /* NAN = NAN */
- else
- return 1; /* NAN > non-NAN */
- }
- else if (isnan(b))
- {
- return -1; /* non-NAN < NAN */
- }
- else
- {
- if (a > b)
- return 1;
- else if (a < b)
- return -1;
- else
- return 0;
- }
+ if (float4_gt(a, b))
+ return 1;
+ if (float4_lt(a, b))
+ return -1;
+ return 0;
}
Datum
float4eq(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(float4_eq(arg1, arg2));
}
Datum
float4ne(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(float4_ne(arg1, arg2));
}
Datum
float4lt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(float4_lt(arg1, arg2));
}
Datum
float4le(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(float4_le(arg1, arg2));
}
Datum
float4gt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(float4_gt(arg1, arg2));
}
Datum
float4ge(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float4_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(float4_ge(arg1, arg2));
}
Datum
btfloat4cmp(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
}
@@ -1044,99 +858,79 @@ btfloat4sortsupport(PG_FUNCTION_ARGS)
ssup->comparator = btfloat4fastcmp;
PG_RETURN_VOID();
}
/*
* float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations
*/
int
float8_cmp_internal(float8 a, float8 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
- if (isnan(a))
- {
- if (isnan(b))
- return 0; /* NAN = NAN */
- else
- return 1; /* NAN > non-NAN */
- }
- else if (isnan(b))
- {
- return -1; /* non-NAN < NAN */
- }
- else
- {
- if (a > b)
- return 1;
- else if (a < b)
- return -1;
- else
- return 0;
- }
+ if (float8_gt(a, b))
+ return 1;
+ if (float8_lt(a, b))
+ return -1;
+ return 0;
}
Datum
float8eq(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(float8_eq(arg1, arg2));
}
Datum
float8ne(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(float8_ne(arg1, arg2));
}
Datum
float8lt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(float8_lt(arg1, arg2));
}
Datum
float8le(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(float8_le(arg1, arg2));
}
Datum
float8gt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(float8_gt(arg1, arg2));
}
Datum
float8ge(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(float8_ge(arg1, arg2));
}
Datum
btfloat8cmp(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
}
@@ -1199,21 +993,21 @@ ftod(PG_FUNCTION_ARGS)
/*
* dtof - converts a float8 number to a float4 number
*/
Datum
dtof(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
- CHECKFLOATVAL((float4) num, isinf(num), num == 0);
+ check_float4_val((float4) num, isinf(num), num == 0);
PG_RETURN_FLOAT4((float4) num);
}
/*
* dtoi4 - converts a float8 number to an int4 number
*/
Datum
dtoi4(PG_FUNCTION_ARGS)
@@ -1424,36 +1218,36 @@ dsqrt(PG_FUNCTION_ARGS)
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
errmsg("cannot take square root of a negative number")));
result = sqrt(arg1);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
+ check_float8_val(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dcbrt - returns cube root of arg1
*/
Datum
dcbrt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
result = cbrt(arg1);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
+ check_float8_val(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dpow - returns pow(arg1,arg2)
*/
Datum
dpow(PG_FUNCTION_ARGS)
{
@@ -1492,40 +1286,40 @@ dpow(PG_FUNCTION_ARGS)
/* The sign of Inf is not significant in this case. */
result = get_float8_infinity();
else if (fabs(arg1) != 1)
result = 0;
else
result = 1;
}
else if (errno == ERANGE && result != 0 && !isinf(result))
result = get_float8_infinity();
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
+ check_float8_val(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dexp - returns the exponential function of arg1
*/
Datum
dexp(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
errno = 0;
result = exp(arg1);
if (errno == ERANGE && result != 0 && !isinf(result))
result = get_float8_infinity();
- CHECKFLOATVAL(result, isinf(arg1), false);
+ check_float8_val(result, isinf(arg1), false);
PG_RETURN_FLOAT8(result);
}
/*
* dlog1 - returns the natural logarithm of arg1
*/
Datum
dlog1(PG_FUNCTION_ARGS)
{
@@ -1540,21 +1334,21 @@ dlog1(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of zero")));
if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of a negative number")));
result = log(arg1);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 1);
+ check_float8_val(result, isinf(arg1), arg1 == 1);
PG_RETURN_FLOAT8(result);
}
/*
* dlog10 - returns the base 10 logarithm of arg1
*/
Datum
dlog10(PG_FUNCTION_ARGS)
{
@@ -1570,21 +1364,21 @@ dlog10(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of zero")));
if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of a negative number")));
result = log10(arg1);
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 1);
+ check_float8_val(result, isinf(arg1), arg1 == 1);
PG_RETURN_FLOAT8(result);
}
/*
* dacos - returns the arccos of arg1 (radians)
*/
Datum
dacos(PG_FUNCTION_ARGS)
{
@@ -1600,21 +1394,21 @@ dacos(PG_FUNCTION_ARGS)
* range [-1, 1] to values in the range [0, Pi], so we should reject any
* inputs outside that range and the result will always be finite.
*/
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = acos(arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dasin - returns the arcsin of arg1 (radians)
*/
Datum
dasin(PG_FUNCTION_ARGS)
{
@@ -1630,21 +1424,21 @@ dasin(PG_FUNCTION_ARGS)
* range [-1, 1] to values in the range [-Pi/2, Pi/2], so we should reject
* any inputs outside that range and the result will always be finite.
*/
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = asin(arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* datan - returns the arctan of arg1 (radians)
*/
Datum
datan(PG_FUNCTION_ARGS)
{
@@ -1655,21 +1449,21 @@ datan(PG_FUNCTION_ARGS)
if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/*
* The principal branch of the inverse tangent function maps all inputs to
* values in the range [-Pi/2, Pi/2], so the result should always be
* finite, even if the input is infinite.
*/
result = atan(arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* atan2 - returns the arctan of arg1/arg2 (radians)
*/
Datum
datan2(PG_FUNCTION_ARGS)
{
@@ -1680,21 +1474,21 @@ datan2(PG_FUNCTION_ARGS)
/* Per the POSIX spec, return NaN if either input is NaN */
if (isnan(arg1) || isnan(arg2))
PG_RETURN_FLOAT8(get_float8_nan());
/*
* atan2 maps all inputs to values in the range [-Pi, Pi], so the result
* should always be finite, even if the inputs are infinite.
*/
result = atan2(arg1, arg2);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcos - returns the cosine of arg1 (radians)
*/
Datum
dcos(PG_FUNCTION_ARGS)
{
@@ -1720,21 +1514,21 @@ dcos(PG_FUNCTION_ARGS)
* platform reports via errno, so also explicitly test for infinite
* inputs.
*/
errno = 0;
result = cos(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcot - returns the cotangent of arg1 (radians)
*/
Datum
dcot(PG_FUNCTION_ARGS)
{
@@ -1747,21 +1541,21 @@ dcot(PG_FUNCTION_ARGS)
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = tan(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = 1.0 / result;
- CHECKFLOATVAL(result, true /* cot(0) == Inf */ , true);
+ check_float8_val(result, true /* cot(0) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/*
* dsin - returns the sine of arg1 (radians)
*/
Datum
dsin(PG_FUNCTION_ARGS)
{
@@ -1773,21 +1567,21 @@ dsin(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = sin(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dtan - returns the tangent of arg1 (radians)
*/
Datum
dtan(PG_FUNCTION_ARGS)
{
@@ -1799,21 +1593,21 @@ dtan(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = tan(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
- CHECKFLOATVAL(result, true /* tan(pi/2) == Inf */ , true);
+ check_float8_val(result, true /* tan(pi/2) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/* ========== DEGREE-BASED TRIGONOMETRIC FUNCTIONS ========== */
/*
* Initialize the cached constants declared at the head of this file
* (sin_30 etc). The fact that we need those at all, let alone need this
@@ -1951,21 +1745,21 @@ dacosd(PG_FUNCTION_ARGS)
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
if (arg1 >= 0.0)
result = acosd_q1(arg1);
else
result = 90.0 + asind_q1(-arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dasind - returns the arcsin of arg1 (degrees)
*/
Datum
dasind(PG_FUNCTION_ARGS)
{
@@ -1986,21 +1780,21 @@ dasind(PG_FUNCTION_ARGS)
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
if (arg1 >= 0.0)
result = asind_q1(arg1);
else
result = -asind_q1(-arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* datand - returns the arctan of arg1 (degrees)
*/
Datum
datand(PG_FUNCTION_ARGS)
{
@@ -2016,21 +1810,21 @@ datand(PG_FUNCTION_ARGS)
/*
* The principal branch of the inverse tangent function maps all inputs to
* values in the range [-90, 90], so the result should always be finite,
* even if the input is infinite. Additionally, we take care to ensure
* than when arg1 is 1, the result is exactly 45.
*/
atan_arg1 = atan(arg1);
result = (atan_arg1 / atan_1_0) * 45.0;
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* atan2d - returns the arctan of arg1/arg2 (degrees)
*/
Datum
datan2d(PG_FUNCTION_ARGS)
{
@@ -2050,21 +1844,21 @@ datan2d(PG_FUNCTION_ARGS)
* result should always be finite, even if the inputs are infinite.
*
* Note: this coding assumes that atan(1.0) is a suitable scaling constant
* to get an exact result from atan2(). This might well fail on us at
* some point, requiring us to decide exactly what inputs we think we're
* going to guarantee an exact result for.
*/
atan2_arg1_arg2 = atan2(arg1, arg2);
result = (atan2_arg1_arg2 / atan_1_0) * 45.0;
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* sind_0_to_30 - returns the sine of an angle that lies between 0 and
* 30 degrees. This will return exactly 0 when x is 0,
* and exactly 0.5 when x is 30 degrees.
*/
static double
@@ -2171,21 +1965,21 @@ dcosd(PG_FUNCTION_ARGS)
if (arg1 > 90.0)
{
/* cosd(180-x) = -cosd(x) */
arg1 = 180.0 - arg1;
sign = -sign;
}
result = sign * cosd_q1(arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcotd - returns the cotangent of arg1 (degrees)
*/
Datum
dcotd(PG_FUNCTION_ARGS)
{
@@ -2236,21 +2030,21 @@ dcotd(PG_FUNCTION_ARGS)
result = sign * (cot_arg1 / cot_45);
/*
* On some machines we get cotd(270) = minus zero, but this isn't always
* true. For portability, and because the user constituency for this
* function probably doesn't want minus zero, force it to plain zero.
*/
if (result == 0.0)
result = 0.0;
- CHECKFLOATVAL(result, true /* cotd(0) == Inf */ , true);
+ check_float8_val(result, true /* cotd(0) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/*
* dsind - returns the sine of arg1 (degrees)
*/
Datum
dsind(PG_FUNCTION_ARGS)
{
@@ -2290,21 +2084,21 @@ dsind(PG_FUNCTION_ARGS)
}
if (arg1 > 90.0)
{
/* sind(180-x) = sind(x) */
arg1 = 180.0 - arg1;
}
result = sign * sind_q1(arg1);
- CHECKFLOATVAL(result, false, true);
+ check_float8_val(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dtand - returns the tangent of arg1 (degrees)
*/
Datum
dtand(PG_FUNCTION_ARGS)
{
@@ -2355,64 +2149,56 @@ dtand(PG_FUNCTION_ARGS)
result = sign * (tan_arg1 / tan_45);
/*
* On some machines we get tand(180) = minus zero, but this isn't always
* true. For portability, and because the user constituency for this
* function probably doesn't want minus zero, force it to plain zero.
*/
if (result == 0.0)
result = 0.0;
- CHECKFLOATVAL(result, true /* tand(90) == Inf */ , true);
+ check_float8_val(result, true /* tand(90) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/*
* degrees - returns degrees converted from radians
*/
Datum
degrees(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
- float8 result;
- result = arg1 / RADIANS_PER_DEGREE;
-
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_div(arg1, RADIANS_PER_DEGREE));
}
/*
* dpi - returns the constant PI
*/
Datum
dpi(PG_FUNCTION_ARGS)
{
PG_RETURN_FLOAT8(M_PI);
}
/*
* radians - returns radians converted from degrees
*/
Datum
radians(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
- float8 result;
- result = arg1 * RADIANS_PER_DEGREE;
-
- CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mul(arg1, RADIANS_PER_DEGREE));
}
/*
* drandom - returns a random number
*/
Datum
drandom(PG_FUNCTION_ARGS)
{
float8 result;
@@ -2490,144 +2276,105 @@ check_float8_array(ArrayType *transarray, const char *caller, int n)
* This function is used only in two stage aggregation and
* shouldn't be called outside aggregate context.
*/
Datum
float8_combine(PG_FUNCTION_ARGS)
{
ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
float8 *transvalues1;
float8 *transvalues2;
- float8 N,
- sumX,
- sumX2;
if (!AggCheckCallContext(fcinfo, NULL))
elog(ERROR, "aggregate function called in non-aggregate context");
transvalues1 = check_float8_array(transarray1, "float8_combine", 3);
- N = transvalues1[0];
- sumX = transvalues1[1];
- sumX2 = transvalues1[2];
-
transvalues2 = check_float8_array(transarray2, "float8_combine", 3);
- N += transvalues2[0];
- sumX += transvalues2[1];
- CHECKFLOATVAL(sumX, isinf(transvalues1[1]) || isinf(transvalues2[1]),
- true);
- sumX2 += transvalues2[2];
- CHECKFLOATVAL(sumX2, isinf(transvalues1[2]) || isinf(transvalues2[2]),
- true);
-
- transvalues1[0] = N;
- transvalues1[1] = sumX;
- transvalues1[2] = sumX2;
+ transvalues1[0] = transvalues1[0] + transvalues2[0];
+ transvalues1[1] = float8_pl(transvalues1[1], transvalues2[1]);
+ transvalues1[2] = float8_pl(transvalues1[2], transvalues2[2]);
PG_RETURN_ARRAYTYPE_P(transarray1);
}
Datum
float8_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
float8 newval = PG_GETARG_FLOAT8(1);
float8 *transvalues;
- float8 N,
- sumX,
- sumX2;
transvalues = check_float8_array(transarray, "float8_accum", 3);
- N = transvalues[0];
- sumX = transvalues[1];
- sumX2 = transvalues[2];
-
- N += 1.0;
- sumX += newval;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true);
- sumX2 += newval * newval;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
- transvalues[0] = N;
- transvalues[1] = sumX;
- transvalues[2] = sumX2;
+ transvalues[0] = transvalues[0] + 1.0;
+ transvalues[1] = float8_pl(transvalues[1], newval);
+ transvalues[2] = float8_pl(transvalues[2], float8_mul(newval, newval));
PG_RETURN_ARRAYTYPE_P(transarray);
}
else
{
Datum transdatums[3];
ArrayType *result;
- transdatums[0] = Float8GetDatumFast(N);
- transdatums[1] = Float8GetDatumFast(sumX);
- transdatums[2] = Float8GetDatumFast(sumX2);
+ transvalues[0] = transvalues[0] + 1.0;
+ transvalues[1] = float8_pl(transvalues[1], newval);
+ transvalues[2] = float8_pl(transvalues[2], float8_mul(newval, newval));
result = construct_array(transdatums, 3,
FLOAT8OID,
sizeof(float8), FLOAT8PASSBYVAL, 'd');
PG_RETURN_ARRAYTYPE_P(result);
}
}
Datum
float4_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
/* do computations as float8 */
float8 newval = PG_GETARG_FLOAT4(1);
float8 *transvalues;
- float8 N,
- sumX,
- sumX2;
transvalues = check_float8_array(transarray, "float4_accum", 3);
- N = transvalues[0];
- sumX = transvalues[1];
- sumX2 = transvalues[2];
-
- N += 1.0;
- sumX += newval;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true);
- sumX2 += newval * newval;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
- transvalues[0] = N;
- transvalues[1] = sumX;
- transvalues[2] = sumX2;
+ transvalues[0] = transvalues[0] + 1.0;
+ transvalues[1] = float8_pl(transvalues[1], newval);
+ transvalues[2] = float8_pl(transvalues[2], float8_mul(newval, newval));
PG_RETURN_ARRAYTYPE_P(transarray);
}
else
{
Datum transdatums[3];
ArrayType *result;
- transdatums[0] = Float8GetDatumFast(N);
- transdatums[1] = Float8GetDatumFast(sumX);
- transdatums[2] = Float8GetDatumFast(sumX2);
+ transvalues[0] = transvalues[0] + 1.0;
+ transvalues[1] = float8_pl(transvalues[1], newval);
+ transvalues[2] = float8_pl(transvalues[2], float8_mul(newval, newval));
result = construct_array(transdatums, 3,
FLOAT8OID,
sizeof(float8), FLOAT8PASSBYVAL, 'd');
PG_RETURN_ARRAYTYPE_P(result);
}
}
Datum
@@ -2663,21 +2410,21 @@ float8_var_pop(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_var_pop", 3);
N = transvalues[0];
sumX = transvalues[1];
sumX2 = transvalues[2];
/* Population variance is undefined when N is 0, so return NULL */
if (N == 0.0)
PG_RETURN_NULL();
numerator = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numerator, isinf(sumX2) || isinf(sumX), true);
/* Watch out for roundoff error producing a negative numerator */
if (numerator <= 0.0)
PG_RETURN_FLOAT8(0.0);
PG_RETURN_FLOAT8(numerator / (N * N));
}
Datum
float8_var_samp(PG_FUNCTION_ARGS)
@@ -2692,21 +2439,21 @@ float8_var_samp(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_var_samp", 3);
N = transvalues[0];
sumX = transvalues[1];
sumX2 = transvalues[2];
/* Sample variance is undefined when N is 0 or 1, so return NULL */
if (N <= 1.0)
PG_RETURN_NULL();
numerator = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numerator, isinf(sumX2) || isinf(sumX), true);
/* Watch out for roundoff error producing a negative numerator */
if (numerator <= 0.0)
PG_RETURN_FLOAT8(0.0);
PG_RETURN_FLOAT8(numerator / (N * (N - 1.0)));
}
Datum
float8_stddev_pop(PG_FUNCTION_ARGS)
@@ -2721,21 +2468,21 @@ float8_stddev_pop(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_stddev_pop", 3);
N = transvalues[0];
sumX = transvalues[1];
sumX2 = transvalues[2];
/* Population stddev is undefined when N is 0, so return NULL */
if (N == 0.0)
PG_RETURN_NULL();
numerator = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numerator, isinf(sumX2) || isinf(sumX), true);
/* Watch out for roundoff error producing a negative numerator */
if (numerator <= 0.0)
PG_RETURN_FLOAT8(0.0);
PG_RETURN_FLOAT8(sqrt(numerator / (N * N)));
}
Datum
float8_stddev_samp(PG_FUNCTION_ARGS)
@@ -2750,21 +2497,21 @@ float8_stddev_samp(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_stddev_samp", 3);
N = transvalues[0];
sumX = transvalues[1];
sumX2 = transvalues[2];
/* Sample stddev is undefined when N is 0 or 1, so return NULL */
if (N <= 1.0)
PG_RETURN_NULL();
numerator = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numerator, isinf(sumX2) || isinf(sumX), true);
/* Watch out for roundoff error producing a negative numerator */
if (numerator <= 0.0)
PG_RETURN_FLOAT8(0.0);
PG_RETURN_FLOAT8(sqrt(numerator / (N * (N - 1.0))));
}
/*
* =========================
@@ -2799,30 +2546,30 @@ float8_regr_accum(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_regr_accum", 6);
N = transvalues[0];
sumX = transvalues[1];
sumX2 = transvalues[2];
sumY = transvalues[3];
sumY2 = transvalues[4];
sumXY = transvalues[5];
N += 1.0;
sumX += newvalX;
- CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newvalX), true);
+ check_float8_val(sumX, isinf(transvalues[1]) || isinf(newvalX), true);
sumX2 += newvalX * newvalX;
- CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newvalX), true);
+ check_float8_val(sumX2, isinf(transvalues[2]) || isinf(newvalX), true);
sumY += newvalY;
- CHECKFLOATVAL(sumY, isinf(transvalues[3]) || isinf(newvalY), true);
+ check_float8_val(sumY, isinf(transvalues[3]) || isinf(newvalY), true);
sumY2 += newvalY * newvalY;
- CHECKFLOATVAL(sumY2, isinf(transvalues[4]) || isinf(newvalY), true);
+ check_float8_val(sumY2, isinf(transvalues[4]) || isinf(newvalY), true);
sumXY += newvalX * newvalY;
- CHECKFLOATVAL(sumXY, isinf(transvalues[5]) || isinf(newvalX) ||
- isinf(newvalY), true);
+ check_float8_val(sumXY, isinf(transvalues[5]) || isinf(newvalX) ||
+ isinf(newvalY), true);
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = sumX;
@@ -2861,63 +2608,33 @@ float8_regr_accum(PG_FUNCTION_ARGS)
* This function is used only in two stage aggregation and
* shouldn't be called outside aggregate context.
*/
Datum
float8_regr_combine(PG_FUNCTION_ARGS)
{
ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
float8 *transvalues1;
float8 *transvalues2;
- float8 N,
- sumX,
- sumX2,
- sumY,
- sumY2,
- sumXY;
if (!AggCheckCallContext(fcinfo, NULL))
elog(ERROR, "aggregate function called in non-aggregate context");
transvalues1 = check_float8_array(transarray1, "float8_regr_combine", 6);
- N = transvalues1[0];
- sumX = transvalues1[1];
- sumX2 = transvalues1[2];
- sumY = transvalues1[3];
- sumY2 = transvalues1[4];
- sumXY = transvalues1[5];
-
transvalues2 = check_float8_array(transarray2, "float8_regr_combine", 6);
- N += transvalues2[0];
- sumX += transvalues2[1];
- CHECKFLOATVAL(sumX, isinf(transvalues1[1]) || isinf(transvalues2[1]),
- true);
- sumX2 += transvalues2[2];
- CHECKFLOATVAL(sumX2, isinf(transvalues1[2]) || isinf(transvalues2[2]),
- true);
- sumY += transvalues2[3];
- CHECKFLOATVAL(sumY, isinf(transvalues1[3]) || isinf(transvalues2[3]),
- true);
- sumY2 += transvalues2[4];
- CHECKFLOATVAL(sumY2, isinf(transvalues1[4]) || isinf(transvalues2[4]),
- true);
- sumXY += transvalues2[5];
- CHECKFLOATVAL(sumXY, isinf(transvalues1[5]) || isinf(transvalues2[5]),
- true);
-
- transvalues1[0] = N;
- transvalues1[1] = sumX;
- transvalues1[2] = sumX2;
- transvalues1[3] = sumY;
- transvalues1[4] = sumY2;
- transvalues1[5] = sumXY;
+ transvalues1[0] = transvalues1[0] + transvalues2[0];
+ transvalues1[1] = float8_pl(transvalues1[1], transvalues2[1]);
+ transvalues1[2] = float8_pl(transvalues1[2], transvalues2[2]);
+ transvalues1[3] = float8_pl(transvalues1[3], transvalues2[3]);
+ transvalues1[4] = float8_pl(transvalues1[4], transvalues2[4]);
+ transvalues1[5] = float8_pl(transvalues1[5], transvalues2[5]);
PG_RETURN_ARRAYTYPE_P(transarray1);
}
Datum
float8_regr_sxx(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
float8 *transvalues;
@@ -2929,21 +2646,21 @@ float8_regr_sxx(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_regr_sxx", 6);
N = transvalues[0];
sumX = transvalues[1];
sumX2 = transvalues[2];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numerator = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numerator, isinf(sumX2) || isinf(sumX), true);
/* Watch out for roundoff error producing a negative numerator */
if (numerator <= 0.0)
PG_RETURN_FLOAT8(0.0);
PG_RETURN_FLOAT8(numerator / N);
}
Datum
float8_regr_syy(PG_FUNCTION_ARGS)
@@ -2958,21 +2675,21 @@ float8_regr_syy(PG_FUNCTION_ARGS)
transvalues = check_float8_array(transarray, "float8_regr_syy", 6);
N = transvalues[0];
sumY = transvalues[3];
sumY2 = transvalues[4];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numerator = N * sumY2 - sumY * sumY;
- CHECKFLOATVAL(numerator, isinf(sumY2) || isinf(sumY), true);
+ check_float8_val(numerator, isinf(sumY2) || isinf(sumY), true);
/* Watch out for roundoff error producing a negative numerator */
if (numerator <= 0.0)
PG_RETURN_FLOAT8(0.0);
PG_RETURN_FLOAT8(numerator / N);
}
Datum
float8_regr_sxy(PG_FUNCTION_ARGS)
@@ -2989,22 +2706,22 @@ float8_regr_sxy(PG_FUNCTION_ARGS)
N = transvalues[0];
sumX = transvalues[1];
sumY = transvalues[3];
sumXY = transvalues[5];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numerator = N * sumXY - sumX * sumY;
- CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ check_float8_val(numerator, isinf(sumXY) || isinf(sumX) ||
+ isinf(sumY), true);
/* A negative result is valid here */
PG_RETURN_FLOAT8(numerator / N);
}
Datum
float8_regr_avgx(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
@@ -3057,22 +2774,22 @@ float8_covar_pop(PG_FUNCTION_ARGS)
N = transvalues[0];
sumX = transvalues[1];
sumY = transvalues[3];
sumXY = transvalues[5];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numerator = N * sumXY - sumX * sumY;
- CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ check_float8_val(numerator, isinf(sumXY) || isinf(sumX) ||
+ isinf(sumY), true);
PG_RETURN_FLOAT8(numerator / (N * N));
}
Datum
float8_covar_samp(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
float8 *transvalues;
float8 N,
@@ -3085,22 +2802,22 @@ float8_covar_samp(PG_FUNCTION_ARGS)
N = transvalues[0];
sumX = transvalues[1];
sumY = transvalues[3];
sumXY = transvalues[5];
/* if N is <= 1 we should return NULL */
if (N < 2.0)
PG_RETURN_NULL();
numerator = N * sumXY - sumX * sumY;
- CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ check_float8_val(numerator, isinf(sumXY) || isinf(sumX) ||
+ isinf(sumY), true);
PG_RETURN_FLOAT8(numerator / (N * (N - 1.0)));
}
Datum
float8_corr(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
float8 *transvalues;
float8 N,
@@ -3119,26 +2836,26 @@ float8_corr(PG_FUNCTION_ARGS)
sumX2 = transvalues[2];
sumY = transvalues[3];
sumY2 = transvalues[4];
sumXY = transvalues[5];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numeratorX = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numeratorX, isinf(sumX2) || isinf(sumX), true);
numeratorY = N * sumY2 - sumY * sumY;
- CHECKFLOATVAL(numeratorY, isinf(sumY2) || isinf(sumY), true);
+ check_float8_val(numeratorY, isinf(sumY2) || isinf(sumY), true);
numeratorXY = N * sumXY - sumX * sumY;
- CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ check_float8_val(numeratorXY, isinf(sumXY) || isinf(sumX) ||
+ isinf(sumY), true);
if (numeratorX <= 0 || numeratorY <= 0)
PG_RETURN_NULL();
PG_RETURN_FLOAT8(numeratorXY / sqrt(numeratorX * numeratorY));
}
Datum
float8_regr_r2(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
@@ -3159,26 +2876,26 @@ float8_regr_r2(PG_FUNCTION_ARGS)
sumX2 = transvalues[2];
sumY = transvalues[3];
sumY2 = transvalues[4];
sumXY = transvalues[5];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numeratorX = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numeratorX, isinf(sumX2) || isinf(sumX), true);
numeratorY = N * sumY2 - sumY * sumY;
- CHECKFLOATVAL(numeratorY, isinf(sumY2) || isinf(sumY), true);
+ check_float8_val(numeratorY, isinf(sumY2) || isinf(sumY), true);
numeratorXY = N * sumXY - sumX * sumY;
- CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ check_float8_val(numeratorXY, isinf(sumXY) || isinf(sumX) ||
+ isinf(sumY), true);
if (numeratorX <= 0)
PG_RETURN_NULL();
/* per spec, horizontal line produces 1.0 */
if (numeratorY <= 0)
PG_RETURN_FLOAT8(1.0);
PG_RETURN_FLOAT8((numeratorXY * numeratorXY) /
(numeratorX * numeratorY));
}
@@ -3200,24 +2917,24 @@ float8_regr_slope(PG_FUNCTION_ARGS)
sumX = transvalues[1];
sumX2 = transvalues[2];
sumY = transvalues[3];
sumXY = transvalues[5];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numeratorX = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numeratorX, isinf(sumX2) || isinf(sumX), true);
numeratorXY = N * sumXY - sumX * sumY;
- CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ check_float8_val(numeratorXY, isinf(sumXY) || isinf(sumX) ||
+ isinf(sumY), true);
if (numeratorX <= 0)
PG_RETURN_NULL();
PG_RETURN_FLOAT8(numeratorXY / numeratorX);
}
Datum
float8_regr_intercept(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
@@ -3235,24 +2952,24 @@ float8_regr_intercept(PG_FUNCTION_ARGS)
sumX = transvalues[1];
sumX2 = transvalues[2];
sumY = transvalues[3];
sumXY = transvalues[5];
/* if N is 0 we should return NULL */
if (N < 1.0)
PG_RETURN_NULL();
numeratorX = N * sumX2 - sumX * sumX;
- CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true);
+ check_float8_val(numeratorX, isinf(sumX2) || isinf(sumX), true);
numeratorXXY = sumY * sumX2 - sumX * sumXY;
- CHECKFLOATVAL(numeratorXXY, isinf(sumY) || isinf(sumX2) ||
- isinf(sumX) || isinf(sumXY), true);
+ check_float8_val(numeratorXXY, isinf(sumY) || isinf(sumX2) ||
+ isinf(sumX) || isinf(sumXY), true);
if (numeratorX <= 0)
PG_RETURN_NULL();
PG_RETURN_FLOAT8(numeratorXXY / numeratorX);
}
/*
* ====================================
* MIXED-PRECISION ARITHMETIC OPERATORS
@@ -3263,251 +2980,211 @@ float8_regr_intercept(PG_FUNCTION_ARGS)
* float48pl - returns arg1 + arg2
* float48mi - returns arg1 - arg2
* float48mul - returns arg1 * arg2
* float48div - returns arg1 / arg2
*/
Datum
float48pl(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- result = arg1 + arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_pl((float8) arg1, arg2));
}
Datum
float48mi(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- result = arg1 - arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mi((float8) arg1, arg2));
}
Datum
float48mul(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- result = arg1 * arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mul((float8) arg1, arg2));
}
Datum
float48div(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
-
- result = arg1 / arg2;
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_div((float8) arg1, arg2));
}
/*
* float84pl - returns arg1 + arg2
* float84mi - returns arg1 - arg2
* float84mul - returns arg1 * arg2
* float84div - returns arg1 / arg2
*/
Datum
float84pl(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float8 result;
- result = arg1 + arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_pl(arg1, (float8) arg2));
}
Datum
float84mi(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float8 result;
- result = arg1 - arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mi(arg1, (float8) arg2));
}
Datum
float84mul(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float8 result;
- result = arg1 * arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_mul(arg1, (float8) arg2));
}
Datum
float84div(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- float8 result;
- if (arg2 == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
-
- result = arg1 / arg2;
-
- CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
- PG_RETURN_FLOAT8(result);
+ PG_RETURN_FLOAT8(float8_div(arg1, (float8) arg2));
}
/*
* ====================
* COMPARISON OPERATORS
* ====================
*/
/*
* float48{eq,ne,lt,le,gt,ge} - float4/float8 comparison operations
*/
Datum
float48eq(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(float8_eq((float8) arg1, arg2));
}
Datum
float48ne(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(float8_ne((float8) arg1, arg2));
}
Datum
float48lt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(float8_lt((float8) arg1, arg2));
}
Datum
float48le(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(float8_le((float8) arg1, arg2));
}
Datum
float48gt(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(float8_gt((float8) arg1, arg2));
}
Datum
float48ge(PG_FUNCTION_ARGS)
{
float4 arg1 = PG_GETARG_FLOAT4(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(float8_ge((float8) arg1, arg2));
}
/*
* float84{eq,ne,lt,le,gt,ge} - float8/float4 comparison operations
*/
Datum
float84eq(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) == 0);
+ PG_RETURN_BOOL(float8_eq(arg1, (float8) arg2));
}
Datum
float84ne(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) != 0);
+ PG_RETURN_BOOL(float8_ne(arg1, (float8) arg2));
}
Datum
float84lt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) < 0);
+ PG_RETURN_BOOL(float8_lt(arg1, (float8) arg2));
}
Datum
float84le(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(float8_le(arg1, (float8) arg2));
}
Datum
float84gt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) > 0);
+ PG_RETURN_BOOL(float8_gt(arg1, (float8) arg2));
}
Datum
float84ge(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float4 arg2 = PG_GETARG_FLOAT4(1);
- PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(float8_ge(arg1, (float8) arg2));
}
/*
* Implements the float8 version of the width_bucket() function
* defined by SQL2003. See also width_bucket_numeric().
*
* 'bound1' and 'bound2' are the lower and upper bounds of the
* histogram's range, respectively. 'count' is the number of buckets
* in the histogram. width_bucket() returns an integer indicating the
* bucket number that 'operand' belongs to in an equiwidth histogram
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index bbd97dc..b67477f 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -80,20 +80,21 @@
#endif
#ifdef HAVE_WCTYPE_H
#include <wctype.h>
#endif
#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
+#include "utils/float.h"
#include "utils/formatting.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"
/* ----------
* Routines type
* ----------
*/
#define DCH_TYPE 1 /* DATE-TIME version */
@@ -106,27 +107,20 @@
#define KeyWord_INDEX_SIZE ('~' - ' ')
#define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
/* ----------
* Maximal length of one node
* ----------
*/
#define DCH_MAX_ITEM_SIZ 12 /* max localized day name */
#define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
-/* ----------
- * More is in float.c
- * ----------
- */
-#define MAXFLOATWIDTH 60
-#define MAXDOUBLEWIDTH 500
-
/* ----------
* Format parser structs
* ----------
*/
typedef struct
{
char *name; /* suffix string */
int len, /* suffix length */
id, /* used in node->suffix */
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 178b556..9e01ce9 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -14,27 +14,22 @@
*/
#include "postgres.h"
#include <math.h>
#include <limits.h>
#include <float.h>
#include <ctype.h>
#include "libpq/pqformat.h"
#include "miscadmin.h"
-#include "utils/builtins.h"
#include "utils/geo_decls.h"
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
#define GEODEBUG
/*
* Internal routines
*/
enum path_delim
{
PATH_NONE, PATH_OPEN, PATH_CLOSED
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 0190156..54f4f0e 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -69,21 +69,20 @@
* src/backend/utils/adt/geo_spgist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
-#include "utils/builtins.h"
#include "utils/geo_decls.h"
/*
* Comparator for qsort
*
* We don't need to use the floating point macros in here, because this
* is going only going to be used in a place to effect the performance
* of the index, not the correctness.
*/
static int
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 384e672..ece1d4b 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -28,20 +28,21 @@
#include "access/hash.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "lib/hyperloglog.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/guc.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/sortsupport.h"
/* ----------
* Uncomment the following to enable compilation of dump_numeric()
* and dump_var() and to get a dump of any result produced by make_result().
* ----------
#define NUMERIC_DEBUG
diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c
index 01cd234..71dc12e 100644
--- a/src/backend/utils/adt/rangetypes_gist.c
+++ b/src/backend/utils/adt/rangetypes_gist.c
@@ -9,21 +9,21 @@
*
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_gist.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist.h"
#include "access/stratnum.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/datum.h"
#include "utils/rangetypes.h"
/*
* Range class properties used to segregate different classes of ranges in
* GiST. Each unique combination of properties is a class. CLS_EMPTY cannot
* be combined with anything else.
*/
#define CLS_NORMAL 0 /* Ordinary finite range (no bits set) */
diff --git a/src/backend/utils/adt/rangetypes_selfuncs.c b/src/backend/utils/adt/rangetypes_selfuncs.c
index 8595d41..2e79ec7 100644
--- a/src/backend/utils/adt/rangetypes_selfuncs.c
+++ b/src/backend/utils/adt/rangetypes_selfuncs.c
@@ -13,21 +13,21 @@
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_selfuncs.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/selfuncs.h"
#include "utils/typcache.h"
static double calc_rangesel(TypeCacheEntry *typcache, VariableStatData *vardata,
RangeType *constval, Oid operator);
static double default_range_selectivity(Oid operator);
static double calc_hist_selectivity(TypeCacheEntry *typcache,
VariableStatData *vardata, RangeType *constval,
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index 56504fc..69f6623 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -19,21 +19,21 @@
*
* IDENTIFICATION
* src/backend/utils/adt/rangetypes_typanalyze.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "commands/vacuum.h"
-#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
static int float8_qsort_cmp(const void *a1, const void *a2);
static int range_bound_qsort_cmp(const void *a1, const void *a2, void *arg);
static void compute_range_stats(VacAttrStats *stats,
AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows);
/*
* range_typanalyze -- typanalyze function for range columns
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index c1d6f05..792c32a 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -26,20 +26,21 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/scansup.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
+#include "utils/float.h"
/*
* gcc's -ffast-math switch breaks routines that expect exact results from
* expressions like timeval / SECS_PER_HOUR, where timeval is double.
*/
#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index cced814..3423043 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -68,20 +68,21 @@
#include "storage/standby.h"
#include "storage/fd.h"
#include "storage/pg_shmem.h"
#include "storage/proc.h"
#include "storage/predicate.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/guc_tables.h"
+#include "utils/float.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
#include "utils/plancache.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/tzparser.h"
#include "utils/xml.h"
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2ae212a..22ba041 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -334,33 +334,20 @@ extern Datum bttextsortsupport(PG_FUNCTION_ARGS);
*/
extern Datum btint2sortsupport(PG_FUNCTION_ARGS);
extern Datum btint4sortsupport(PG_FUNCTION_ARGS);
extern Datum btint8sortsupport(PG_FUNCTION_ARGS);
extern Datum btfloat4sortsupport(PG_FUNCTION_ARGS);
extern Datum btfloat8sortsupport(PG_FUNCTION_ARGS);
extern Datum btoidsortsupport(PG_FUNCTION_ARGS);
extern Datum btnamesortsupport(PG_FUNCTION_ARGS);
/* float.c */
-extern PGDLLIMPORT int extra_float_digits;
-
-extern double get_float8_infinity(void);
-extern float get_float4_infinity(void);
-extern double get_float8_nan(void);
-extern float get_float4_nan(void);
-extern int is_infinite(double val);
-extern double float8in_internal(char *num, char **endptr_p,
- const char *type_name, const char *orig_string);
-extern char *float8out_internal(double num);
-extern int float4_cmp_internal(float4 a, float4 b);
-extern int float8_cmp_internal(float8 a, float8 b);
-
extern Datum float4in(PG_FUNCTION_ARGS);
extern Datum float4out(PG_FUNCTION_ARGS);
extern Datum float4recv(PG_FUNCTION_ARGS);
extern Datum float4send(PG_FUNCTION_ARGS);
extern Datum float8in(PG_FUNCTION_ARGS);
extern Datum float8out(PG_FUNCTION_ARGS);
extern Datum float8recv(PG_FUNCTION_ARGS);
extern Datum float8send(PG_FUNCTION_ARGS);
extern Datum float4abs(PG_FUNCTION_ARGS);
extern Datum float4um(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
new file mode 100644
index 0000000..c26de1c
--- /dev/null
+++ b/src/include/utils/float.h
@@ -0,0 +1,383 @@
+/*-------------------------------------------------------------------------
+ *
+ * float.h
+ * Definitions for the built-in floating-point types
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/include/utils/float.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FLOAT_H
+#define FLOAT_H
+
+#include <math.h>
+
+#ifndef M_PI
+/* From my RH5.2 gcc math.h file - thomas 2000-04-03 */
+#define M_PI 3.14159265358979323846
+#endif
+
+/* Radians per degree, a.k.a. PI / 180 */
+#define RADIANS_PER_DEGREE 0.0174532925199432957692
+
+/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
+ */
+#if defined(WIN32) && !defined(NAN)
+static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
+
+#define NAN (*(const float8 *) nan)
+#endif
+
+/*
+ * We are not sure what the following should be, but better to make it
+ * over-sufficient.
+ */
+#define MAXFLOATWIDTH 64
+#define MAXDOUBLEWIDTH 128
+
+extern PGDLLIMPORT int extra_float_digits;
+
+/*
+ * Utility functions in float.c
+ */
+extern int is_infinite(float8 val);
+extern float8 float8in_internal(char *num, char **endptr_p,
+ const char *type_name, const char *orig_string);
+extern char *float8out_internal(float8 num);
+extern int float4_cmp_internal(float4 a, float4 b);
+extern int float8_cmp_internal(float8 a, float8 b);
+
+/*
+ * Routines to provide reasonably platform-independent handling of
+ * infinity and NaN
+ *
+ * We assume that isinf() and isnan() are available and work per spec.
+ * (On some platforms, we have to supply our own; see src/port.) However,
+ * generating an Infinity or NaN in the first place is less well standardized;
+ * pre-C99 systems tend not to have C99's INFINITY and NAN macros. We
+ * centralize our workarounds for this here.
+ */
+
+/*
+* The funny placements of the two #pragmas is necessary because of a
+* long lived bug in the Microsoft compilers.
+* See http://support.microsoft.com/kb/120968/en-us for details
+*/
+#if (_MSC_VER >= 1800)
+#pragma warning(disable:4756)
+#endif
+static inline float
+get_float4_infinity(void)
+{
+#ifdef INFINITY
+ /* C99 standard way */
+ return (float) INFINITY;
+#else
+#if (_MSC_VER >= 1800)
+#pragma warning(default:4756)
+#endif
+
+ /*
+ * On some platforms, HUGE_VAL is an infinity, elsewhere it's just the
+ * largest normal float8. We assume forcing an overflow will get us a
+ * true infinity.
+ */
+ return (float) (HUGE_VAL * HUGE_VAL);
+#endif
+}
+
+static inline float8
+get_float8_infinity(void)
+{
+#ifdef INFINITY
+ /* C99 standard way */
+ return (float8) INFINITY;
+#else
+
+ /*
+ * On some platforms, HUGE_VAL is an infinity, elsewhere it's just the
+ * largest normal float8. We assume forcing an overflow will get us a
+ * true infinity.
+ */
+ return (float8) (HUGE_VAL * HUGE_VAL);
+#endif
+}
+
+static inline float4
+get_float4_nan(void)
+{
+#ifdef NAN
+ /* C99 standard way */
+ return (float) NAN;
+#else
+ /* Assume we can get a NAN via zero divide */
+ return (float) (0.0 / 0.0);
+#endif
+}
+
+static inline float8
+get_float8_nan(void)
+{
+ /* (float8) NAN doesn't work on some NetBSD/MIPS releases */
+#if defined(NAN) && !(defined(__NetBSD__) && defined(__mips__))
+ /* C99 standard way */
+ return (float8) NAN;
+#else
+ /* Assume we can get a NAN via zero divide */
+ return (float8) (0.0 / 0.0);
+#endif
+}
+
+/*
+ * Checks to see if a float4/8 val has underflowed or overflowed
+ */
+
+static inline void
+check_float4_val(float4 val, bool inf_is_valid, bool zero_is_valid)
+{
+ if (isinf(val) && !(inf_is_valid))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow")));
+
+ if ((val) == 0.0 && !(zero_is_valid))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow")));
+}
+
+static inline void
+check_float8_val(float8 val, bool inf_is_valid, bool zero_is_valid)
+{
+ if (isinf(val) && !(inf_is_valid))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow")));
+
+ if ((val) == 0.0 && !(zero_is_valid))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow")));
+}
+
+/*
+ * Routines for operations with the checks above
+ *
+ * There isn't any way to check for underflow of addition/subtraction
+ * because numbers near the underflow value have already been rounded to
+ * the point where we can't detect that the two values were originally
+ * different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
+ * 1.4013e-45.
+ */
+
+static inline float4
+float4_pl(float4 val1, float4 val2)
+{
+ float4 result;
+
+ result = val1 + val2;
+ check_float4_val(result, isinf(val1) || isinf(val2), true);
+
+ return result;
+}
+
+static inline float8
+float8_pl(float8 val1, float8 val2)
+{
+ float8 result;
+
+ result = val1 + val2;
+ check_float8_val(result, isinf(val1) || isinf(val2), true);
+
+ return result;
+}
+
+static inline float4
+float4_mi(float4 val1, float4 val2)
+{
+ float4 result;
+
+ result = val1 - val2;
+ check_float4_val(result, isinf(val1) || isinf(val2), true);
+
+ return result;
+}
+
+static inline float8
+float8_mi(float8 val1, float8 val2)
+{
+ float8 result;
+
+ result = val1 - val2;
+ check_float8_val(result, isinf(val1) || isinf(val2), true);
+
+ return result;
+}
+
+static inline float4
+float4_mul(float4 val1, float4 val2)
+{
+ float4 result;
+
+ result = val1 * val2;
+ check_float4_val(result, isinf(val1) || isinf(val2),
+ val1 == 0.0f || val2 == 0.0f);
+
+ return result;
+}
+
+static inline float8
+float8_mul(float8 val1, float8 val2)
+{
+ float8 result;
+
+ result = val1 * val2;
+ check_float8_val(result, isinf(val1) || isinf(val2),
+ val1 == 0.0 || val2 == 0.0);
+
+ return result;
+}
+
+static inline float4
+float4_div(float4 val1, float4 val2)
+{
+ float4 result;
+
+ if (val2 == 0.0f)
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+
+ result = val1 / val2;
+ check_float4_val(result, isinf(val1) || isinf(val2), val1 == 0.0f);
+
+ return result;
+}
+
+static inline float8
+float8_div(float8 val1, float8 val2)
+{
+ float8 result;
+
+ if (val2 == 0.0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+
+ result = val1 / val2;
+ check_float8_val(result, isinf(val1) || isinf(val2), val1 == 0.0);
+
+ return result;
+}
+
+/*
+ * Routines for NaN-aware comparisons
+ *
+ * We consider all NANs to be equal and larger than any non-NAN. This is
+ * somewhat arbitrary; the important thing is to have a consistent sort
+ * order.
+ */
+
+static inline bool
+float4_eq(float4 val1, float4 val2)
+{
+ return isnan(val1) ? isnan(val2) : !isnan(val2) && val1 == val2;
+}
+
+static inline bool
+float8_eq(float8 val1, float8 val2)
+{
+ return isnan(val1) ? isnan(val2) : !isnan(val2) && val1 == val2;
+}
+
+static inline bool
+float4_ne(float4 val1, float4 val2)
+{
+ return isnan(val1) ? !isnan(val2) : isnan(val2) || val1 != val2;
+}
+
+static inline bool
+float8_ne(float8 val1, float8 val2)
+{
+ return isnan(val1) ? !isnan(val2) : isnan(val2) || val1 != val2;
+}
+
+static inline bool
+float4_lt(float4 val1, float4 val2)
+{
+ return !isnan(val1) && (isnan(val2) || val1 < val2);
+}
+
+static inline bool
+float8_lt(float8 val1, float8 val2)
+{
+ return !isnan(val1) && (isnan(val2) || val1 < val2);
+}
+
+static inline bool
+float4_le(float4 val1, float4 val2)
+{
+ return isnan(val2) || (!isnan(val1) && val1 <= val2);
+}
+
+static inline bool
+float8_le(float8 val1, float8 val2)
+{
+ return isnan(val2) || (!isnan(val1) && val1 <= val2);
+}
+
+static inline bool
+float4_gt(float4 val1, float4 val2)
+{
+ return !isnan(val2) && (isnan(val1) || val1 > val2);
+}
+
+static inline bool
+float8_gt(float8 val1, float8 val2)
+{
+ return !isnan(val2) && (isnan(val1) || val1 > val2);
+}
+
+static inline bool
+float4_ge(float4 val1, float4 val2)
+{
+ return isnan(val1) || (!isnan(val2) && val1 >= val2);
+}
+
+static inline bool
+float8_ge(float8 val1, float8 val2)
+{
+ return isnan(val1) || (!isnan(val2) && val1 >= val2);
+}
+
+static inline float4
+float4_min(float4 val1, float4 val2)
+{
+ return float4_lt(val1, val2) ? val1 : val2;
+}
+
+static inline float8
+float8_min(float8 val1, float8 val2)
+{
+ return float8_lt(val1, val2) ? val1 : val2;
+}
+
+static inline float4
+float4_max(float4 val1, float4 val2)
+{
+ return float4_gt(val1, val2) ? val1 : val2;
+}
+
+static inline float8
+float8_max(float8 val1, float8 val2)
+{
+ return float8_gt(val1, val2) ? val1 : val2;
+}
+
+#endif /* FLOAT_H */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index fe9bc60..3a9287f 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -17,20 +17,21 @@
* and data types. There are still some more to do. - tgl 97/04/19
*
*-------------------------------------------------------------------------
*/
#ifndef GEO_DECLS_H
#define GEO_DECLS_H
#include <math.h>
#include "fmgr.h"
+#include "utils/float.h"
/*--------------------------------------------------------------------
* Useful floating point utilities and constants.
*-------------------------------------------------------------------*/
#define EPSILON 1.0E-06
#ifdef EPSILON
#define FPzero(A) (fabs(A) <= EPSILON)
--
2.7.4 (Apple Git-66)
0002-geo-ops-fpcomp-v03.patchapplication/octet-stream; name=0002-geo-ops-fpcomp-v03.patchDownload
From 1f30dff669e7b332cfa1fde5852cd43bd0418890 Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Mon, 20 Jun 2016 10:24:32 +0200
Subject: [PATCH 2/2] geo-ops-fpcomp-v03
---
src/backend/access/gist/gistproc.c | 102 +-
src/backend/access/spgist/spgkdtreeproc.c | 25 +-
src/backend/utils/adt/geo_ops.c | 1588 ++++++++++++++--------------
src/backend/utils/adt/geo_spgist.c | 40 +-
src/include/utils/geo_decls.h | 49 +-
src/test/regress/expected/point.out | 8 +-
src/test/regress/expected/select_views.out | 29 +-
src/test/regress/sql/select_views.sql | 2 +-
8 files changed, 890 insertions(+), 953 deletions(-)
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index 24b262d..37383a8 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -858,24 +858,20 @@ gist_box_picksplit(PG_FUNCTION_ARGS)
v->spl_ldatum = PointerGetDatum(leftBox);
v->spl_rdatum = PointerGetDatum(rightBox);
PG_RETURN_POINTER(v);
}
/*
* Equality method
*
* This is used for boxes, points, circles, and polygons, all of which store
* boxes as GiST index entries.
- *
- * Returns true only when boxes are exactly the same. We can't use fuzzy
- * comparisons here without breaking index consistency; therefore, this isn't
- * equivalent to box_same().
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
*result = (float8_eq(b1->low.x, b2->low.x) &&
@@ -1127,24 +1123,24 @@ gist_circle_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
CIRCLE *in = DatumGetCircleP(entry->key);
BOX *r;
r = (BOX *) palloc(sizeof(BOX));
- r->high.x = in->center.x + in->radius;
- r->low.x = in->center.x - in->radius;
- r->high.y = in->center.y + in->radius;
- r->low.y = in->center.y - in->radius;
+ r->high.x = float8_pl(in->center.x, in->radius);
+ r->low.x = float8_mi(in->center.x, in->radius);
+ r->high.y = float8_pl(in->center.y, in->radius);
+ r->low.y = float8_mi(in->center.y, in->radius);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, FALSE);
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
@@ -1168,24 +1164,24 @@ gist_circle_consistent(PG_FUNCTION_ARGS)
*recheck = true;
if (DatumGetBoxP(entry->key) == NULL || query == NULL)
PG_RETURN_BOOL(FALSE);
/*
* Since the operators require recheck anyway, we can just use
* rtree_internal_consistent even at leaf nodes. (This works in part
* because the index entries are bounding boxes not circles.)
*/
- bbox.high.x = query->center.x + query->radius;
- bbox.low.x = query->center.x - query->radius;
- bbox.high.y = query->center.y + query->radius;
- bbox.low.y = query->center.y - query->radius;
+ bbox.high.x = float8_pl(query->center.x, query->radius);
+ bbox.low.x = float8_mi(query->center.x, query->radius);
+ bbox.high.y = float8_pl(query->center.y, query->radius);
+ bbox.low.y = float8_mi(query->center.y, query->radius);
result = rtree_internal_consistent(DatumGetBoxP(entry->key),
&bbox, strategy);
PG_RETURN_BOOL(result);
}
/**************************************************
* Point ops
**************************************************/
@@ -1246,109 +1242,113 @@ gist_point_fetch(PG_FUNCTION_ARGS)
static double
computeDistance(bool isLeaf, BOX *box, Point *point)
{
double result = 0.0;
if (isLeaf)
{
/* simple point to point distance */
result = point_point_distance(point, &box->low);
}
- else if (point->x <= box->high.x && point->x >= box->low.x &&
- point->y <= box->high.y && point->y >= box->low.y)
+ else if (float8_le(point->x, box->high.x) &&
+ float8_ge(point->x, box->low.x) &&
+ float8_le(point->y, box->high.y) &&
+ float8_ge(point->y, box->low.y))
{
/* point inside the box */
result = 0.0;
}
- else if (point->x <= box->high.x && point->x >= box->low.x)
+ else if (float8_le(point->x, box->high.x) &&
+ float8_ge(point->x, box->low.x))
{
/* point is over or below box */
- Assert(box->low.y <= box->high.y);
- if (point->y > box->high.y)
- result = point->y - box->high.y;
- else if (point->y < box->low.y)
- result = box->low.y - point->y;
+ Assert(float8_le(box->low.y, box->high.y));
+ if (float8_gt(point->y, box->high.y))
+ result = float8_mi(point->y, box->high.y);
+ else if (float8_lt(point->y, box->low.y))
+ result = float8_mi(box->low.y, point->y);
else
elog(ERROR, "inconsistent point values");
}
- else if (point->y <= box->high.y && point->y >= box->low.y)
+ else if (float8_le(point->y, box->high.y) &&
+ float8_ge(point->y, box->low.y))
{
/* point is to left or right of box */
- Assert(box->low.x <= box->high.x);
- if (point->x > box->high.x)
- result = point->x - box->high.x;
- else if (point->x < box->low.x)
- result = box->low.x - point->x;
+ Assert(float8_le(box->low.x, box->high.x));
+ if (float8_gt(point->x, box->high.x))
+ result = float8_mi(point->x, box->high.x);
+ else if (float8_lt(point->x, box->low.x))
+ result = float8_mi(box->low.x, point->x);
else
elog(ERROR, "inconsistent point values");
}
else
{
/* closest point will be a vertex */
Point p;
double subresult;
result = point_point_distance(point, &box->low);
subresult = point_point_distance(point, &box->high);
- if (result > subresult)
+ if (float8_gt(result, subresult))
result = subresult;
p.x = box->low.x;
p.y = box->high.y;
subresult = point_point_distance(point, &p);
- if (result > subresult)
+ if (float8_gt(result, subresult))
result = subresult;
p.x = box->high.x;
p.y = box->low.y;
subresult = point_point_distance(point, &p);
- if (result > subresult)
+ if (float8_gt(result, subresult))
result = subresult;
}
return result;
}
static bool
gist_point_consistent_internal(StrategyNumber strategy,
bool isLeaf, BOX *key, Point *query)
{
bool result = false;
switch (strategy)
{
case RTLeftStrategyNumber:
- result = FPlt(key->low.x, query->x);
+ result = float8_lt(key->low.x, query->x);
break;
case RTRightStrategyNumber:
- result = FPgt(key->high.x, query->x);
+ result = float8_gt(key->high.x, query->x);
break;
case RTAboveStrategyNumber:
- result = FPgt(key->high.y, query->y);
+ result = float8_gt(key->high.y, query->y);
break;
case RTBelowStrategyNumber:
- result = FPlt(key->low.y, query->y);
+ result = float8_lt(key->low.y, query->y);
break;
case RTSameStrategyNumber:
if (isLeaf)
{
/* key.high must equal key.low, so we can disregard it */
- result = (FPeq(key->low.x, query->x) &&
- FPeq(key->low.y, query->y));
+ result = (float8_eq(key->low.x, query->x) &&
+ float8_eq(key->low.y, query->y));
}
else
{
- result = (FPle(query->x, key->high.x) &&
- FPge(query->x, key->low.x) &&
- FPle(query->y, key->high.y) &&
- FPge(query->y, key->low.y));
+ result = (float8_le(query->x, key->high.x) &&
+ float8_ge(query->x, key->low.x) &&
+ float8_le(query->y, key->high.y) &&
+ float8_ge(query->y, key->low.y));
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
result = false; /* keep compiler quiet */
break;
}
return result;
}
@@ -1375,39 +1375,31 @@ gist_point_consistent(PG_FUNCTION_ARGS)
GIST_LEAF(entry),
DatumGetBoxP(entry->key),
PG_GETARG_POINT_P(1));
*recheck = false;
break;
case BoxStrategyNumberGroup:
{
/*
* The only operator in this group is point <@ box (on_pb), so
* we needn't examine strategy again.
- *
- * For historical reasons, on_pb uses exact rather than fuzzy
- * comparisons. We could use box_overlap when at an internal
- * page, but that would lead to possibly visiting child pages
- * uselessly, because box_overlap uses fuzzy comparisons.
- * Instead we write a non-fuzzy overlap test. The same code
- * will also serve for leaf-page tests, since leaf keys have
- * high == low.
*/
BOX *query,
*key;
query = PG_GETARG_BOX_P(1);
key = DatumGetBoxP(entry->key);
- result = (key->high.x >= query->low.x &&
- key->low.x <= query->high.x &&
- key->high.y >= query->low.y &&
- key->low.y <= query->high.y);
+ result = (float8_ge(key->high.x, query->low.x) &&
+ float8_le(key->low.x, query->high.x) &&
+ float8_ge(key->high.y, query->low.y) &&
+ float8_le(key->low.y, query->high.y));
*recheck = false;
}
break;
case PolygonStrategyNumberGroup:
{
POLYGON *query = PG_GETARG_POLYGON_P(1);
result = DatumGetBool(DirectFunctionCall5(
gist_poly_consistent,
PointerGetDatum(entry),
@@ -1416,22 +1408,22 @@ gist_point_consistent(PG_FUNCTION_ARGS)
0, PointerGetDatum(recheck)));
if (GIST_LEAF(entry) && result)
{
/*
* We are on leaf page and quick check shows overlapping
* of polygon's bounding box and point
*/
BOX *box = DatumGetBoxP(entry->key);
- Assert(box->high.x == box->low.x
- && box->high.y == box->low.y);
+ Assert(float8_eq(box->high.x, box->low.x) &&
+ float8_eq(box->high.y, box->low.y));
result = DatumGetBool(DirectFunctionCall2(
poly_contain_pt,
PolygonPGetDatum(query),
PointPGetDatum(&box->high)));
*recheck = false;
}
}
break;
case CircleStrategyNumberGroup:
{
@@ -1445,22 +1437,22 @@ gist_point_consistent(PG_FUNCTION_ARGS)
0, PointerGetDatum(recheck)));
if (GIST_LEAF(entry) && result)
{
/*
* We are on leaf page and quick check shows overlapping
* of polygon's bounding box and point
*/
BOX *box = DatumGetBoxP(entry->key);
- Assert(box->high.x == box->low.x
- && box->high.y == box->low.y);
+ Assert(float8_eq(box->high.x, box->low.x) &&
+ float8_eq(box->high.y, box->low.y));
result = DatumGetBool(DirectFunctionCall2(
circle_contain_pt,
CirclePGetDatum(query),
PointPGetDatum(&box->high)));
*recheck = false;
}
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
diff --git a/src/backend/access/spgist/spgkdtreeproc.c b/src/backend/access/spgist/spgkdtreeproc.c
index 1ab9335..c030d7b 100644
--- a/src/backend/access/spgist/spgkdtreeproc.c
+++ b/src/backend/access/spgist/spgkdtreeproc.c
@@ -12,20 +12,21 @@
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/geo_decls.h"
Datum
spg_kd_config(PG_FUNCTION_ARGS)
{
/* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */
spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
cfg->prefixType = FLOAT8OID;
@@ -175,72 +176,72 @@ spg_kd_inner_consistent(PG_FUNCTION_ARGS)
which = (1 << 1) | (1 << 2);
for (i = 0; i < in->nkeys; i++)
{
Point *query = DatumGetPointP(in->scankeys[i].sk_argument);
BOX *boxQuery;
switch (in->scankeys[i].sk_strategy)
{
case RTLeftStrategyNumber:
- if ((in->level % 2) != 0 && FPlt(query->x, coord))
+ if ((in->level % 2) != 0 && float8_lt(query->x, coord))
which &= (1 << 1);
break;
case RTRightStrategyNumber:
- if ((in->level % 2) != 0 && FPgt(query->x, coord))
+ if ((in->level % 2) != 0 && float8_gt(query->x, coord))
which &= (1 << 2);
break;
case RTSameStrategyNumber:
if ((in->level % 2) != 0)
{
- if (FPlt(query->x, coord))
+ if (float8_lt(query->x, coord))
which &= (1 << 1);
- else if (FPgt(query->x, coord))
+ else if (float8_gt(query->x, coord))
which &= (1 << 2);
}
else
{
- if (FPlt(query->y, coord))
+ if (float8_lt(query->y, coord))
which &= (1 << 1);
- else if (FPgt(query->y, coord))
+ else if (float8_gt(query->y, coord))
which &= (1 << 2);
}
break;
case RTBelowStrategyNumber:
- if ((in->level % 2) == 0 && FPlt(query->y, coord))
+ if ((in->level % 2) == 0 && float8_lt(query->y, coord))
which &= (1 << 1);
break;
case RTAboveStrategyNumber:
- if ((in->level % 2) == 0 && FPgt(query->y, coord))
+ if ((in->level % 2) == 0 && float8_gt(query->y, coord))
which &= (1 << 2);
break;
case RTContainedByStrategyNumber:
/*
* For this operator, the query is a box not a point. We
* cheat to the extent of assuming that DatumGetPointP won't
* do anything that would be bad for a pointer-to-box.
*/
boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
if ((in->level % 2) != 0)
{
- if (FPlt(boxQuery->high.x, coord))
+ if (float8_lt(boxQuery->high.x, coord))
which &= (1 << 1);
- else if (FPgt(boxQuery->low.x, coord))
+ else if (float8_gt(boxQuery->low.x, coord))
which &= (1 << 2);
}
else
{
- if (FPlt(boxQuery->high.y, coord))
+ if (float8_lt(boxQuery->high.y, coord))
which &= (1 << 1);
- else if (FPgt(boxQuery->low.y, coord))
+ else if (float8_gt(boxQuery->low.y, coord))
which &= (1 << 2);
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d",
in->scankeys[i].sk_strategy);
break;
}
if (which == 0)
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 9e01ce9..e1f3698 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -37,52 +37,57 @@ enum path_delim
static int point_inside(Point *p, int npts, Point *plist);
static int lseg_crossing(double x, double y, double px, double py);
static BOX *box_construct(double x1, double x2, double y1, double y2);
static BOX *box_copy(BOX *box);
static BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2);
static bool box_ov(BOX *box1, BOX *box2);
static double box_ht(BOX *box);
static double box_wd(BOX *box);
static double circle_ar(CIRCLE *circle);
-static CIRCLE *circle_copy(CIRCLE *circle);
static LINE *line_construct_pm(Point *pt, double m);
static void line_construct_pts(LINE *line, Point *pt1, Point *pt2);
-static bool lseg_intersect_internal(LSEG *l1, LSEG *l2);
+static bool lseg_intersect_internal(Point *result, LSEG *l1, LSEG *l2);
static double lseg_dt(LSEG *l1, LSEG *l2);
static bool on_ps_internal(Point *pt, LSEG *lseg);
+static float8 point_on_line_value(Point *pt, LINE *line);
+static int point_lseg_cmp(Point *pt, LSEG *lseg);
static void make_bound_box(POLYGON *poly);
static bool plist_same(int npts, Point *p1, Point *p2);
-static Point *point_construct(double x, double y);
static Point *point_copy(Point *pt);
+static bool point_eq_internal(Point *pt1, Point *pt2);
static double single_decode(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
static void single_encode(float8 x, StringInfo str);
static void pair_decode(char *str, double *x, double *y, char **endptr_p,
const char *type_name, const char *orig_string);
static void pair_encode(float8 x, float8 y, StringInfo str);
static int pair_count(char *s, char delim);
static void path_decode(char *str, bool opentype, int npts, Point *p,
bool *isopen, char **endptr_p,
const char *type_name, const char *orig_string);
static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
static void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
static double box_ar(BOX *box);
static void box_cn(Point *center, BOX *box);
-static Point *interpt_sl(LSEG *lseg, LINE *line);
-static bool has_interpt_sl(LSEG *lseg, LINE *line);
+static bool lseg_intersect_line_internal(Point *result, LSEG *lseg, LINE *line);
static double dist_pl_internal(Point *pt, LINE *line);
static double dist_ps_internal(Point *pt, LSEG *lseg);
-static Point *line_interpt_internal(LINE *l1, LINE *l2);
+static bool line_intersect_internal(Point *result, LINE *l1, LINE *l2);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
-static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2);
static double dist_ppoly_internal(Point *pt, POLYGON *poly);
+static void point_add_internal(Point *result, Point *pt1, Point *pt2);
+static void point_sub_internal(Point *result, Point *pt1, Point *pt2);
+static void point_mul_internal(Point *result, Point *pt1, Point *pt2);
+static void point_div_internal(Point *result, Point *pt1, Point *pt2);
+static void point_mul_float8_internal(Point *result, Point *pt, float8 num);
+static float8 point_cross_product_internal(Point *pt1, Point *pt2);
/*
* Delimiters for input and output strings.
* LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively.
* LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints.
*/
#define LDELIM '('
#define RDELIM ')'
@@ -350,27 +355,27 @@ box_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
BOX *box = (BOX *) palloc(sizeof(BOX));
bool isopen;
double x,
y;
path_decode(str, false, 2, &(box->high), &isopen, NULL, "box", str);
/* reorder corners if necessary... */
- if (box->high.x < box->low.x)
+ if (float8_lt(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
- if (box->high.y < box->low.y)
+ if (float8_lt(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/* box_out - convert a box to external form.
@@ -395,27 +400,27 @@ box_recv(PG_FUNCTION_ARGS)
y;
box = (BOX *) palloc(sizeof(BOX));
box->high.x = pq_getmsgfloat8(buf);
box->high.y = pq_getmsgfloat8(buf);
box->low.x = pq_getmsgfloat8(buf);
box->low.y = pq_getmsgfloat8(buf);
/* reorder corners if necessary... */
- if (box->high.x < box->low.x)
+ if (float8_lt(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
- if (box->high.y < box->low.y)
+ if (float8_lt(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/*
@@ -445,31 +450,31 @@ box_construct(double x1, double x2, double y1, double y2)
return box_fill(result, x1, x2, y1, y2);
}
/* box_fill - fill in a given box struct
*/
static BOX *
box_fill(BOX *result, double x1, double x2, double y1, double y2)
{
- if (x1 > x2)
+ if (float8_gt(x1, x2))
{
result->high.x = x1;
result->low.x = x2;
}
else
{
result->high.x = x2;
result->low.x = x1;
}
- if (y1 > y2)
+ if (float8_gt(y1, y2))
{
result->high.y = y1;
result->low.y = y2;
}
else
{
result->high.y = y2;
result->low.y = y1;
}
@@ -496,246 +501,244 @@ box_copy(BOX *box)
*---------------------------------------------------------*/
/* box_same - are two boxes identical?
*/
Datum
box_same(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box1->high.x, box2->high.x) &&
- FPeq(box1->low.x, box2->low.x) &&
- FPeq(box1->high.y, box2->high.y) &&
- FPeq(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(point_eq_internal(&box1->high, &box2->high) &&
+ point_eq_internal(&box1->low, &box2->low));
}
/* box_overlap - does box1 overlap box2?
*/
Datum
box_overlap(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
PG_RETURN_BOOL(box_ov(box1, box2));
}
static bool
box_ov(BOX *box1, BOX *box2)
{
- return (FPle(box1->low.x, box2->high.x) &&
- FPle(box2->low.x, box1->high.x) &&
- FPle(box1->low.y, box2->high.y) &&
- FPle(box2->low.y, box1->high.y));
+ return (float8_le(box1->low.x, box2->high.x) &&
+ float8_le(box2->low.x, box1->high.x) &&
+ float8_le(box1->low.y, box2->high.y) &&
+ float8_le(box2->low.y, box1->high.y));
}
/* box_left - is box1 strictly left of box2?
*/
Datum
box_left(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
+ PG_RETURN_BOOL(float8_lt(box1->high.x, box2->low.x));
}
/* box_overleft - is the right edge of box1 at or left of
* the right edge of box2?
*
* This is "less than or equal" for the end of a time range,
* when time ranges are stored as rectangles.
*/
Datum
box_overleft(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
+ PG_RETURN_BOOL(float8_le(box1->high.x, box2->high.x));
}
/* box_right - is box1 strictly right of box2?
*/
Datum
box_right(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
+ PG_RETURN_BOOL(float8_gt(box1->low.x, box2->high.x));
}
/* box_overright - is the left edge of box1 at or right of
* the left edge of box2?
*
* This is "greater than or equal" for time ranges, when time ranges
* are stored as rectangles.
*/
Datum
box_overright(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
+ PG_RETURN_BOOL(float8_ge(box1->low.x, box2->low.x));
}
/* box_below - is box1 strictly below box2?
*/
Datum
box_below(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(float8_lt(box1->high.y, box2->low.y));
}
/* box_overbelow - is the upper edge of box1 at or below
* the upper edge of box2?
*/
Datum
box_overbelow(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
+ PG_RETURN_BOOL(float8_le(box1->high.y, box2->high.y));
}
/* box_above - is box1 strictly above box2?
*/
Datum
box_above(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(float8_gt(box1->low.y, box2->high.y));
}
/* box_overabove - is the lower edge of box1 at or above
* the lower edge of box2?
*/
Datum
box_overabove(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(float8_ge(box1->low.y, box2->low.y));
}
/* box_contained - is box1 contained by box2?
*/
Datum
box_contained(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x) &&
- FPge(box1->low.x, box2->low.x) &&
- FPle(box1->high.y, box2->high.y) &&
- FPge(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(float8_le(box1->high.x, box2->high.x) &&
+ float8_ge(box1->low.x, box2->low.x) &&
+ float8_le(box1->high.y, box2->high.y) &&
+ float8_ge(box1->low.y, box2->low.y));
}
/* box_contain - does box1 contain box2?
*/
Datum
box_contain(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y));
+ PG_RETURN_BOOL(float8_ge(box1->high.x, box2->high.x) &&
+ float8_le(box1->low.x, box2->low.x) &&
+ float8_ge(box1->high.y, box2->high.y) &&
+ float8_le(box1->low.y, box2->low.y));
}
/* box_positionop -
* is box1 entirely {above,below} box2?
*
* box_below_eq and box_above_eq are obsolete versions that (probably
* erroneously) accept the equal-boundaries case. Since these are not
* in sync with the box_left and box_right code, they are deprecated and
* not supported in the PG 8.1 rtree operator class extension.
*/
Datum
box_below_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
+ PG_RETURN_BOOL(float8_le(box1->high.y, box2->low.y));
}
Datum
box_above_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
+ PG_RETURN_BOOL(float8_ge(box1->low.y, box2->high.y));
}
/* box_relop - is area(box1) relop area(box2), within
* our accuracy constraint?
*/
Datum
box_lt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(float8_lt(box_ar(box1), box_ar(box2)));
}
Datum
box_gt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(float8_gt(box_ar(box1), box_ar(box2)));
}
Datum
box_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(float8_eq(box_ar(box1), box_ar(box2)));
}
Datum
box_le(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(float8_le(box_ar(box1), box_ar(box2)));
}
Datum
box_ge(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
+ PG_RETURN_BOOL(float8_ge(box_ar(box1), box_ar(box2)));
}
/*----------------------------------------------------------
* "Arithmetic" operators on boxes.
*---------------------------------------------------------*/
/* box_area - returns the area of the box.
*/
Datum
@@ -778,21 +781,21 @@ Datum
box_distance(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
Point a,
b;
box_cn(&a, box1);
box_cn(&b, box2);
- PG_RETURN_FLOAT8(HYPOT(a.x - b.x, a.y - b.y));
+ PG_RETURN_FLOAT8(point_dt(&a, &b));
}
/* box_center - returns the center point of the box.
*/
Datum
box_center(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *result = (Point *) palloc(sizeof(Point));
@@ -810,22 +813,22 @@ box_ar(BOX *box)
{
return box_wd(box) * box_ht(box);
}
/* box_cn - stores the centerpoint of the box into *center.
*/
static void
box_cn(Point *center, BOX *box)
{
- center->x = (box->high.x + box->low.x) / 2.0;
- center->y = (box->high.y + box->low.y) / 2.0;
+ center->x = float8_div(float8_pl(box->high.x, box->low.x), 2.0);
+ center->y = float8_div(float8_pl(box->high.y, box->low.y), 2.0);
}
/* box_wd - returns the width (length) of the box
* (horizontal magnitude).
*/
static double
box_wd(BOX *box)
{
return box->high.x - box->low.x;
@@ -855,24 +858,24 @@ box_intersect(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
BOX *result;
if (!box_ov(box1, box2))
PG_RETURN_NULL();
result = (BOX *) palloc(sizeof(BOX));
- result->high.x = Min(box1->high.x, box2->high.x);
- result->low.x = Max(box1->low.x, box2->low.x);
- result->high.y = Min(box1->high.y, box2->high.y);
- result->low.y = Max(box1->low.y, box2->low.y);
+ result->high.x = float8_min(box1->high.x, box2->high.x);
+ result->low.x = float8_max(box1->low.x, box2->low.x);
+ result->high.y = float8_min(box1->high.y, box2->high.y);
+ result->low.y = float8_max(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(result);
}
/* box_diagonal -
* returns a line segment which happens to be the
* positive-slope diagonal of "box".
*/
Datum
@@ -924,29 +927,29 @@ line_in(PG_FUNCTION_ARGS)
s = str;
while (isspace((unsigned char) *s))
s++;
if (*s == '{')
{
if (!line_decode(s + 1, str, line))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"line", str)));
- if (FPzero(line->A) && FPzero(line->B))
+ if (float8_eq(line->A, 0.0) && float8_eq(line->B, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: A and B cannot both be zero")));
}
else
{
path_decode(s, true, 2, &(lseg.p[0]), &isopen, NULL, "line", str);
- if (FPeq(lseg.p[0].x, lseg.p[1].x) && FPeq(lseg.p[0].y, lseg.p[1].y))
+ if (point_eq_internal(&lseg.p[0], &lseg.p[1]))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: must be two distinct points")));
line_construct_pts(line, &lseg.p[0], &lseg.p[1]);
}
PG_RETURN_LINE_P(line);
}
@@ -969,20 +972,25 @@ line_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
LINE *line;
line = (LINE *) palloc(sizeof(LINE));
line->A = pq_getmsgfloat8(buf);
line->B = pq_getmsgfloat8(buf);
line->C = pq_getmsgfloat8(buf);
+ if (float8_eq(line->A, 0.0) && float8_eq(line->B, 0.0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid line specification: A and B cannot both be zero")));
+
PG_RETURN_LINE_P(line);
}
/*
* line_send - converts line to binary format
*/
Datum
line_send(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
@@ -1002,72 +1010,73 @@ line_send(PG_FUNCTION_ARGS)
*---------------------------------------------------------*/
/* line_construct_pm()
* point-slope
*/
static LINE *
line_construct_pm(Point *pt, double m)
{
LINE *result = (LINE *) palloc(sizeof(LINE));
- if (m == DBL_MAX)
+ if (float8_eq(m, DBL_MAX))
{
/* vertical - use "x = C" */
- result->A = -1;
- result->B = 0;
+ result->A = -1.0;
+ result->B = 0.0;
result->C = pt->x;
}
else
{
/* use "mx - y + yinter = 0" */
result->A = m;
result->B = -1.0;
- result->C = pt->y - m * pt->x;
+ result->C = float8_mi(pt->y, float8_mul(m, pt->x));
}
return result;
}
/*
* Fill already-allocated LINE struct from two points on the line
*/
static void
line_construct_pts(LINE *line, Point *pt1, Point *pt2)
{
- if (FPeq(pt1->x, pt2->x))
+ if (float8_eq(pt1->x, pt2->x))
{ /* vertical */
/* use "x = C" */
- line->A = -1;
- line->B = 0;
+ line->A = -1.0;
+ line->B = 0.0;
line->C = pt1->x;
#ifdef GEODEBUG
printf("line_construct_pts- line is vertical\n");
#endif
}
- else if (FPeq(pt1->y, pt2->y))
+ else if (float8_eq(pt1->y, pt2->y))
{ /* horizontal */
/* use "y = C" */
- line->A = 0;
- line->B = -1;
+ line->A = 0.0;
+ line->B = -1.0;
line->C = pt1->y;
#ifdef GEODEBUG
printf("line_construct_pts- line is horizontal\n");
#endif
}
else
{
/* use "mx - y + yinter = 0" */
- line->A = (pt2->y - pt1->y) / (pt2->x - pt1->x);
+ line->A = float8_div(float8_mi(pt2->y, pt1->y),
+ float8_mi(pt2->x, pt1->x));
line->B = -1.0;
- line->C = pt1->y - line->A * pt1->x;
+ line->C = float8_mi(pt1->y, float8_mul(line->A, pt1->x));
/* on some platforms, the preceding expression tends to produce -0 */
- if (line->C == 0.0)
+ if (float8_eq(line->C, 0.0))
line->C = 0.0;
#ifdef GEODEBUG
printf("line_construct_pts- line is neither vertical nor horizontal (diffs x=%.*g, y=%.*g\n",
DBL_DIG, (pt2->x - pt1->x), DBL_DIG, (pt2->y - pt1->y));
#endif
}
}
/* line_construct_pp()
* two points
@@ -1087,178 +1096,201 @@ line_construct_pp(PG_FUNCTION_ARGS)
/*----------------------------------------------------------
* Relative position routines.
*---------------------------------------------------------*/
Datum
line_intersect(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- PG_RETURN_BOOL(!DatumGetBool(DirectFunctionCall2(line_parallel,
- LinePGetDatum(l1),
- LinePGetDatum(l2))));
+ PG_RETURN_BOOL(line_intersect_internal(NULL, l1, l2));
}
Datum
line_parallel(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->B));
-
- PG_RETURN_BOOL(FPeq(l2->A, l1->A * (l2->B / l1->B)));
+ PG_RETURN_BOOL(!line_intersect_internal(NULL, l1, l2));
}
Datum
line_perp(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- if (FPzero(l1->A))
- PG_RETURN_BOOL(FPzero(l2->B));
- else if (FPzero(l1->B))
- PG_RETURN_BOOL(FPzero(l2->A));
+ if (float8_eq(l1->A, 0.0)) /* horizontal? */
+ PG_RETURN_BOOL(float8_eq(l2->B, 0.0));
+ if (float8_eq(l1->B, 0.0)) /* vertical? */
+ PG_RETURN_BOOL(float8_eq(l2->A, 0.0));
- PG_RETURN_BOOL(FPeq(((l1->A * l2->B) / (l1->B * l2->A)), -1.0));
+ if (float8_eq(l2->A, 0.0) || float8_eq(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(float8_eq(float8_mul(l1->A, l2->B),
+ -float8_mul(l2->A, l1->B)));
}
Datum
line_vertical(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->B));
+ PG_RETURN_BOOL(float8_eq(line->B, 0.0));
}
Datum
line_horizontal(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
- PG_RETURN_BOOL(FPzero(line->A));
+ PG_RETURN_BOOL(float8_eq(line->A, 0.0));
}
Datum
line_eq(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
- double k;
- if (!FPzero(l2->A))
- k = l1->A / l2->A;
- else if (!FPzero(l2->B))
- k = l1->B / l2->B;
- else if (!FPzero(l2->C))
- k = l1->C / l2->C;
- else
- k = 1.0;
+ if (float8_ne(float8_mul(l1->A, l2->B), float8_mul(l2->A, l1->B)))
+ PG_RETURN_BOOL(false);
- PG_RETURN_BOOL(FPeq(l1->A, k * l2->A) &&
- FPeq(l1->B, k * l2->B) &&
- FPeq(l1->C, k * l2->C));
+ if (float8_ne(l1->A, 0.0))
+ {
+ if (float8_eq(l2->A, 0.0))
+ PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(float8_eq(float8_mul(l1->A, l2->C),
+ float8_mul(l2->A, l1->C)));
+ }
+
+ /* l1->A == 0.0 => l1->B != 0.0 => l2->A == 0.0 */
+ if (float8_eq(l2->B, 0.0))
+ PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(float8_eq(float8_mul(l1->B, l2->C),
+ float8_mul(l2->B, l1->C)));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* line_distance()
* Distance between two lines.
*/
Datum
line_distance(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
float8 result;
- Point *tmp;
+ Point tmp;
- if (!DatumGetBool(DirectFunctionCall2(line_parallel,
- LinePGetDatum(l1),
- LinePGetDatum(l2))))
+ if (line_intersect_internal(NULL, l1, l2))
PG_RETURN_FLOAT8(0.0);
- if (FPzero(l1->B)) /* vertical? */
- PG_RETURN_FLOAT8(fabs(l1->C - l2->C));
- tmp = point_construct(0.0, l1->C);
- result = dist_pl_internal(tmp, l2);
+
+ /* horizontal or vertical? */
+ if (float8_eq(l1->A, 0.0) || float8_eq(l1->B, 0.0))
+ PG_RETURN_FLOAT8(float8_mi(l1->C, l2->C));
+
+ tmp.x = 0.0;
+ tmp.y = float8_div(-l1->C, l1->B);
+ result = dist_pl_internal(&tmp, l2);
PG_RETURN_FLOAT8(result);
}
/* line_interpt()
* Point where two lines l1, l2 intersect (if any)
*/
Datum
line_interpt(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0);
LINE *l2 = PG_GETARG_LINE_P(1);
Point *result;
- result = line_interpt_internal(l1, l2);
+ result = (Point *) palloc(sizeof(Point));
- if (result == NULL)
- PG_RETURN_NULL();
- PG_RETURN_POINT_P(result);
+ if (line_intersect_internal(result, l1, l2))
+ PG_RETURN_POINT_P(result);
+ PG_RETURN_NULL();
}
/*
- * Internal version of line_interpt
+ * Check if two lines intersect (they do, if they are not parallel)
*
- * returns a NULL pointer if no intersection point
+ * It sets the intersection point to *result, if it is not NULL.
+ *
+ * NOTE: If the lines are identical then we will find they are parallel
+ * and report "no intersection". This is a little weird, but since
+ * there's no *unique* intersection, maybe it's appropriate behavior.
*/
-static Point *
-line_interpt_internal(LINE *l1, LINE *l2)
+static bool
+line_intersect_internal(Point *result, LINE *l1, LINE *l2)
{
- Point *result;
- double x,
- y;
-
- /*
- * NOTE: if the lines are identical then we will find they are parallel
- * and report "no intersection". This is a little weird, but since
- * there's no *unique* intersection, maybe it's appropriate behavior.
- */
- if (DatumGetBool(DirectFunctionCall2(line_parallel,
- LinePGetDatum(l1),
- LinePGetDatum(l2))))
- return NULL;
-
- if (FPzero(l1->B)) /* l1 vertical? */
+ if (float8_eq(l1->A, 0.0)) /* l1 horizontal? */
{
- x = l1->C;
- y = (l2->A * x + l2->C);
+ if (float8_eq(l2->A, 0.0))
+ return false;
+
+ if (result != NULL)
+ {
+ result->y = float8_div(-l1->C, l1->B);
+ result->x = float8_div(-float8_pl(float8_mul(l2->B, result->y),
+ l2->C), l2->A);
+ }
}
- else if (FPzero(l2->B)) /* l2 vertical? */
+ else if (float8_eq(l1->B, 0.0)) /* l1 vertical? */
{
- x = l2->C;
- y = (l1->A * x + l1->C);
+ if (float8_eq(l2->B, 0.0))
+ return false;
+
+ if (result != NULL)
+ {
+ result->x = float8_div(-l1->C, l1->A);
+ result->y = float8_div(-float8_pl(float8_mul(l2->A, result->x),
+ l2->C), l2->B);
+ }
}
else
{
- x = (l1->C - l2->C) / (l2->A - l1->A);
- y = (l1->A * x + l1->C);
+ if (float8_eq(float8_mul(l1->A, l2->B), float8_mul(l2->A, l1->B)))
+ return false;
+
+ if (result != NULL)
+ {
+ if (float8_eq(l2->B, 0.0)) /* l2 vertical? */
+ result->x = float8_div(-l2->C, l2->A);
+ else
+ result->x = float8_div(float8_mi(float8_mul(l1->B, l2->C),
+ float8_mul(l2->B, l1->C)),
+ float8_mi(float8_mul(l1->A, l2->B),
+ float8_mul(l2->A, l1->B)));
+ result->y = float8_div(-float8_pl(float8_mul(l1->A, result->x),
+ l1->C), l1->B);
+ }
}
- result = point_construct(x, y);
#ifdef GEODEBUG
- printf("line_interpt- lines are A=%.*g, B=%.*g, C=%.*g, A=%.*g, B=%.*g, C=%.*g\n",
+ printf("line_intersect- lines are A=%.*g, B=%.*g, C=%.*g, A=%.*g, B=%.*g, C=%.*g\n",
DBL_DIG, l1->A, DBL_DIG, l1->B, DBL_DIG, l1->C, DBL_DIG, l2->A, DBL_DIG, l2->B, DBL_DIG, l2->C);
- printf("line_interpt- lines intersect at (%.*g,%.*g)\n", DBL_DIG, x, DBL_DIG, y);
+ if (result != NULL)
+ printf("line_intersect- lines intersect at (%.*g,%.*g)\n", DBL_DIG, result->x, DBL_DIG, result->y);
#endif
- return result;
+ return true;
}
/***********************************************************************
**
** Routines for 2D paths (sequences of line segments, also
** called `polylines').
**
** This is not a general package for geometric paths,
** which of course include polygons; the emphasis here
@@ -1284,26 +1316,26 @@ path_area(PG_FUNCTION_ARGS)
double area = 0.0;
int i,
j;
if (!path->closed)
PG_RETURN_NULL();
for (i = 0; i < path->npts; i++)
{
j = (i + 1) % path->npts;
- area += path->p[i].x * path->p[j].y;
- area -= path->p[i].y * path->p[j].x;
+ area = float8_pl(area,
+ float8_mi(float8_mul(path->p[i].x, path->p[j].y),
+ float8_mul(path->p[i].y, path->p[j].x)));
}
- area *= 0.5;
- PG_RETURN_FLOAT8(area < 0.0 ? -area : area);
+ PG_RETURN_FLOAT8(float8_div(fabs(area), 2.0));
}
Datum
path_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
PATH *path;
bool isopen;
char *s;
@@ -1520,31 +1552,31 @@ path_npoints(PG_FUNCTION_ARGS)
PG_RETURN_INT32(path->npts);
}
Datum
path_close(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
- path->closed = TRUE;
+ path->closed = true;
PG_RETURN_PATH_P(path);
}
Datum
path_open(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
- path->closed = FALSE;
+ path->closed = false;
PG_RETURN_PATH_P(path);
}
/* path_inter -
* Does p1 intersect p2 at any point?
* Use bounding boxes for a quick (O(n)) check, then do a
* O(n^2) iterative edge check.
*/
@@ -1560,33 +1592,33 @@ path_inter(PG_FUNCTION_ARGS)
LSEG seg1,
seg2;
if (p1->npts <= 0 || p2->npts <= 0)
PG_RETURN_BOOL(false);
b1.high.x = b1.low.x = p1->p[0].x;
b1.high.y = b1.low.y = p1->p[0].y;
for (i = 1; i < p1->npts; i++)
{
- b1.high.x = Max(p1->p[i].x, b1.high.x);
- b1.high.y = Max(p1->p[i].y, b1.high.y);
- b1.low.x = Min(p1->p[i].x, b1.low.x);
- b1.low.y = Min(p1->p[i].y, b1.low.y);
+ b1.high.x = float8_max(p1->p[i].x, b1.high.x);
+ b1.high.y = float8_max(p1->p[i].y, b1.high.y);
+ b1.low.x = float8_min(p1->p[i].x, b1.low.x);
+ b1.low.y = float8_min(p1->p[i].y, b1.low.y);
}
b2.high.x = b2.low.x = p2->p[0].x;
b2.high.y = b2.low.y = p2->p[0].y;
for (i = 1; i < p2->npts; i++)
{
- b2.high.x = Max(p2->p[i].x, b2.high.x);
- b2.high.y = Max(p2->p[i].y, b2.high.y);
- b2.low.x = Min(p2->p[i].x, b2.low.x);
- b2.low.y = Min(p2->p[i].y, b2.low.y);
+ b2.high.x = float8_max(p2->p[i].x, b2.high.x);
+ b2.high.y = float8_max(p2->p[i].y, b2.high.y);
+ b2.low.x = float8_min(p2->p[i].x, b2.low.x);
+ b2.low.y = float8_min(p2->p[i].y, b2.low.y);
}
if (!box_ov(&b1, &b2))
PG_RETURN_BOOL(false);
/* pairwise check lseg intersections */
for (i = 0; i < p1->npts; i++)
{
int iprev;
if (i > 0)
@@ -1606,21 +1638,21 @@ path_inter(PG_FUNCTION_ARGS)
jprev = j - 1;
else
{
if (!p2->closed)
continue;
jprev = p2->npts - 1; /* include the closure segment */
}
statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
- if (lseg_intersect_internal(&seg1, &seg2))
+ if (lseg_intersect_internal(NULL, &seg1, &seg2))
PG_RETURN_BOOL(true);
}
}
/* if we dropped through, no two segs intersected */
PG_RETURN_BOOL(false);
}
/* path_distance()
* This essentially does a cartesian product of the lsegs in the
@@ -1660,25 +1692,22 @@ path_distance(PG_FUNCTION_ARGS)
jprev = j - 1;
else
{
if (!p2->closed)
continue;
jprev = p2->npts - 1; /* include the closure segment */
}
statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
-
- tmp = DatumGetFloat8(DirectFunctionCall2(lseg_distance,
- LsegPGetDatum(&seg1),
- LsegPGetDatum(&seg2)));
- if (!have_min || tmp < min)
+ tmp = lseg_dt(&seg1, &seg2);
+ if (!have_min || float8_lt(tmp, min))
{
min = tmp;
have_min = true;
}
}
}
if (!have_min)
PG_RETURN_NULL();
@@ -1703,21 +1732,21 @@ path_length(PG_FUNCTION_ARGS)
if (i > 0)
iprev = i - 1;
else
{
if (!path->closed)
continue;
iprev = path->npts - 1; /* include the closure segment */
}
- result += point_dt(&path->p[iprev], &path->p[i]);
+ result = float8_pl(result, point_dt(&path->p[iprev], &path->p[i]));
}
PG_RETURN_FLOAT8(result);
}
/***********************************************************************
**
** Routines for 2D points.
**
***********************************************************************/
@@ -1772,31 +1801,20 @@ point_send(PG_FUNCTION_ARGS)
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, pt->x);
pq_sendfloat8(&buf, pt->y);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
static Point *
-point_construct(double x, double y)
-{
- Point *result = (Point *) palloc(sizeof(Point));
-
- result->x = x;
- result->y = y;
- return result;
-}
-
-
-static Point *
point_copy(Point *pt)
{
Point *result;
if (!PointerIsValid(pt))
return NULL;
result = (Point *) palloc(sizeof(Point));
result->x = pt->x;
@@ -1813,125 +1831,138 @@ point_copy(Point *pt)
* that results may, strictly speaking, be a lie (unless
* EPSILON = 0.0).
*---------------------------------------------------------*/
Datum
point_left(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(float8_lt(pt1->x, pt2->x));
}
Datum
point_right(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
+ PG_RETURN_BOOL(float8_gt(pt1->x, pt2->x));
}
Datum
point_above(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(float8_gt(pt1->y, pt2->y));
}
Datum
point_below(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
+ PG_RETURN_BOOL(float8_lt(pt1->y, pt2->y));
}
Datum
point_vert(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
+ PG_RETURN_BOOL(float8_eq(pt1->x, pt2->x));
}
Datum
point_horiz(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(float8_eq(pt1->y, pt2->y));
}
Datum
point_eq(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
+ PG_RETURN_BOOL(point_eq_internal(pt1, pt2));
+}
+
+static bool
+point_eq_internal(Point *pt1, Point *pt2)
+{
+ return float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y);
}
Datum
point_ne(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(FPne(pt1->x, pt2->x) || FPne(pt1->y, pt2->y));
+ PG_RETURN_BOOL(float8_ne(pt1->x, pt2->x) || float8_ne(pt1->y, pt2->y));
}
/*----------------------------------------------------------
* "Arithmetic" operators on points.
*---------------------------------------------------------*/
Datum
point_distance(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
- PG_RETURN_FLOAT8(HYPOT(pt1->x - pt2->x, pt1->y - pt2->y));
+ PG_RETURN_FLOAT8(point_dt(pt1, pt2));
}
double
point_dt(Point *pt1, Point *pt2)
{
+ float8 result;
+
+ result = float8_hypot(float8_mi(pt1->x, pt2->x), float8_mi(pt1->y, pt2->y));
+
#ifdef GEODEBUG
printf("point_dt- segment (%f,%f),(%f,%f) length is %f\n",
- pt1->x, pt1->y, pt2->x, pt2->y, HYPOT(pt1->x - pt2->x, pt1->y - pt2->y));
+ pt1->x, pt1->y, pt2->x, pt2->y, result);
#endif
- return HYPOT(pt1->x - pt2->x, pt1->y - pt2->y);
+
+ return result;
}
Datum
point_slope(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0);
Point *pt2 = PG_GETARG_POINT_P(1);
PG_RETURN_FLOAT8(point_sl(pt1, pt2));
}
double
point_sl(Point *pt1, Point *pt2)
{
- return (FPeq(pt1->x, pt2->x)
- ? (double) DBL_MAX
- : (pt1->y - pt2->y) / (pt1->x - pt2->x));
+ if (float8_eq(pt1->x, pt2->x))
+ return (double) DBL_MAX;
+ if (float8_eq(pt1->y, pt2->y))
+ return 0.0;
+ return float8_div(float8_mi(pt1->y, pt2->y), float8_mi(pt1->x, pt2->x));
}
/***********************************************************************
**
** Routines for 2D line segments.
**
***********************************************************************/
/*----------------------------------------------------------
@@ -2033,58 +2064,111 @@ lseg_length(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
PG_RETURN_FLOAT8(point_dt(&lseg->p[0], &lseg->p[1]));
}
/*----------------------------------------------------------
* Relative position routines.
*---------------------------------------------------------*/
-/*
- ** find intersection of the two lines, and see if it falls on
- ** both segments.
- */
Datum
lseg_intersect(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(lseg_intersect_internal(l1, l2));
+ PG_RETURN_BOOL(lseg_intersect_internal(NULL, l1, l2));
}
+/*
+ * Check if two line segments intersect using the vector cross product
+ * approach [1]
+ *
+ * It sets the intersection point to *result, if it is not NULL.
+ *
+ * NOTE: If the segments are collinear then we will find they are parallel
+ * and report "no intersection". This is a little weird, but since
+ * there's no *unique* intersection, maybe it's appropriate behavior.
+ * If this would need to be changed one day, is trivial to detect collinear
+ * lines with this algorithm.
+ *
+ * [1] http://stackoverflow.com/a/565282/786339
+ */
static bool
-lseg_intersect_internal(LSEG *l1, LSEG *l2)
+lseg_intersect_internal(Point *result, LSEG *l1, LSEG *l2)
{
- LINE ln;
- Point *interpt;
- bool retval;
+ float8 denominator,
+ ratio1,
+ ratio2;
+ Point p1,
+ p2,
+ p3;
- line_construct_pts(&ln, &l2->p[0], &l2->p[1]);
- interpt = interpt_sl(l1, &ln);
+ if (point_eq_internal(&l1->p[0], &l1->p[1]))
+ {
+ if (!on_ps_internal(&l1->p[0], l2))
+ return false;
- if (interpt != NULL && on_ps_internal(interpt, l2))
- retval = true; /* interpt on l1 and l2 */
- else
- retval = false;
- return retval;
+ if (result != NULL)
+ {
+ result->x = l1->p[0].x;
+ result->y = l1->p[0].y;
+ }
+ return true;
+ }
+ if (point_eq_internal(&l2->p[0], &l2->p[1]))
+ {
+ if (!on_ps_internal(&l2->p[0], l1))
+ return false;
+
+ if (result != NULL)
+ {
+ result->x = l2->p[0].x;
+ result->y = l2->p[0].y;
+ }
+ return true;
+ }
+
+ point_sub_internal(&p1, &l1->p[1], &l1->p[0]);
+ point_sub_internal(&p2, &l2->p[1], &l2->p[0]);
+ denominator = point_cross_product_internal(&p1, &p2);
+
+ /* Are they parallel? */
+ if (float8_eq(denominator, 0.0))
+ return false;
+
+ point_sub_internal(&p3, &l2->p[0], &l1->p[0]);
+ ratio1 = float8_div(point_cross_product_internal(&p3, &p1), denominator);
+ ratio2 = float8_div(point_cross_product_internal(&p3, &p2), denominator);
+
+ if (float8_lt(ratio1, 0.0) || float8_gt(ratio1, 1.0) ||
+ float8_lt(ratio2, 0.0) || float8_gt(ratio2, 1.0))
+ return false;
+
+ if (result != NULL)
+ {
+ point_mul_float8_internal(result, &p1, ratio2);
+ point_add_internal(result, &l1->p[0], result);
+ }
+
+ return true;
}
Datum
lseg_parallel(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(point_sl(&l1->p[0], &l1->p[1]),
- point_sl(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(float8_eq(point_sl(&l1->p[0], &l1->p[1]),
+ point_sl(&l2->p[0], &l2->p[1])));
}
/* lseg_perp()
* Determine if two line segments are perpendicular.
*
* This code did not get the correct answer for
* '((0,0),(0,1))'::lseg ?-| '((0,0),(1,0))'::lseg
* So, modified it to check explicitly for slope of vertical line
* returned by point_sl() and the results seem better.
* - thomas 1998-01-31
@@ -2096,107 +2180,105 @@ lseg_perp(PG_FUNCTION_ARGS)
LSEG *l2 = PG_GETARG_LSEG_P(1);
double m1,
m2;
m1 = point_sl(&(l1->p[0]), &(l1->p[1]));
m2 = point_sl(&(l2->p[0]), &(l2->p[1]));
#ifdef GEODEBUG
printf("lseg_perp- slopes are %g and %g\n", m1, m2);
#endif
- if (FPzero(m1))
- PG_RETURN_BOOL(FPeq(m2, DBL_MAX));
- else if (FPzero(m2))
- PG_RETURN_BOOL(FPeq(m1, DBL_MAX));
- PG_RETURN_BOOL(FPeq(m1 / m2, -1.0));
+ if (float8_eq(m1, 0.0))
+ PG_RETURN_BOOL(float8_eq(m2, DBL_MAX));
+ if (float8_eq(m2, 0.0))
+ PG_RETURN_BOOL(float8_eq(m1, DBL_MAX));
+ PG_RETURN_BOOL(float8_eq(m1, -m2));
}
Datum
lseg_vertical(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
+ PG_RETURN_BOOL(float8_eq(lseg->p[0].x, lseg->p[1].x));
}
Datum
lseg_horizontal(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
- PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
+ PG_RETURN_BOOL(float8_eq(lseg->p[0].y, lseg->p[1].y));
}
Datum
lseg_eq(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPeq(l1->p[0].x, l2->p[0].x) &&
- FPeq(l1->p[0].y, l2->p[0].y) &&
- FPeq(l1->p[1].x, l2->p[1].x) &&
- FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(point_eq_internal(&l1->p[0], &l2->p[0]) &&
+ point_eq_internal(&l1->p[1], &l2->p[1]));
}
Datum
lseg_ne(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(!FPeq(l1->p[0].x, l2->p[0].x) ||
- !FPeq(l1->p[0].y, l2->p[0].y) ||
- !FPeq(l1->p[1].x, l2->p[1].x) ||
- !FPeq(l1->p[1].y, l2->p[1].y));
+ PG_RETURN_BOOL(float8_ne(l1->p[0].x, l2->p[0].x) ||
+ float8_ne(l1->p[0].y, l2->p[0].y) ||
+ float8_ne(l1->p[1].x, l2->p[1].x) ||
+ float8_ne(l1->p[1].y, l2->p[1].y));
}
Datum
lseg_lt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(float8_lt(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_le(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(float8_le(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_gt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(float8_gt(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
Datum
lseg_ge(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
- PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1]),
- point_dt(&l2->p[0], &l2->p[1])));
+ PG_RETURN_BOOL(float8_ge(point_dt(&l1->p[0], &l1->p[1]),
+ point_dt(&l2->p[0], &l2->p[1])));
}
/*----------------------------------------------------------
* Line arithmetic routines.
*---------------------------------------------------------*/
/* lseg_distance -
* If two segments don't intersect, then the closest
* point will be from one of the endpoints to the other
@@ -2212,116 +2294,63 @@ lseg_distance(PG_FUNCTION_ARGS)
}
/* lseg_dt()
* Distance between two line segments.
* Must check both sets of endpoints to ensure minimum distance is found.
* - thomas 1998-02-01
*/
static double
lseg_dt(LSEG *l1, LSEG *l2)
{
- double result,
- d;
+ double result;
- if (lseg_intersect_internal(l1, l2))
+ if (lseg_intersect_internal(NULL, l1, l2))
return 0.0;
- d = dist_ps_internal(&l1->p[0], l2);
- result = d;
- d = dist_ps_internal(&l1->p[1], l2);
- result = Min(result, d);
- d = dist_ps_internal(&l2->p[0], l1);
- result = Min(result, d);
- d = dist_ps_internal(&l2->p[1], l1);
- result = Min(result, d);
+ result = dist_ps_internal(&l1->p[0], l2);
+ result = float8_min(result, dist_ps_internal(&l1->p[1], l2));
+ result = float8_min(result, dist_ps_internal(&l2->p[0], l1));
+ result = float8_min(result, dist_ps_internal(&l2->p[1], l1));
return result;
}
Datum
lseg_center(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
Point *result;
result = (Point *) palloc(sizeof(Point));
- result->x = (lseg->p[0].x + lseg->p[1].x) / 2.0;
- result->y = (lseg->p[0].y + lseg->p[1].y) / 2.0;
+ result->x = float8_div(float8_pl(lseg->p[0].x, lseg->p[1].x), 2.0);
+ result->y = float8_div(float8_pl(lseg->p[0].y, lseg->p[1].y), 2.0);
PG_RETURN_POINT_P(result);
}
-static Point *
-lseg_interpt_internal(LSEG *l1, LSEG *l2)
-{
- Point *result;
- LINE tmp1,
- tmp2;
-
- /*
- * Find the intersection of the appropriate lines, if any.
- */
- line_construct_pts(&tmp1, &l1->p[0], &l1->p[1]);
- line_construct_pts(&tmp2, &l2->p[0], &l2->p[1]);
- result = line_interpt_internal(&tmp1, &tmp2);
- if (!PointerIsValid(result))
- return NULL;
-
- /*
- * If the line intersection point isn't within l1 (or equivalently l2),
- * there is no valid segment intersection point at all.
- */
- if (!on_ps_internal(result, l1) ||
- !on_ps_internal(result, l2))
- {
- pfree(result);
- return NULL;
- }
-
- /*
- * If there is an intersection, then check explicitly for matching
- * endpoints since there may be rounding effects with annoying lsb
- * residue. - tgl 1997-07-09
- */
- if ((FPeq(l1->p[0].x, l2->p[0].x) && FPeq(l1->p[0].y, l2->p[0].y)) ||
- (FPeq(l1->p[0].x, l2->p[1].x) && FPeq(l1->p[0].y, l2->p[1].y)))
- {
- result->x = l1->p[0].x;
- result->y = l1->p[0].y;
- }
- else if ((FPeq(l1->p[1].x, l2->p[0].x) && FPeq(l1->p[1].y, l2->p[0].y)) ||
- (FPeq(l1->p[1].x, l2->p[1].x) && FPeq(l1->p[1].y, l2->p[1].y)))
- {
- result->x = l1->p[1].x;
- result->y = l1->p[1].y;
- }
-
- return result;
-}
-
/* lseg_interpt -
* Find the intersection point of two segments (if any).
*/
Datum
lseg_interpt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
Point *result;
- result = lseg_interpt_internal(l1, l2);
- if (!PointerIsValid(result))
- PG_RETURN_NULL();
+ result = (Point *) palloc(sizeof(Point));
- PG_RETURN_POINT_P(result);
+ if (lseg_intersect_internal(result, l1, l2))
+ PG_RETURN_POINT_P(result);
+ PG_RETURN_NULL();
}
/***********************************************************************
**
** Routines for position comparisons of differently-typed
** 2D objects.
**
***********************************************************************/
/*---------------------------------------------------------------------
@@ -2337,84 +2366,81 @@ dist_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
PG_RETURN_FLOAT8(dist_pl_internal(pt, line));
}
static double
dist_pl_internal(Point *pt, LINE *line)
{
- return fabs((line->A * pt->x + line->B * pt->y + line->C) /
- HYPOT(line->A, line->B));
+ return float8_div(fabs(point_on_line_value(pt, line)),
+ float8_hypot(line->A, line->B));
}
/*
* Distance from a point to a lseg
*/
Datum
dist_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
PG_RETURN_FLOAT8(dist_ps_internal(pt, lseg));
}
static double
dist_ps_internal(Point *pt, LSEG *lseg)
{
double m; /* slope of perp. */
LINE *ln;
- double result,
- tmpdist;
- Point *ip;
+ double result;
+ Point tmp;
/*
* Construct a line perpendicular to the input segment and through the
* input point
*/
- if (lseg->p[1].x == lseg->p[0].x)
- m = 0;
- else if (lseg->p[1].y == lseg->p[0].y)
+ if (float8_eq(lseg->p[0].x, lseg->p[1].x))
+ m = 0.0;
+ else if (float8_eq(lseg->p[0].y, lseg->p[1].y))
m = (double) DBL_MAX; /* slope is infinite */
else
- m = (lseg->p[0].x - lseg->p[1].x) / (lseg->p[1].y - lseg->p[0].y);
+ m = float8_div(-1.0, point_sl(&lseg->p[0], &lseg->p[1]));
ln = line_construct_pm(pt, m);
#ifdef GEODEBUG
printf("dist_ps- line is A=%g B=%g C=%g from (point) slope (%f,%f) %g\n",
ln->A, ln->B, ln->C, pt->x, pt->y, m);
#endif
/*
* Calculate distance to the line segment or to the nearest endpoint of
* the segment.
*/
/* intersection is on the line segment? */
- if ((ip = interpt_sl(lseg, ln)) != NULL)
+ if (lseg_intersect_line_internal(&tmp, lseg, ln))
{
/* yes, so use distance to the intersection point */
- result = point_dt(pt, ip);
+ result = point_dt(pt, &tmp);
#ifdef GEODEBUG
printf("dist_ps- distance is %f to intersection point is (%f,%f)\n",
- result, ip->x, ip->y);
+ result, tmp.x, tmp.y);
#endif
}
else
{
/* no, so use distance to the nearer endpoint */
- result = point_dt(pt, &lseg->p[0]);
- tmpdist = point_dt(pt, &lseg->p[1]);
- if (tmpdist < result)
- result = tmpdist;
+ result = float8_min(point_dt(pt, &lseg->p[0]),
+ point_dt(pt, &lseg->p[1]));
}
return result;
}
/*
* Distance from a point to a path
*/
Datum
dist_ppath(PG_FUNCTION_ARGS)
@@ -2452,21 +2478,21 @@ dist_ppath(PG_FUNCTION_ARGS)
iprev = i - 1;
else
{
if (!path->closed)
continue;
iprev = path->npts - 1; /* include the closure segment */
}
statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
tmp = dist_ps_internal(pt, &lseg);
- if (!have_min || tmp < result)
+ if (!have_min || float8_lt(tmp, result))
{
result = tmp;
have_min = true;
}
}
break;
}
PG_RETURN_FLOAT8(result);
}
@@ -2490,33 +2516,27 @@ dist_pb(PG_FUNCTION_ARGS)
}
/*
* Distance from a lseg to a line
*/
Datum
dist_sl(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
LINE *line = PG_GETARG_LINE_P(1);
- float8 result,
- d2;
+ float8 result;
- if (has_interpt_sl(lseg, line))
+ if (lseg_intersect_line_internal(NULL, lseg, line))
result = 0.0;
else
- {
- result = dist_pl_internal(&lseg->p[0], line);
- d2 = dist_pl_internal(&lseg->p[1], line);
- /* XXX shouldn't we take the min not max? */
- if (d2 > result)
- result = d2;
- }
+ result = float8_min(dist_pl_internal(&lseg->p[0], line),
+ dist_pl_internal(&lseg->p[1], line));
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a lseg to a box
*/
Datum
dist_sb(PG_FUNCTION_ARGS)
{
@@ -2560,23 +2580,23 @@ dist_lb(PG_FUNCTION_ARGS)
Datum
dist_cpoly(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
POLYGON *poly = PG_GETARG_POLYGON_P(1);
float8 result;
/* calculate distance to center, and subtract radius */
result = dist_ppoly_internal(&circle->center, poly);
- result -= circle->radius;
- if (result < 0)
- result = 0;
+ result = float8_mi(result, circle->radius);
+ if (float8_lt(result, 0.0))
+ result = 0.0;
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a point to a polygon
*/
Datum
dist_ppoly(PG_FUNCTION_ARGS)
{
@@ -2631,248 +2651,143 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
for (i = 0; (i < poly->npts - 1); i++)
{
seg.p[0].x = poly->p[i].x;
seg.p[0].y = poly->p[i].y;
seg.p[1].x = poly->p[i + 1].x;
seg.p[1].y = poly->p[i + 1].y;
d = dist_ps_internal(pt, &seg);
#ifdef GEODEBUG
printf("dist_ppoly_internal- segment %d distance is %f\n", (i + 1), d);
#endif
- if (d < result)
+ if (float8_lt(d, result))
result = d;
}
return result;
}
-/*---------------------------------------------------------------------
- * interpt_
- * Intersection point of objects.
- * We choose to ignore the "point" of intersection between
- * lines and boxes, since there are typically two.
- *-------------------------------------------------------------------*/
-
-/* Get intersection point of lseg and line; returns NULL if no intersection */
-static Point *
-interpt_sl(LSEG *lseg, LINE *line)
+/*
+ * Check if the line segment intersects with the line
+ *
+ * It sets the intersection point to *result, if it is not NULL.
+ */
+static bool
+lseg_intersect_line_internal(Point *result, LSEG *lseg, LINE *line)
{
+ Point interpt;
LINE tmp;
- Point *p;
line_construct_pts(&tmp, &lseg->p[0], &lseg->p[1]);
- p = line_interpt_internal(&tmp, line);
+
#ifdef GEODEBUG
- printf("interpt_sl- segment is (%.*g %.*g) (%.*g %.*g)\n",
+ printf("lseg_intersect_line- segment is (%.*g %.*g) (%.*g %.*g)\n",
DBL_DIG, lseg->p[0].x, DBL_DIG, lseg->p[0].y, DBL_DIG, lseg->p[1].x, DBL_DIG, lseg->p[1].y);
- printf("interpt_sl- segment becomes line A=%.*g B=%.*g C=%.*g\n",
+ printf("lseg_intersect_line- segment becomes line A=%.*g B=%.*g C=%.*g\n",
DBL_DIG, tmp.A, DBL_DIG, tmp.B, DBL_DIG, tmp.C);
#endif
- if (PointerIsValid(p))
- {
-#ifdef GEODEBUG
- printf("interpt_sl- intersection point is (%.*g %.*g)\n", DBL_DIG, p->x, DBL_DIG, p->y);
-#endif
- if (on_ps_internal(p, lseg))
- {
-#ifdef GEODEBUG
- printf("interpt_sl- intersection point is on segment\n");
-#endif
- }
- else
- p = NULL;
- }
- return p;
+ if (!line_intersect_internal(&interpt, &tmp, line))
+ return false;
+
+#ifdef GEODEBUG
+ printf("lseg_intersect_line- intersection point is (%.*g %.*g)\n", DBL_DIG, interpt.x, DBL_DIG, interpt.y);
+#endif
+
+ if (point_lseg_cmp(&interpt, lseg) != 0)
+ return false;
+
+#ifdef GEODEBUG
+ printf("lseg_intersect_line- intersection point is on segment\n");
+#endif
+
+ if (result != NULL)
+ *result = interpt;
+
+ return true;
}
-/* variant: just indicate if intersection point exists */
-static bool
-has_interpt_sl(LSEG *lseg, LINE *line)
-{
- Point *tmp;
-
- tmp = interpt_sl(lseg, line);
- if (tmp)
- return true;
- return false;
-}
/*---------------------------------------------------------------------
* close_
* Point of closest proximity between objects.
*-------------------------------------------------------------------*/
/* close_pl -
* The intersection point of a perpendicular of the line
* through the point.
*/
Datum
close_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
Point *result;
- LINE *tmp;
- double invm;
result = (Point *) palloc(sizeof(Point));
- if (FPzero(line->B)) /* vertical? */
+ if (float8_eq(line->B, 0.0)) /* vertical? */
{
- result->x = line->C;
+ result->x = float8_div(-line->C, line->A);
result->y = pt->y;
- PG_RETURN_POINT_P(result);
}
- if (FPzero(line->A)) /* horizontal? */
+ else if (float8_eq(line->A, 0.0)) /* horizontal? */
{
- result->x = pt->x;
- result->y = line->C;
- PG_RETURN_POINT_P(result);
+ result->x = float8_div(-line->C, line->B);
+ result->y = pt->y;
+ }
+ else
+ {
+ double slope;
+
+ /*
+ * Drop a perpendicular and find the intersection point
+ *
+ * We need to invert and flip the sign on the slope to get the
+ * perpendicular. We might lose some precision on the division, but
+ * it cannot be as much to turn the line.
+ */
+ slope = float8_div(line->B, line->A);
+ line_intersect_internal(result, line_construct_pm(pt, slope), line);
}
- /* drop a perpendicular and find the intersection point */
- /* invert and flip the sign on the slope to get a perpendicular */
- invm = line->B / line->A;
- tmp = line_construct_pm(pt, invm);
- result = line_interpt_internal(tmp, line);
- Assert(result != NULL);
PG_RETURN_POINT_P(result);
}
-/* close_ps()
- * Closest point on line segment to specified point.
- * Take the closest endpoint if the point is left, right,
- * above, or below the segment, otherwise find the intersection
- * point of the segment and its perpendicular through the point.
+/*
+ * Closest point on line segment to specified point
*
- * Some tricky code here, relying on boolean expressions
- * evaluating to only zero or one to use as an array index.
- * bug fixes by gthaker@atl.lmco.com; May 1, 1998
+ * We find the closest point to the line, and then test if the point is
+ * on the line segment. If it is not, we return the closest endpoint
+ * of the line segment.
*/
Datum
close_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
- Point *result = NULL;
- LINE *tmp;
- double invm;
- int xh,
- yh;
+ Point *result;
+ int order;
+ LINE tmp;
-#ifdef GEODEBUG
- printf("close_sp:pt->x %f pt->y %f\nlseg(0).x %f lseg(0).y %f lseg(1).x %f lseg(1).y %f\n",
- pt->x, pt->y, lseg->p[0].x, lseg->p[0].y,
- lseg->p[1].x, lseg->p[1].y);
-#endif
+ line_construct_pts(&tmp, &lseg->p[0], &lseg->p[1]);
+ result = DatumGetPointP(DirectFunctionCall2(close_pl,
+ PointPGetDatum(pt),
+ LsegPGetDatum(&tmp)));
+ order = point_lseg_cmp(result, lseg);
- /* xh (or yh) is the index of upper x( or y) end point of lseg */
- /* !xh (or !yh) is the index of lower x( or y) end point of lseg */
- xh = lseg->p[0].x < lseg->p[1].x;
- yh = lseg->p[0].y < lseg->p[1].y;
+ if (order < 0)
+ *result = lseg->p[0];
+ else if (order > 0)
+ *result = lseg->p[1];
- if (FPeq(lseg->p[0].x, lseg->p[1].x)) /* vertical? */
- {
-#ifdef GEODEBUG
- printf("close_ps- segment is vertical\n");
-#endif
- /* first check if point is below or above the entire lseg. */
- if (pt->y < lseg->p[!yh].y)
- result = point_copy(&lseg->p[!yh]); /* below the lseg */
- else if (pt->y > lseg->p[yh].y)
- result = point_copy(&lseg->p[yh]); /* above the lseg */
- if (result != NULL)
- PG_RETURN_POINT_P(result);
-
- /* point lines along (to left or right) of the vertical lseg. */
-
- result = (Point *) palloc(sizeof(Point));
- result->x = lseg->p[0].x;
- result->y = pt->y;
- PG_RETURN_POINT_P(result);
- }
- else if (FPeq(lseg->p[0].y, lseg->p[1].y)) /* horizontal? */
- {
-#ifdef GEODEBUG
- printf("close_ps- segment is horizontal\n");
-#endif
- /* first check if point is left or right of the entire lseg. */
- if (pt->x < lseg->p[!xh].x)
- result = point_copy(&lseg->p[!xh]); /* left of the lseg */
- else if (pt->x > lseg->p[xh].x)
- result = point_copy(&lseg->p[xh]); /* right of the lseg */
- if (result != NULL)
- PG_RETURN_POINT_P(result);
-
- /* point lines along (at top or below) the horiz. lseg. */
- result = (Point *) palloc(sizeof(Point));
- result->x = pt->x;
- result->y = lseg->p[0].y;
- PG_RETURN_POINT_P(result);
- }
-
- /*
- * vert. and horiz. cases are down, now check if the closest point is one
- * of the end points or someplace on the lseg.
- */
-
- invm = -1.0 / point_sl(&(lseg->p[0]), &(lseg->p[1]));
- tmp = line_construct_pm(&lseg->p[!yh], invm); /* lower edge of the
- * "band" */
- if (pt->y < (tmp->A * pt->x + tmp->C))
- { /* we are below the lower edge */
- result = point_copy(&lseg->p[!yh]); /* below the lseg, take lower
- * end pt */
-#ifdef GEODEBUG
- printf("close_ps below: tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- PG_RETURN_POINT_P(result);
- }
- tmp = line_construct_pm(&lseg->p[yh], invm); /* upper edge of the
- * "band" */
- if (pt->y > (tmp->A * pt->x + tmp->C))
- { /* we are below the lower edge */
- result = point_copy(&lseg->p[yh]); /* above the lseg, take higher
- * end pt */
-#ifdef GEODEBUG
- printf("close_ps above: tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- PG_RETURN_POINT_P(result);
- }
-
- /*
- * at this point the "normal" from point will hit lseg. The closest point
- * will be somewhere on the lseg
- */
- tmp = line_construct_pm(pt, invm);
-#ifdef GEODEBUG
- printf("close_ps- tmp A %f B %f C %f\n",
- tmp->A, tmp->B, tmp->C);
-#endif
- result = interpt_sl(lseg, tmp);
-
- /*
- * ordinarily we should always find an intersection point, but that could
- * fail in the presence of NaN coordinates, and perhaps even from simple
- * roundoff issues. Return a SQL NULL if so.
- */
- if (result == NULL)
- PG_RETURN_NULL();
-
-#ifdef GEODEBUG
- printf("close_ps- result.x %f result.y %f\n", result->x, result->y);
-#endif
PG_RETURN_POINT_P(result);
}
/* close_lseg()
* Closest point to l1 on l2.
*/
Datum
close_lseg(PG_FUNCTION_ARGS)
{
@@ -2880,38 +2795,39 @@ close_lseg(PG_FUNCTION_ARGS)
LSEG *l2 = PG_GETARG_LSEG_P(1);
Point *result = NULL;
Point point;
double dist;
double d;
d = dist_ps_internal(&l1->p[0], l2);
dist = d;
memcpy(&point, &l1->p[0], sizeof(Point));
- if ((d = dist_ps_internal(&l1->p[1], l2)) < dist)
+ d = dist_ps_internal(&l1->p[1], l2);
+ if (float8_lt(d, dist))
{
dist = d;
memcpy(&point, &l1->p[1], sizeof(Point));
}
- if (dist_ps_internal(&l2->p[0], l1) < dist)
+ if (float8_lt(dist_ps_internal(&l2->p[0], l1), dist))
{
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&l2->p[0]),
LsegPGetDatum(l1)));
memcpy(&point, result, sizeof(Point));
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&point),
LsegPGetDatum(l2)));
}
- if (dist_ps_internal(&l2->p[1], l1) < dist)
+ if (float8_lt(dist_ps_internal(&l2->p[1], l1), dist))
{
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&l2->p[1]),
LsegPGetDatum(l1)));
memcpy(&point, result, sizeof(Point));
result = DatumGetPointP(DirectFunctionCall2(close_ps,
PointPGetDatum(&point),
LsegPGetDatum(l2)));
}
@@ -2940,37 +2856,40 @@ close_pb(PG_FUNCTION_ARGS)
BoxPGetDatum(box))))
PG_RETURN_POINT_P(pt);
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&lseg, &box->low, &point);
dist = dist_ps_internal(pt, &lseg);
statlseg_construct(&seg, &box->high, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (float8_lt(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&seg, &box->low, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (float8_lt(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
statlseg_construct(&seg, &box->high, &point);
- if ((d = dist_ps_internal(pt, &seg)) < dist)
+ d = dist_ps_internal(pt, &seg);
+ if (float8_lt(d, dist))
{
dist = d;
memcpy(&lseg, &seg, sizeof(lseg));
}
PG_RETURN_DATUM(DirectFunctionCall2(close_ps,
PointPGetDatum(pt),
LsegPGetDatum(&lseg)));
}
@@ -2986,30 +2905,31 @@ close_pb(PG_FUNCTION_ARGS)
Datum
close_sl(PG_FUNCTION_ARGS)
{
#ifdef NOT_USED
LSEG *lseg = PG_GETARG_LSEG_P(0);
LINE *line = PG_GETARG_LINE_P(1);
Point *result;
float8 d1,
d2;
- result = interpt_sl(lseg, line);
- if (result)
+ result = (Point *) palloc(sizeof(Point));
+
+ if (lseg_intersect_line_internal(result, lseg, line))
PG_RETURN_POINT_P(result);
d1 = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- if (d1 < d2)
- result = point_copy(&lseg->p[0]);
+ if (float8_lt(d1, d2))
+ *result = lseg->p[0];
else
- result = point_copy(&lseg->p[1]);
+ *result = lseg->p[1];
PG_RETURN_POINT_P(result);
#endif
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function \"close_sl\" not implemented")));
PG_RETURN_NULL();
}
@@ -3019,30 +2939,31 @@ close_sl(PG_FUNCTION_ARGS)
*/
Datum
close_ls(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
Point *result;
float8 d1,
d2;
- result = interpt_sl(lseg, line);
- if (result)
+ result = (Point *) palloc(sizeof(Point));
+
+ if (lseg_intersect_line_internal(result, lseg, line))
PG_RETURN_POINT_P(result);
d1 = dist_pl_internal(&lseg->p[0], line);
d2 = dist_pl_internal(&lseg->p[1], line);
- if (d1 < d2)
- result = point_copy(&lseg->p[0]);
+ if (float8_lt(d1, d2))
+ *result = lseg->p[0];
else
- result = point_copy(&lseg->p[1]);
+ *result = lseg->p[1];
PG_RETURN_POINT_P(result);
}
/* close_sb()
* Closest point on or in box to line segment.
*/
Datum
close_sb(PG_FUNCTION_ARGS)
{
@@ -3065,37 +2986,40 @@ close_sb(PG_FUNCTION_ARGS)
LsegPGetDatum(lseg)));
}
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&bseg, &box->low, &point);
dist = lseg_dt(lseg, &bseg);
statlseg_construct(&seg, &box->high, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (float8_lt(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&seg, &box->low, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (float8_le(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
statlseg_construct(&seg, &box->high, &point);
- if ((d = lseg_dt(lseg, &seg)) < dist)
+ d = lseg_dt(lseg, &seg);
+ if (float8_le(d, dist))
{
dist = d;
memcpy(&bseg, &seg, sizeof(bseg));
}
/* OK, we now have the closest line segment on the box boundary */
PG_RETURN_DATUM(DirectFunctionCall2(close_lseg,
LsegPGetDatum(lseg),
LsegPGetDatum(&bseg)));
}
@@ -3123,62 +3047,141 @@ close_lb(PG_FUNCTION_ARGS)
/* on_pl -
* Does the point satisfy the equation?
*/
Datum
on_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LINE *line = PG_GETARG_LINE_P(1);
- PG_RETURN_BOOL(FPzero(line->A * pt->x + line->B * pt->y + line->C));
+ PG_RETURN_BOOL(point_on_line_value(pt, line) == 0);
}
-
/* on_ps -
- * Determine colinearity by detecting a triangle inequality.
- * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
+ * Does the point on line segment?
*/
Datum
on_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
LSEG *lseg = PG_GETARG_LSEG_P(1);
PG_RETURN_BOOL(on_ps_internal(pt, lseg));
}
static bool
on_ps_internal(Point *pt, LSEG *lseg)
{
- return FPeq(point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1]),
- point_dt(&lseg->p[0], &lseg->p[1]));
+ LINE tmp;
+
+ /*
+ * We are first comparing the point with the line segment, even though
+ * this comparison makes no sense when the point is not on the line. The
+ * reason of it is that this check is cheaper than constructing the line
+ * and validating the line equation.
+ */
+ if (point_lseg_cmp(pt, lseg) != 0)
+ return false;
+
+ line_construct_pts(&tmp, &lseg->p[0], &lseg->p[1]);
+ if (float8_ne(point_on_line_value(pt, &tmp), 0.0))
+ return false;
+
+ return true;
+}
+
+/*
+ * Calculate the line equation for a point
+ *
+ * This returns the result of the line equation Ax + By + C. The result
+ * needs to be 0 for the point to be on the line.
+ */
+static double
+point_on_line_value(Point *pt, LINE *line)
+{
+ return float8_pl(float8_pl(float8_mul(line->A, pt->x),
+ float8_mul(line->B, pt->y)), line->C);
+}
+
+/*
+ * Compare a point with a line segment
+ *
+ * This function is useful when the point and line segment are collinear.
+ * It returns
+ *
+ * * -1, if the point extends the first edge of the segment
+ * * 0, if the point is on the segment
+ * * 1, if the point extends the last edge of the segment.
+ *
+ * This check is not as trivial as checking if a point is inside a box,
+ * because the edges of the line segments are not normalised. We are
+ * doing the same checks for both x and y coordinates, to be correct
+ * when one of the coordinates of the point is on one edge of the segment.
+ */
+static int
+point_lseg_cmp(Point *pt, LSEG *lseg)
+{
+ if (float8_lt(lseg->p[0].x, lseg->p[1].x))
+ {
+ if (float8_lt(pt->x, lseg->p[0].x))
+ return -1;
+ if (float8_gt(pt->x, lseg->p[1].x))
+ return 1;
+ }
+ else if (float8_gt(lseg->p[0].x, lseg->p[1].x))
+ {
+ if (float8_gt(pt->x, lseg->p[0].x))
+ return -1;
+ if (float8_lt(pt->x, lseg->p[1].x))
+ return 1;
+ }
+ else if (float8_le(lseg->p[0].y, lseg->p[1].y))
+ {
+ if (float8_lt(pt->y, lseg->p[0].y))
+ return -1;
+ if (float8_gt(pt->y, lseg->p[1].y))
+ return 1;
+ }
+ else
+ {
+ if (float8_gt(pt->y, lseg->p[0].y))
+ return -1;
+ if (float8_lt(pt->y, lseg->p[1].y))
+ return 1;
+ }
+
+ return 0;
}
Datum
on_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
BOX *box = PG_GETARG_BOX_P(1);
- PG_RETURN_BOOL(pt->x <= box->high.x && pt->x >= box->low.x &&
- pt->y <= box->high.y && pt->y >= box->low.y);
+ PG_RETURN_BOOL(float8_le(pt->x, box->high.x) &&
+ float8_ge(pt->x, box->low.x) &&
+ float8_le(pt->y, box->high.y) &&
+ float8_ge(pt->y, box->low.y));
}
Datum
box_contain_pt(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *pt = PG_GETARG_POINT_P(1);
- PG_RETURN_BOOL(pt->x <= box->high.x && pt->x >= box->low.x &&
- pt->y <= box->high.y && pt->y >= box->low.y);
+ PG_RETURN_BOOL(float8_le(pt->x, box->high.x) &&
+ float8_ge(pt->x, box->low.x) &&
+ float8_le(pt->y, box->high.y) &&
+ float8_ge(pt->y, box->low.y));
}
/* on_ppath -
* Whether a point lies within (on) a polyline.
* If open, we have to (groan) check each segment.
* (uses same algorithm as for point intersecting segment - tgl 1997-07-09)
* If closed, we use the old O(n) ray method for point-in-polygon.
* The ray is horizontal, from pt out to the right.
* Each segment that crosses the ray counts as an
* intersection; note that an endpoint or edge may touch
@@ -3196,22 +3199,22 @@ on_ppath(PG_FUNCTION_ARGS)
b;
/*-- OPEN --*/
if (!path->closed)
{
n = path->npts - 1;
a = point_dt(pt, &path->p[0]);
for (i = 0; i < n; i++)
{
b = point_dt(pt, &path->p[i + 1]);
- if (FPeq(a + b,
- point_dt(&path->p[i], &path->p[i + 1])))
+ if (float8_eq(float8_pl(a, b),
+ point_dt(&path->p[i], &path->p[i + 1])))
PG_RETURN_BOOL(true);
a = b;
}
PG_RETURN_BOOL(false);
}
/*-- CLOSED --*/
PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
}
@@ -3247,21 +3250,21 @@ on_sb(PG_FUNCTION_ARGS)
* inter_
* Whether one object intersects another.
*-------------------------------------------------------------------*/
Datum
inter_sl(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
LINE *line = PG_GETARG_LINE_P(1);
- PG_RETURN_BOOL(has_interpt_sl(lseg, line));
+ PG_RETURN_BOOL(lseg_intersect_line_internal(NULL, lseg, line));
}
/* inter_sb()
* Do line segment and box intersect?
*
* Segment completely inside box counts as intersection.
* If you want only segments crossing box boundaries,
* try converting box to path first.
*
* Optimize for non-intersection by checking for box intersection first.
@@ -3269,97 +3272,91 @@ inter_sl(PG_FUNCTION_ARGS)
*/
Datum
inter_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
BOX *box = PG_GETARG_BOX_P(1);
BOX lbox;
LSEG bseg;
Point point;
- lbox.low.x = Min(lseg->p[0].x, lseg->p[1].x);
- lbox.low.y = Min(lseg->p[0].y, lseg->p[1].y);
- lbox.high.x = Max(lseg->p[0].x, lseg->p[1].x);
- lbox.high.y = Max(lseg->p[0].y, lseg->p[1].y);
+ lbox.low.x = float8_min(lseg->p[0].x, lseg->p[1].x);
+ lbox.low.y = float8_min(lseg->p[0].y, lseg->p[1].y);
+ lbox.high.x = float8_max(lseg->p[0].x, lseg->p[1].x);
+ lbox.high.y = float8_max(lseg->p[0].y, lseg->p[1].y);
/* nothing close to overlap? then not going to intersect */
if (!box_ov(&lbox, box))
PG_RETURN_BOOL(false);
/* an endpoint of segment is inside box? then clearly intersects */
if (DatumGetBool(DirectFunctionCall2(on_pb,
PointPGetDatum(&lseg->p[0]),
BoxPGetDatum(box))) ||
DatumGetBool(DirectFunctionCall2(on_pb,
PointPGetDatum(&lseg->p[1]),
BoxPGetDatum(box))))
PG_RETURN_BOOL(true);
/* pairwise check lseg intersections */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&bseg, &box->low, &point);
- if (lseg_intersect_internal(&bseg, lseg))
+ if (lseg_intersect_internal(NULL, &bseg, lseg))
PG_RETURN_BOOL(true);
statlseg_construct(&bseg, &box->high, &point);
- if (lseg_intersect_internal(&bseg, lseg))
+ if (lseg_intersect_internal(NULL, &bseg, lseg))
PG_RETURN_BOOL(true);
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&bseg, &box->low, &point);
- if (lseg_intersect_internal(&bseg, lseg))
+ if (lseg_intersect_internal(NULL, &bseg, lseg))
PG_RETURN_BOOL(true);
statlseg_construct(&bseg, &box->high, &point);
- if (lseg_intersect_internal(&bseg, lseg))
+ if (lseg_intersect_internal(NULL, &bseg, lseg))
PG_RETURN_BOOL(true);
/* if we dropped through, no two segs intersected */
PG_RETURN_BOOL(false);
}
/* inter_lb()
* Do line and box intersect?
*/
Datum
inter_lb(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0);
BOX *box = PG_GETARG_BOX_P(1);
LSEG bseg;
- Point p1,
- p2;
/* pairwise check lseg intersections */
- p1.x = box->low.x;
- p1.y = box->low.y;
- p2.x = box->low.x;
- p2.y = box->high.y;
- statlseg_construct(&bseg, &p1, &p2);
- if (has_interpt_sl(&bseg, line))
+ bseg.p[0].x = box->low.x;
+ bseg.p[0].y = box->low.y;
+ bseg.p[1].x = box->low.x;
+ bseg.p[1].y = box->high.y;
+ if (lseg_intersect_line_internal(NULL, &bseg, line))
PG_RETURN_BOOL(true);
- p1.x = box->high.x;
- p1.y = box->high.y;
- statlseg_construct(&bseg, &p1, &p2);
- if (has_interpt_sl(&bseg, line))
+ bseg.p[0].x = box->high.x;
+ bseg.p[0].y = box->high.y;
+ if (lseg_intersect_line_internal(NULL, &bseg, line))
PG_RETURN_BOOL(true);
- p2.x = box->high.x;
- p2.y = box->low.y;
- statlseg_construct(&bseg, &p1, &p2);
- if (has_interpt_sl(&bseg, line))
+ bseg.p[1].x = box->high.x;
+ bseg.p[1].y = box->low.y;
+ if (lseg_intersect_line_internal(NULL, &bseg, line))
PG_RETURN_BOOL(true);
- p1.x = box->low.x;
- p1.y = box->low.y;
- statlseg_construct(&bseg, &p1, &p2);
- if (has_interpt_sl(&bseg, line))
+ bseg.p[0].x = box->low.x;
+ bseg.p[0].y = box->low.y;
+ if (lseg_intersect_line_internal(NULL, &bseg, line))
PG_RETURN_BOOL(true);
/* if we dropped through, no intersection */
PG_RETURN_BOOL(false);
}
/*------------------------------------------------------------------
* The following routines define a data type and operator class for
* POLYGONS .... Part of which (the polygon's bounding box) is built on
* top of the BOX data type.
@@ -3378,27 +3375,27 @@ make_bound_box(POLYGON *poly)
y1,
x2,
y2;
if (poly->npts > 0)
{
x2 = x1 = poly->p[0].x;
y2 = y1 = poly->p[0].y;
for (i = 1; i < poly->npts; i++)
{
- if (poly->p[i].x < x1)
+ if (float8_lt(poly->p[i].x, x1))
x1 = poly->p[i].x;
- if (poly->p[i].x > x2)
+ if (float8_gt(poly->p[i].x, x2))
x2 = poly->p[i].x;
- if (poly->p[i].y < y1)
+ if (float8_lt(poly->p[i].y, y1))
y1 = poly->p[i].y;
- if (poly->p[i].y > y2)
+ if (float8_gt(poly->p[i].y, y2))
y2 = poly->p[i].y;
}
box_fill(&(poly->boundbox), x1, x2, y1, y2);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot create bounding box for empty polygon")));
}
@@ -3526,21 +3523,21 @@ poly_send(PG_FUNCTION_ARGS)
* the right most point of A left of the left most point
* of B?
*-------------------------------------------------------*/
Datum
poly_left(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.x < polyb->boundbox.low.x;
+ result = float8_lt(polya->boundbox.high.x, polyb->boundbox.low.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3549,21 +3546,21 @@ poly_left(PG_FUNCTION_ARGS)
* the right most point of A at or left of the right most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overleft(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.x <= polyb->boundbox.high.x;
+ result = float8_le(polya->boundbox.high.x, polyb->boundbox.high.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3572,21 +3569,21 @@ poly_overleft(PG_FUNCTION_ARGS)
* the left most point of A right of the right most point
* of B?
*-------------------------------------------------------*/
Datum
poly_right(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.x > polyb->boundbox.high.x;
+ result = float8_gt(polya->boundbox.low.x, polyb->boundbox.high.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3595,21 +3592,21 @@ poly_right(PG_FUNCTION_ARGS)
* the left most point of A at or right of the left most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overright(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.x >= polyb->boundbox.low.x;
+ result = float8_ge(polya->boundbox.low.x, polyb->boundbox.low.x);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3618,21 +3615,21 @@ poly_overright(PG_FUNCTION_ARGS)
* the upper most point of A below the lower most point
* of B?
*-------------------------------------------------------*/
Datum
poly_below(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.y < polyb->boundbox.low.y;
+ result = float8_lt(polya->boundbox.high.y, polyb->boundbox.low.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3641,21 +3638,21 @@ poly_below(PG_FUNCTION_ARGS)
* the upper most point of A at or below the upper most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overbelow(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.high.y <= polyb->boundbox.high.y;
+ result = float8_le(polya->boundbox.high.y, polyb->boundbox.high.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3664,21 +3661,21 @@ poly_overbelow(PG_FUNCTION_ARGS)
* the lower most point of A above the upper most point
* of B?
*-------------------------------------------------------*/
Datum
poly_above(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.y > polyb->boundbox.high.y;
+ result = float8_gt(polya->boundbox.low.y, polyb->boundbox.high.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3687,21 +3684,21 @@ poly_above(PG_FUNCTION_ARGS)
* the lower most point of A at or above the lower most point
* of B?
*-------------------------------------------------------*/
Datum
poly_overabove(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
- result = polya->boundbox.low.y >= polyb->boundbox.low.y;
+ result = float8_ge(polya->boundbox.low.y, polyb->boundbox.low.y);
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
PG_RETURN_BOOL(result);
}
@@ -3739,21 +3736,21 @@ poly_same(PG_FUNCTION_ARGS)
*-----------------------------------------------------------------*/
Datum
poly_overlap(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
/* Quick check by bounding box */
result = (polya->npts > 0 && polyb->npts > 0 &&
- box_ov(&polya->boundbox, &polyb->boundbox)) ? true : false;
+ box_ov(&polya->boundbox, &polyb->boundbox));
/*
* Brute-force algorithm - try to find intersected edges, if so then
* polygons are overlapped else check is one polygon inside other or not
* by testing single point of them.
*/
if (result)
{
int ia,
ib;
@@ -3768,34 +3765,33 @@ poly_overlap(PG_FUNCTION_ARGS)
{
/* Second point of polya's edge is a current one */
sa.p[1] = polya->p[ia];
/* Init first of polyb's edge with last point */
sb.p[0] = polyb->p[polyb->npts - 1];
for (ib = 0; ib < polyb->npts && result == false; ib++)
{
sb.p[1] = polyb->p[ib];
- result = lseg_intersect_internal(&sa, &sb);
+ result = lseg_intersect_internal(NULL, &sa, &sb);
sb.p[0] = sb.p[1];
}
/*
* move current endpoint to the first point of next edge
*/
sa.p[0] = sa.p[1];
}
if (result == false)
{
- result = (point_inside(polya->p, polyb->npts, polyb->p)
- ||
+ result = (point_inside(polya->p, polyb->npts, polyb->p) ||
point_inside(polyb->p, polya->npts, polya->p));
}
}
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0);
PG_FREE_IF_COPY(polyb, 1);
@@ -3815,27 +3811,26 @@ poly_overlap(PG_FUNCTION_ARGS)
static bool
touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
{
/* point a is on s, b is not */
LSEG t;
t.p[0] = *a;
t.p[1] = *b;
-#define POINTEQ(pt1, pt2) (FPeq((pt1)->x, (pt2)->x) && FPeq((pt1)->y, (pt2)->y))
- if (POINTEQ(a, s->p))
+ if (point_eq_internal(a, s->p))
{
if (on_ps_internal(s->p + 1, &t))
return lseg_inside_poly(b, s->p + 1, poly, start);
}
- else if (POINTEQ(a, s->p + 1))
+ else if (point_eq_internal(a, s->p + 1))
{
if (on_ps_internal(s->p, &t))
return lseg_inside_poly(b, s->p, poly, start);
}
else if (on_ps_internal(s->p, &t))
{
return lseg_inside_poly(b, s->p, poly, start);
}
else if (on_ps_internal(s->p + 1, &t))
{
@@ -3858,65 +3853,64 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
int i;
bool res = true,
intersection = false;
t.p[0] = *a;
t.p[1] = *b;
s.p[0] = poly->p[(start == 0) ? (poly->npts - 1) : (start - 1)];
for (i = start; i < poly->npts && res; i++)
{
- Point *interpt;
+ Point interpt;
CHECK_FOR_INTERRUPTS();
s.p[1] = poly->p[i];
if (on_ps_internal(t.p, &s))
{
if (on_ps_internal(t.p + 1, &s))
return true; /* t is contained by s */
/* Y-cross */
res = touched_lseg_inside_poly(t.p, t.p + 1, &s, poly, i + 1);
}
else if (on_ps_internal(t.p + 1, &s))
{
/* Y-cross */
res = touched_lseg_inside_poly(t.p + 1, t.p, &s, poly, i + 1);
}
- else if ((interpt = lseg_interpt_internal(&t, &s)) != NULL)
+ else if (lseg_intersect_internal(&interpt, &t, &s))
{
/*
* segments are X-crossing, go to check each subsegment
*/
intersection = true;
- res = lseg_inside_poly(t.p, interpt, poly, i + 1);
+ res = lseg_inside_poly(t.p, &interpt, poly, i + 1);
if (res)
- res = lseg_inside_poly(t.p + 1, interpt, poly, i + 1);
- pfree(interpt);
+ res = lseg_inside_poly(t.p + 1, &interpt, poly, i + 1);
}
s.p[0] = s.p[1];
}
if (res && !intersection)
{
Point p;
/*
* if X-intersection wasn't found then check central point of tested
* segment. In opposite case we already check all subsegments
*/
- p.x = (t.p[0].x + t.p[1].x) / 2.0;
- p.y = (t.p[0].y + t.p[1].y) / 2.0;
+ p.x = float8_div(float8_pl(t.p[0].x, t.p[1].x), 2.0);
+ p.y = float8_div(float8_pl(t.p[0].y, t.p[1].y), 2.0);
res = point_inside(&p, poly->npts, poly->p);
}
return res;
}
/*-----------------------------------------------------------------
* Determine if polygon A contains polygon B.
*-----------------------------------------------------------------*/
@@ -4016,92 +4010,121 @@ poly_distance(PG_FUNCTION_ARGS)
**
** Routines for 2D points.
**
***********************************************************************/
Datum
construct_point(PG_FUNCTION_ARGS)
{
float8 x = PG_GETARG_FLOAT8(0);
float8 y = PG_GETARG_FLOAT8(1);
+ Point *result;
- PG_RETURN_POINT_P(point_construct(x, y));
+ result = (Point *) palloc(sizeof(Point));
+
+ result->x = x;
+ result->y = y;
+ PG_RETURN_POINT_P(result);
}
Datum
point_add(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
-
- result = (Point *) palloc(sizeof(Point));
-
- result->x = (p1->x + p2->x);
- result->y = (p1->y + p2->y);
+ Point *result = (Point *) palloc(sizeof(Point));
+ point_add_internal(result, p1, p2);
PG_RETURN_POINT_P(result);
}
+static void
+point_add_internal(Point *result, Point *pt1, Point *pt2)
+{
+ result->x = float8_pl(pt1->x, pt2->x);
+ result->y = float8_pl(pt1->y, pt2->y);
+}
+
Datum
point_sub(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
-
- result = (Point *) palloc(sizeof(Point));
-
- result->x = (p1->x - p2->x);
- result->y = (p1->y - p2->y);
+ Point *result = (Point *) palloc(sizeof(Point));
+ point_sub_internal(result, p1, p2);
PG_RETURN_POINT_P(result);
}
+static void
+point_sub_internal(Point *result, Point *pt1, Point *pt2)
+{
+ result->x = float8_mi(pt1->x, pt2->x);
+ result->y = float8_mi(pt1->y, pt2->y);
+}
+
Datum
point_mul(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
-
- result = (Point *) palloc(sizeof(Point));
-
- result->x = (p1->x * p2->x) - (p1->y * p2->y);
- result->y = (p1->x * p2->y) + (p1->y * p2->x);
+ Point *result = (Point *) palloc(sizeof(Point));
+ point_mul_internal(result, p1, p2);
PG_RETURN_POINT_P(result);
}
+static void
+point_mul_internal(Point *result, Point *pt1, Point *pt2)
+{
+ result->x = float8_mi(float8_mul(pt1->x, pt2->x),
+ float8_mul(pt1->y, pt2->y));
+ result->y = float8_pl(float8_mul(pt1->x, pt2->y),
+ float8_mul(pt1->y, pt2->x));
+}
+
Datum
point_div(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0);
Point *p2 = PG_GETARG_POINT_P(1);
- Point *result;
- double div;
-
- result = (Point *) palloc(sizeof(Point));
-
- div = (p2->x * p2->x) + (p2->y * p2->y);
-
- if (div == 0.0)
- ereport(ERROR,
- (errcode(ERRCODE_DIVISION_BY_ZERO),
- errmsg("division by zero")));
-
- result->x = ((p1->x * p2->x) + (p1->y * p2->y)) / div;
- result->y = ((p2->x * p1->y) - (p2->y * p1->x)) / div;
+ Point *result = (Point *) palloc(sizeof(Point));
+ point_div_internal(result, p1, p2);
PG_RETURN_POINT_P(result);
}
+static void
+point_div_internal(Point *result, Point *pt1, Point *pt2)
+{
+ float8 div;
+
+ div = float8_pl(float8_mul(pt2->x, pt2->x), float8_mul(pt2->y, pt2->y));
+ result->x = float8_div(float8_pl(float8_mul(pt1->x, pt2->x),
+ float8_mul(pt1->y, pt2->y)), div);
+ result->y = float8_div(float8_mi(float8_mul(pt1->y, pt2->x),
+ float8_mul(pt1->x, pt2->y)), div);
+}
+
+static void
+point_mul_float8_internal(Point *result, Point *pt, float8 num)
+{
+ result->x = float8_mul(pt->x, num);
+ result->y = float8_mul(pt->y, num);
+}
+
+static float8
+point_cross_product_internal(Point *pt1, Point *pt2)
+{
+ return float8_mi(float8_mul(pt1->x, pt2->y), float8_mul(pt1->y, pt2->x));
+}
+
/***********************************************************************
**
** Routines for 2D boxes.
**
***********************************************************************/
Datum
points_box(PG_FUNCTION_ARGS)
{
@@ -4110,115 +4133,99 @@ points_box(PG_FUNCTION_ARGS)
PG_RETURN_BOX_P(box_construct(p1->x, p2->x, p1->y, p2->y));
}
Datum
box_add(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
- PG_RETURN_BOX_P(box_construct((box->high.x + p->x),
- (box->low.x + p->x),
- (box->high.y + p->y),
- (box->low.y + p->y)));
+ PG_RETURN_BOX_P(box_construct(float8_pl(box->high.x, p->x),
+ float8_pl(box->low.x, p->x),
+ float8_pl(box->high.y, p->y),
+ float8_pl(box->low.y, p->y)));
}
Datum
box_sub(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
- PG_RETURN_BOX_P(box_construct((box->high.x - p->x),
- (box->low.x - p->x),
- (box->high.y - p->y),
- (box->low.y - p->y)));
+ PG_RETURN_BOX_P(box_construct(float8_mi(box->high.x, p->x),
+ float8_mi(box->low.x, p->x),
+ float8_mi(box->high.y, p->y),
+ float8_mi(box->low.y, p->y)));
}
Datum
box_mul(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
BOX *result;
- Point *high,
- *low;
+ Point high,
+ low;
- high = DatumGetPointP(DirectFunctionCall2(point_mul,
- PointPGetDatum(&box->high),
- PointPGetDatum(p)));
- low = DatumGetPointP(DirectFunctionCall2(point_mul,
- PointPGetDatum(&box->low),
- PointPGetDatum(p)));
+ point_mul_internal(&high, &box->high, p);
+ point_mul_internal(&low, &box->low, p);
- result = box_construct(high->x, low->x, high->y, low->y);
+ result = box_construct(high.x, low.x, high.y, low.y);
PG_RETURN_BOX_P(result);
}
Datum
box_div(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
Point *p = PG_GETARG_POINT_P(1);
BOX *result;
- Point *high,
- *low;
+ Point high,
+ low;
- high = DatumGetPointP(DirectFunctionCall2(point_div,
- PointPGetDatum(&box->high),
- PointPGetDatum(p)));
- low = DatumGetPointP(DirectFunctionCall2(point_div,
- PointPGetDatum(&box->low),
- PointPGetDatum(p)));
+ point_div_internal(&high, &box->high, p);
+ point_div_internal(&low, &box->low, p);
- result = box_construct(high->x, low->x, high->y, low->y);
+ result = box_construct(high.x, low.x, high.y, low.y);
PG_RETURN_BOX_P(result);
}
/*
* Convert point to empty box
*/
Datum
point_box(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0);
- BOX *box;
- box = (BOX *) palloc(sizeof(BOX));
-
- box->high.x = pt->x;
- box->low.x = pt->x;
- box->high.y = pt->y;
- box->low.y = pt->y;
-
- PG_RETURN_BOX_P(box);
+ PG_RETURN_BOX_P(box_construct(pt->x, pt->x, pt->y, pt->y));
}
/*
* Smallest bounding box that includes both of the given boxes
*/
Datum
boxes_bound_box(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0),
*box2 = PG_GETARG_BOX_P(1),
*container;
container = (BOX *) palloc(sizeof(BOX));
- container->high.x = Max(box1->high.x, box2->high.x);
- container->low.x = Min(box1->low.x, box2->low.x);
- container->high.y = Max(box1->high.y, box2->high.y);
- container->low.y = Min(box1->low.y, box2->low.y);
+ container->high.x = float8_max(box1->high.x, box2->high.x);
+ container->low.x = float8_min(box1->low.x, box2->low.x);
+ container->high.y = float8_max(box1->high.y, box2->high.y);
+ container->low.y = float8_min(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(container);
}
/***********************************************************************
**
** Routines for 2D paths.
**
***********************************************************************/
@@ -4275,82 +4282,72 @@ path_add(PG_FUNCTION_ARGS)
* Translation operators.
*/
Datum
path_add_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
int i;
for (i = 0; i < path->npts; i++)
- {
- path->p[i].x += point->x;
- path->p[i].y += point->y;
- }
+ point_add_internal(&path->p[i], &path->p[i], point);
PG_RETURN_PATH_P(path);
}
Datum
path_sub_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
int i;
for (i = 0; i < path->npts; i++)
- {
- path->p[i].x -= point->x;
- path->p[i].y -= point->y;
- }
+ point_sub_internal(&path->p[i], &path->p[i], point);
PG_RETURN_PATH_P(path);
}
/* path_mul_pt()
* Rotation and scaling operators.
*/
Datum
path_mul_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
- Point *p;
int i;
+ Point tmp;
for (i = 0; i < path->npts; i++)
{
- p = DatumGetPointP(DirectFunctionCall2(point_mul,
- PointPGetDatum(&path->p[i]),
- PointPGetDatum(point)));
- path->p[i].x = p->x;
- path->p[i].y = p->y;
+ point_mul_internal(&tmp, &path->p[i], point);
+ path->p[i].x = tmp.x;
+ path->p[i].y = tmp.y;
}
PG_RETURN_PATH_P(path);
}
Datum
path_div_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0);
Point *point = PG_GETARG_POINT_P(1);
- Point *p;
int i;
+ Point tmp;
for (i = 0; i < path->npts; i++)
{
- p = DatumGetPointP(DirectFunctionCall2(point_div,
- PointPGetDatum(&path->p[i]),
- PointPGetDatum(point)));
- path->p[i].x = p->x;
- path->p[i].y = p->y;
+ point_div_internal(&path->p[i], &path->p[i], point);
+ path->p[i].x = tmp.x;
+ path->p[i].y = tmp.y;
}
PG_RETURN_PATH_P(path);
}
Datum
path_center(PG_FUNCTION_ARGS)
{
#ifdef NOT_USED
@@ -4489,21 +4486,21 @@ poly_path(PG_FUNCTION_ARGS)
/*
* Never overflows: the old size fit in MaxAllocSize, and the new size is
* smaller by a small constant.
*/
size = offsetof(PATH, p) +sizeof(path->p[0]) * poly->npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = poly->npts;
- path->closed = TRUE;
+ path->closed = true;
/* prevent instability in unused pad bytes */
path->dummy = 0;
for (i = 0; i < poly->npts; i++)
{
path->p[i].x = poly->p[i].x;
path->p[i].y = poly->p[i].y;
}
PG_RETURN_PATH_P(path);
@@ -4547,30 +4544,29 @@ circle_in(PG_FUNCTION_ARGS)
if (*cp == LDELIM)
s = cp;
}
pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle", str);
if (*s == DELIM)
s++;
circle->radius = single_decode(s, &s, "circle", str);
- if (circle->radius < 0)
+ if (float8_lt(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"circle", str)));
while (depth > 0)
{
- if ((*s == RDELIM)
- || ((*s == RDELIM_C) && (depth == 1)))
+ if ((*s == RDELIM) || ((*s == RDELIM_C) && (depth == 1)))
{
depth--;
s++;
while (isspace((unsigned char) *s))
s++;
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
@@ -4615,21 +4611,21 @@ circle_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
CIRCLE *circle;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
circle->center.x = pq_getmsgfloat8(buf);
circle->center.y = pq_getmsgfloat8(buf);
circle->radius = pq_getmsgfloat8(buf);
- if (circle->radius < 0)
+ if (float8_lt(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid radius in external \"circle\" value")));
PG_RETURN_CIRCLE_P(circle);
}
/*
* circle_send - converts circle to binary format
*/
@@ -4653,310 +4649,290 @@ circle_send(PG_FUNCTION_ARGS)
*---------------------------------------------------------*/
/* circles identical?
*/
Datum
circle_same(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle1->radius, circle2->radius) &&
- FPeq(circle1->center.x, circle2->center.x) &&
- FPeq(circle1->center.y, circle2->center.y));
+ PG_RETURN_BOOL(float8_eq(circle1->radius, circle2->radius) &&
+ float8_eq(circle1->center.x, circle2->center.x) &&
+ float8_eq(circle1->center.y, circle2->center.y));
}
/* circle_overlap - does circle1 overlap circle2?
*/
Datum
circle_overlap(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
- circle1->radius + circle2->radius));
+ PG_RETURN_BOOL(float8_le(point_dt(&circle1->center, &circle2->center),
+ float8_pl(circle1->radius, circle2->radius)));
}
/* circle_overleft - is the right edge of circle1 at or left of
* the right edge of circle2?
*/
Datum
circle_overleft(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((circle1->center.x + circle1->radius),
- (circle2->center.x + circle2->radius)));
+ PG_RETURN_BOOL(float8_le(float8_pl(circle1->center.x, circle1->radius),
+ float8_pl(circle2->center.x, circle2->radius)));
}
/* circle_left - is circle1 strictly left of circle2?
*/
Datum
circle_left(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt((circle1->center.x + circle1->radius),
- (circle2->center.x - circle2->radius)));
+ PG_RETURN_BOOL(float8_lt(float8_pl(circle1->center.x, circle1->radius),
+ float8_mi(circle2->center.x, circle2->radius)));
}
/* circle_right - is circle1 strictly right of circle2?
*/
Datum
circle_right(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt((circle1->center.x - circle1->radius),
- (circle2->center.x + circle2->radius)));
+ PG_RETURN_BOOL(float8_gt(float8_mi(circle1->center.x, circle1->radius),
+ float8_pl(circle2->center.x, circle2->radius)));
}
/* circle_overright - is the left edge of circle1 at or right of
* the left edge of circle2?
*/
Datum
circle_overright(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge((circle1->center.x - circle1->radius),
- (circle2->center.x - circle2->radius)));
+ PG_RETURN_BOOL(float8_ge(float8_mi(circle1->center.x, circle1->radius),
+ float8_mi(circle2->center.x, circle2->radius)));
}
/* circle_contained - is circle1 contained by circle2?
*/
Datum
circle_contained(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle1->radius), circle2->radius));
+ PG_RETURN_BOOL(float8_le(point_dt(&circle1->center, &circle2->center),
+ float8_mi(circle2->radius, circle1->radius)));
}
/* circle_contain - does circle1 contain circle2?
*/
Datum
circle_contain(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((point_dt(&circle1->center, &circle2->center) + circle2->radius), circle1->radius));
+ PG_RETURN_BOOL(float8_le(point_dt(&circle1->center, &circle2->center),
+ float8_mi(circle1->radius, circle2->radius)));
}
/* circle_below - is circle1 strictly below circle2?
*/
Datum
circle_below(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt((circle1->center.y + circle1->radius),
- (circle2->center.y - circle2->radius)));
+ PG_RETURN_BOOL(float8_lt(float8_pl(circle1->center.y, circle1->radius),
+ float8_mi(circle2->center.y, circle2->radius)));
}
/* circle_above - is circle1 strictly above circle2?
*/
Datum
circle_above(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt((circle1->center.y - circle1->radius),
- (circle2->center.y + circle2->radius)));
+ PG_RETURN_BOOL(float8_gt(float8_mi(circle1->center.y, circle1->radius),
+ float8_pl(circle2->center.y, circle2->radius)));
}
/* circle_overbelow - is the upper edge of circle1 at or below
* the upper edge of circle2?
*/
Datum
circle_overbelow(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle((circle1->center.y + circle1->radius),
- (circle2->center.y + circle2->radius)));
+ PG_RETURN_BOOL(float8_le(float8_pl(circle1->center.y, circle1->radius),
+ float8_pl(circle2->center.y, circle2->radius)));
}
/* circle_overabove - is the lower edge of circle1 at or above
* the lower edge of circle2?
*/
Datum
circle_overabove(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge((circle1->center.y - circle1->radius),
- (circle2->center.y - circle2->radius)));
+ PG_RETURN_BOOL(float8_ge(float8_mi(circle1->center.y, circle1->radius),
+ float8_mi(circle2->center.y, circle2->radius)));
}
/* circle_relop - is area(circle1) relop area(circle2), within
* our accuracy constraint?
*/
Datum
circle_eq(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(float8_eq(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ne(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(float8_ne(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_lt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(float8_lt(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_gt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(float8_gt(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_le(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(float8_le(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ge(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
- PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
+ PG_RETURN_BOOL(float8_ge(circle_ar(circle1), circle_ar(circle2)));
}
/*----------------------------------------------------------
* "Arithmetic" operators on circles.
*---------------------------------------------------------*/
-static CIRCLE *
-circle_copy(CIRCLE *circle)
-{
- CIRCLE *result;
-
- if (!PointerIsValid(circle))
- return NULL;
-
- result = (CIRCLE *) palloc(sizeof(CIRCLE));
- memcpy((char *) result, (char *) circle, sizeof(CIRCLE));
- return result;
-}
-
-
/* circle_add_pt()
* Translation operator.
*/
Datum
circle_add_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
CIRCLE *result;
- result = circle_copy(circle);
+ result = (CIRCLE *) palloc(sizeof(CIRCLE));
- result->center.x += point->x;
- result->center.y += point->y;
+ point_add_internal(&result->center, &circle->center, point);
+ result->radius = circle->radius;
PG_RETURN_CIRCLE_P(result);
}
Datum
circle_sub_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
CIRCLE *result;
- result = circle_copy(circle);
+ result = (CIRCLE *) palloc(sizeof(CIRCLE));
- result->center.x -= point->x;
- result->center.y -= point->y;
+ point_sub_internal(&result->center, &circle->center, point);
+ result->radius = circle->radius;
PG_RETURN_CIRCLE_P(result);
}
/* circle_mul_pt()
* Rotation and scaling operators.
*/
Datum
circle_mul_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
CIRCLE *result;
- Point *p;
- result = circle_copy(circle);
+ result = (CIRCLE *) palloc(sizeof(CIRCLE));
- p = DatumGetPointP(DirectFunctionCall2(point_mul,
- PointPGetDatum(&circle->center),
- PointPGetDatum(point)));
- result->center.x = p->x;
- result->center.y = p->y;
- result->radius *= HYPOT(point->x, point->y);
+ point_mul_internal(&result->center, &circle->center, point);
+ result->radius = float8_mul(circle->radius,
+ float8_hypot(point->x, point->y));
PG_RETURN_CIRCLE_P(result);
}
Datum
circle_div_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
CIRCLE *result;
- Point *p;
- result = circle_copy(circle);
+ result = (CIRCLE *) palloc(sizeof(CIRCLE));
- p = DatumGetPointP(DirectFunctionCall2(point_div,
- PointPGetDatum(&circle->center),
- PointPGetDatum(point)));
- result->center.x = p->x;
- result->center.y = p->y;
- result->radius /= HYPOT(point->x, point->y);
+ point_div_internal(&result->center, &circle->center, point);
+ result->radius = float8_div(circle->radius,
+ float8_hypot(point->x, point->y));
PG_RETURN_CIRCLE_P(result);
}
/* circle_area - returns the area of the circle.
*/
Datum
circle_area(PG_FUNCTION_ARGS)
{
@@ -4966,21 +4942,21 @@ circle_area(PG_FUNCTION_ARGS)
}
/* circle_diameter - returns the diameter of the circle.
*/
Datum
circle_diameter(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
- PG_RETURN_FLOAT8(2 * circle->radius);
+ PG_RETURN_FLOAT8(float8_mul(circle->radius, 2.0));
}
/* circle_radius - returns the radius of the circle.
*/
Datum
circle_radius(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
@@ -4991,106 +4967,104 @@ circle_radius(PG_FUNCTION_ARGS)
/* circle_distance - returns the distance between
* two circles.
*/
Datum
circle_distance(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
float8 result;
- result = point_dt(&circle1->center, &circle2->center)
- - (circle1->radius + circle2->radius);
- if (result < 0)
- result = 0;
+ result = float8_mi(point_dt(&circle1->center, &circle2->center),
+ float8_pl(circle1->radius, circle2->radius));
+ if (float8_lt(result, 0.0))
+ result = 0.0;
+
PG_RETURN_FLOAT8(result);
}
Datum
circle_contain_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
double d;
d = point_dt(&circle->center, point);
- PG_RETURN_BOOL(d <= circle->radius);
+ PG_RETURN_BOOL(float8_le(d, circle->radius));
}
Datum
pt_contained_circle(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
double d;
d = point_dt(&circle->center, point);
- PG_RETURN_BOOL(d <= circle->radius);
+ PG_RETURN_BOOL(float8_le(d, circle->radius));
}
/* dist_pc - returns the distance between
* a point and a circle.
*/
Datum
dist_pc(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
float8 result;
- result = point_dt(point, &circle->center) - circle->radius;
- if (result < 0)
- result = 0;
+ result = float8_mi(point_dt(point, &circle->center), circle->radius);
+ if (float8_lt(result, 0.0))
+ result = 0.0;
+
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a circle to a point
*/
Datum
dist_cpoint(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
Point *point = PG_GETARG_POINT_P(1);
- float8 result;
+ double result;
+
+ result = float8_mi(point_dt(point, &circle->center), circle->radius);
+ if (float8_lt(result, 0.0))
+ result = 0.0;
- result = point_dt(point, &circle->center) - circle->radius;
- if (result < 0)
- result = 0;
PG_RETURN_FLOAT8(result);
}
/* circle_center - returns the center point of the circle.
*/
Datum
circle_center(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
- Point *result;
- result = (Point *) palloc(sizeof(Point));
- result->x = circle->center.x;
- result->y = circle->center.y;
-
- PG_RETURN_POINT_P(result);
+ PG_RETURN_POINT_P(point_copy(&circle->center));
}
/* circle_ar - returns the area of the circle.
*/
static double
circle_ar(CIRCLE *circle)
{
- return M_PI * (circle->radius * circle->radius);
+ return float8_mul(float8_mul(circle->radius, circle->radius), M_PI);
}
/*----------------------------------------------------------
* Conversion operators.
*---------------------------------------------------------*/
Datum
cr_circle(PG_FUNCTION_ARGS)
{
@@ -5108,64 +5082,63 @@ cr_circle(PG_FUNCTION_ARGS)
}
Datum
circle_box(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
BOX *box;
double delta;
box = (BOX *) palloc(sizeof(BOX));
+ delta = float8_div(circle->radius, sqrt(2.0));
- delta = circle->radius / sqrt(2.0);
-
- box->high.x = circle->center.x + delta;
- box->low.x = circle->center.x - delta;
- box->high.y = circle->center.y + delta;
- box->low.y = circle->center.y - delta;
+ box->high.x = float8_pl(circle->center.x, delta);
+ box->low.x = float8_mi(circle->center.x, delta);
+ box->high.y = float8_pl(circle->center.y, delta);
+ box->low.y = float8_mi(circle->center.y, delta);
PG_RETURN_BOX_P(box);
}
/* box_circle()
* Convert a box to a circle.
*/
Datum
box_circle(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
CIRCLE *circle;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
- circle->center.x = (box->high.x + box->low.x) / 2;
- circle->center.y = (box->high.y + box->low.y) / 2;
+ circle->center.x = float8_div(float8_pl(box->high.x, box->low.x), 2.0);
+ circle->center.y = float8_div(float8_pl(box->high.y, box->low.y), 2.0);
circle->radius = point_dt(&circle->center, &box->high);
PG_RETURN_CIRCLE_P(circle);
}
Datum
circle_poly(PG_FUNCTION_ARGS)
{
int32 npts = PG_GETARG_INT32(0);
CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
POLYGON *poly;
int base_size,
size;
int i;
double angle;
double anglestep;
- if (FPzero(circle->radius))
+ if (float8_eq(circle->radius, 0.0))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert circle with radius zero to polygon")));
if (npts < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must request at least 2 points")));
base_size = sizeof(poly->p[0]) * npts;
@@ -5174,27 +5147,30 @@ circle_poly(PG_FUNCTION_ARGS)
/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
- anglestep = (2.0 * M_PI) / npts;
+ anglestep = float8_div(2.0 * M_PI, npts);
for (i = 0; i < npts; i++)
{
- angle = i * anglestep;
- poly->p[i].x = circle->center.x - (circle->radius * cos(angle));
- poly->p[i].y = circle->center.y + (circle->radius * sin(angle));
+ angle = float8_mul(anglestep, i);
+
+ poly->p[i].x = float8_mi(circle->center.x,
+ float8_mul(circle->radius, cos(angle)));
+ poly->p[i].y = float8_pl(circle->center.y,
+ float8_mul(circle->radius, sin(angle)));
}
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
}
/* poly_circle - convert polygon to circle
*
* XXX This algorithm should use weighted means of line segments
@@ -5213,29 +5189,30 @@ poly_circle(PG_FUNCTION_ARGS)
errmsg("cannot convert empty polygon to circle")));
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
circle->center.x = 0;
circle->center.y = 0;
circle->radius = 0;
for (i = 0; i < poly->npts; i++)
{
- circle->center.x += poly->p[i].x;
- circle->center.y += poly->p[i].y;
+ circle->center.x = float8_pl(circle->center.x, poly->p[i].x);
+ circle->center.y = float8_pl(circle->center.y, poly->p[i].y);
}
- circle->center.x /= poly->npts;
- circle->center.y /= poly->npts;
+ circle->center.x = float8_div(circle->center.x, poly->npts);
+ circle->center.y = float8_div(circle->center.y, poly->npts);
for (i = 0; i < poly->npts; i++)
- circle->radius += point_dt(&poly->p[i], &circle->center);
- circle->radius /= poly->npts;
+ circle->radius = float8_pl(circle->radius,
+ point_dt(&poly->p[i], &circle->center));
+ circle->radius = float8_div(circle->radius, poly->npts);
PG_RETURN_CIRCLE_P(circle);
}
/***********************************************************************
**
** Private routines for multiple types.
**
***********************************************************************/
@@ -5263,31 +5240,31 @@ point_inside(Point *p, int npts, Point *plist)
int i = 0;
double x,
y;
int cross,
total_cross = 0;
if (npts <= 0)
return 0;
/* compute first polygon point relative to single point */
- x0 = plist[0].x - p->x;
- y0 = plist[0].y - p->y;
+ x0 = float8_mi(plist[0].x, p->x);
+ y0 = float8_mi(plist[0].y, p->y);
prev_x = x0;
prev_y = y0;
/* loop over polygon points and aggregate total_cross */
for (i = 1; i < npts; i++)
{
/* compute next polygon point relative to single point */
- x = plist[i].x - p->x;
- y = plist[i].y - p->y;
+ x = float8_mi(plist[i].x, p->x);
+ y = float8_mi(plist[i].y, p->y);
/* compute previous to current point crossing */
if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
return 2;
total_cross += cross;
prev_x = x;
prev_y = y;
}
@@ -5310,126 +5287,127 @@ point_inside(Point *p, int npts, Point *plist)
* Wow, that is one confusing API, but it is used above, and when summed,
* can tell is if a point is in a polygon.
*/
static int
lseg_crossing(double x, double y, double prev_x, double prev_y)
{
double z;
int y_sign;
- if (FPzero(y))
+ if (float8_eq(y, 0.0))
{ /* y == 0, on X axis */
- if (FPzero(x)) /* (x,y) is (0,0)? */
+ if (float8_eq(x, 0.0)) /* (x,y) is (0,0)? */
return POINT_ON_POLYGON;
- else if (FPgt(x, 0))
+ else if (float8_gt(x, 0.0))
{ /* x > 0 */
- if (FPzero(prev_y)) /* y and prev_y are zero */
+ if (float8_eq(prev_y, 0.0)) /* y and prev_y are zero */
/* prev_x > 0? */
- return FPgt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
- return FPlt(prev_y, 0) ? 1 : -1;
+ return float8_gt(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
+ return float8_lt(prev_y, 0.0) ? 1 : -1;
}
else
{ /* x < 0, x not on positive X axis */
- if (FPzero(prev_y))
+ if (float8_eq(prev_y, 0.0))
/* prev_x < 0? */
- return FPlt(prev_x, 0) ? 0 : POINT_ON_POLYGON;
+ return float8_lt(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
return 0;
}
}
else
{ /* y != 0 */
/* compute y crossing direction from previous point */
- y_sign = FPgt(y, 0) ? 1 : -1;
+ y_sign = float8_gt(y, 0.0) ? 1 : -1;
- if (FPzero(prev_y))
+ if (float8_eq(prev_y, 0.0))
/* previous point was on X axis, so new point is either off or on */
- return FPlt(prev_x, 0) ? 0 : y_sign;
- else if (FPgt(y_sign * prev_y, 0))
+ return prev_x < 0.0 ? 0 : y_sign;
+ else if ((y_sign < 0 && float8_lt(prev_y, 0.0)) ||
+ (y_sign > 0 && float8_gt(prev_y, 0.0)))
/* both above or below X axis */
return 0; /* same sign */
else
{ /* y and prev_y cross X-axis */
- if (FPge(x, 0) && FPgt(prev_x, 0))
+ if (float8_ge(x, 0.0) && float8_gt(prev_x, 0.0))
/* both non-negative so cross positive X-axis */
return 2 * y_sign;
- if (FPlt(x, 0) && FPle(prev_x, 0))
+ if (float8_lt(x, 0.0) && float8_le(prev_x, 0.0))
/* both non-positive so do not cross positive X-axis */
return 0;
/* x and y cross axises, see URL above point_inside() */
- z = (x - prev_x) * y - (y - prev_y) * x;
- if (FPzero(z))
+ z = float8_mi(float8_mul(float8_mi(x, prev_x), y),
+ float8_mul(float8_mi(y, prev_y), x));
+ if (float8_eq(z, 0.0))
return POINT_ON_POLYGON;
- return FPgt((y_sign * z), 0) ? 0 : 2 * y_sign;
+ return float8_gt((y_sign * z), 0) ? 0 : 2 * y_sign;
}
}
}
static bool
plist_same(int npts, Point *p1, Point *p2)
{
int i,
ii,
j;
/* find match for first point */
for (i = 0; i < npts; i++)
{
- if ((FPeq(p2[i].x, p1[0].x))
- && (FPeq(p2[i].y, p1[0].y)))
+ if (float8_eq(p2[i].x, p1[0].x) && float8_eq(p2[i].y, p1[0].y))
{
/* match found? then look forward through remaining points */
for (ii = 1, j = i + 1; ii < npts; ii++, j++)
{
if (j >= npts)
j = 0;
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (float8_ne(p2[j].x, p1[ii].x) ||
+ float8_ne(p2[j].y, p1[ii].y))
{
#ifdef GEODEBUG
printf("plist_same- %d failed forward match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after forward match\n", ii, npts);
#endif
if (ii == npts)
- return TRUE;
+ return true;
/* match not found forwards? then look backwards */
for (ii = 1, j = i - 1; ii < npts; ii++, j--)
{
if (j < 0)
j = (npts - 1);
- if ((!FPeq(p2[j].x, p1[ii].x))
- || (!FPeq(p2[j].y, p1[ii].y)))
+ if (float8_ne(p2[j].x, p1[ii].x) ||
+ float8_ne(p2[j].y, p1[ii].y))
{
#ifdef GEODEBUG
printf("plist_same- %d failed reverse match with %d\n", j, ii);
#endif
break;
}
}
#ifdef GEODEBUG
printf("plist_same- ii = %d/%d after reverse match\n", ii, npts);
#endif
if (ii == npts)
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/*-------------------------------------------------------------------------
* Determine the hypotenuse.
*
* If required, x and y are swapped to make x the larger number. The
* traditional formula of x^2+y^2 is rearranged to factor x outside the
* sqrt. This allows computation of the hypotenuse for significantly
* larger values, and with a higher precision than when using the naive
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index 54f4f0e..b18e1d9 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -75,21 +75,21 @@
#include "access/spgist.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/geo_decls.h"
/*
* Comparator for qsort
*
* We don't need to use the floating point macros in here, because this
- * is going only going to be used in a place to effect the performance
+ * is only going to be used in a place to effect the performance
* of the index, not the correctness.
*/
static int
compareDoubles(const void *a, const void *b)
{
double x = *(double *) a;
double y = *(double *) b;
if (x == y)
return 0;
@@ -120,30 +120,30 @@ typedef struct
* The quadrant is 8 bit unsigned integer with 4 least bits in use.
* This function accepts BOXes as input. They are not casted to
* RangeBoxes, yet. All 4 bits are set by comparing a corner of the box.
* This makes 16 quadrants in total.
*/
static uint8
getQuadrant(BOX *centroid, BOX *inBox)
{
uint8 quadrant = 0;
- if (inBox->low.x > centroid->low.x)
+ if (float8_gt(inBox->low.x, centroid->low.x))
quadrant |= 0x8;
- if (inBox->high.x > centroid->high.x)
+ if (float8_gt(inBox->high.x, centroid->high.x))
quadrant |= 0x4;
- if (inBox->low.y > centroid->low.y)
+ if (float8_gt(inBox->low.y, centroid->low.y))
quadrant |= 0x2;
- if (inBox->high.y > centroid->high.y)
+ if (float8_gt(inBox->high.y, centroid->high.y))
quadrant |= 0x1;
return quadrant;
}
/*
* Get RangeBox using BOX
*
* We are turning the BOX to our structures to emphasize their function
* of representing points in 4D space. It also is more convenient to
@@ -224,80 +224,80 @@ nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant)
else
next_rect_box->range_box_y.right.high = centroid->right.high;
return next_rect_box;
}
/* Can any range from range_box overlap with this argument? */
static bool
overlap2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->low) &&
- FPle(range_box->left.low, query->high);
+ return float8_ge(range_box->right.high, query->low) &&
+ float8_lt(range_box->left.low, query->high);
}
/* Can any rectangle from rect_box overlap with this argument? */
static bool
overlap4D(RectBox *rect_box, RangeBox *query)
{
return overlap2D(&rect_box->range_box_x, &query->left) &&
- overlap2D(&rect_box->range_box_y, &query->right);
+ overlap2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box contain this argument? */
static bool
contain2D(RangeBox *range_box, Range *query)
{
- return FPge(range_box->right.high, query->high) &&
- FPle(range_box->left.low, query->low);
+ return float8_ge(range_box->right.high, query->high) &&
+ float8_le(range_box->left.low, query->low);
}
/* Can any rectangle from rect_box contain this argument? */
static bool
contain4D(RectBox *rect_box, RangeBox *query)
{
return contain2D(&rect_box->range_box_x, &query->left) &&
- contain2D(&rect_box->range_box_y, &query->right);
+ contain2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box be contained by this argument? */
static bool
contained2D(RangeBox *range_box, Range *query)
{
- return FPle(range_box->left.low, query->high) &&
- FPge(range_box->left.high, query->low) &&
- FPle(range_box->right.low, query->high) &&
- FPge(range_box->right.high, query->low);
+ return float8_le(range_box->left.low, query->high) &&
+ float8_ge(range_box->left.high, query->low) &&
+ float8_le(range_box->right.low, query->high) &&
+ float8_ge(range_box->right.high, query->low);
}
/* Can any rectangle from rect_box be contained by this argument? */
static bool
contained4D(RectBox *rect_box, RangeBox *query)
{
return contained2D(&rect_box->range_box_x, &query->left) &&
- contained2D(&rect_box->range_box_y, &query->right);
+ contained2D(&rect_box->range_box_y, &query->right);
}
/* Can any range from range_box to be lower than this argument? */
static bool
lower2D(RangeBox *range_box, Range *query)
{
- return FPlt(range_box->left.low, query->low) &&
- FPlt(range_box->right.low, query->low);
+ return float8_lt(range_box->left.low, query->low) &&
+ float8_lt(range_box->right.low, query->low);
}
/* Can any range from range_box to be higher than this argument? */
static bool
higher2D(RangeBox *range_box, Range *query)
{
- return FPgt(range_box->left.high, query->high) &&
- FPgt(range_box->right.high, query->high);
+ return float8_gt(range_box->left.high, query->high) &&
+ float8_gt(range_box->right.high, query->high);
}
/* Can any rectangle from rect_box be left of this argument? */
static bool
left4D(RectBox *rect_box, RangeBox *query)
{
return lower2D(&rect_box->range_box_x, &query->left);
}
/* Can any rectangle from rect_box does not extend the right of this argument? */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 3a9287f..9977854 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -1,65 +1,33 @@
/*-------------------------------------------------------------------------
*
* geo_decls.h - Declarations for various 2D constructs.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/geo_decls.h
*
- * NOTE
- * These routines do *not* use the float types from adt/.
- *
- * XXX These routines were not written by a numerical analyst.
- *
* XXX I have made some attempt to flesh out the operators
* and data types. There are still some more to do. - tgl 97/04/19
*
*-------------------------------------------------------------------------
*/
#ifndef GEO_DECLS_H
#define GEO_DECLS_H
#include <math.h>
#include "fmgr.h"
#include "utils/float.h"
-/*--------------------------------------------------------------------
- * Useful floating point utilities and constants.
- *-------------------------------------------------------------------*/
-
-
-#define EPSILON 1.0E-06
-
-#ifdef EPSILON
-#define FPzero(A) (fabs(A) <= EPSILON)
-#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
-#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
-#define FPlt(A,B) ((B) - (A) > EPSILON)
-#define FPle(A,B) ((A) - (B) <= EPSILON)
-#define FPgt(A,B) ((A) - (B) > EPSILON)
-#define FPge(A,B) ((B) - (A) <= EPSILON)
-#else
-#define FPzero(A) ((A) == 0)
-#define FPeq(A,B) ((A) == (B))
-#define FPne(A,B) ((A) != (B))
-#define FPlt(A,B) ((A) < (B))
-#define FPle(A,B) ((A) <= (B))
-#define FPgt(A,B) ((A) > (B))
-#define FPge(A,B) ((A) >= (B))
-#endif
-
-#define HYPOT(A, B) pg_hypot(A, B)
-
/*---------------------------------------------------------------------
* Point - (x,y)
*-------------------------------------------------------------------*/
typedef struct
{
double x,
y;
} Point;
@@ -435,11 +403,28 @@ Datum spg_box_quad_inner_consistent(PG_FUNCTION_ARGS);
Datum spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS);
/* geo_selfuncs.c */
extern Datum areasel(PG_FUNCTION_ARGS);
extern Datum areajoinsel(PG_FUNCTION_ARGS);
extern Datum positionsel(PG_FUNCTION_ARGS);
extern Datum positionjoinsel(PG_FUNCTION_ARGS);
extern Datum contsel(PG_FUNCTION_ARGS);
extern Datum contjoinsel(PG_FUNCTION_ARGS);
+
+/*
+ * Hypotenuse route with underflow and overflow checking
+ */
+static inline float8
+float8_hypot(float8 val1, float8 val2)
+{
+ float8 result;
+
+ result = pg_hypot(val1, val2);
+ check_float8_val(result, isinf(val1) || isinf(val2),
+ val1 == 0.0 && val2 == 0.0);
+ Assert(isinf(result) || result >= 0.0);
+
+ return result;
+}
+
#endif /* GEO_DECLS_H */
diff --git a/src/test/regress/expected/point.out b/src/test/regress/expected/point.out
index bfc0962..6319652 100644
--- a/src/test/regress/expected/point.out
+++ b/src/test/regress/expected/point.out
@@ -249,49 +249,49 @@ SELECT '' AS three, p1.f1 AS point1, p2.f1 AS point2, (p1.f1 <-> p2.f1) AS dista
CREATE TEMP TABLE point_gist_tbl(f1 point);
INSERT INTO point_gist_tbl SELECT '(0,0)' FROM generate_series(0,1000);
CREATE INDEX point_gist_tbl_index ON point_gist_tbl USING gist (f1);
INSERT INTO point_gist_tbl VALUES ('(0.0000009,0.0000009)');
SET enable_seqscan TO true;
SET enable_indexscan TO false;
SET enable_bitmapscan TO false;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
SET enable_seqscan TO false;
SET enable_indexscan TO true;
SET enable_bitmapscan TO true;
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000009,0.0000009)'::point;
count
-------
- 1002
+ 1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 <@ '(0.0000009,0.0000009),(0.0000009,0.0000009)'::box;
count
-------
1
(1 row)
SELECT COUNT(*) FROM point_gist_tbl WHERE f1 ~= '(0.0000018,0.0000018)'::point;
count
-------
- 1
+ 0
(1 row)
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index 8780353..5d9fc44 100644
--- a/src/test/regress/expected/select_views.out
+++ b/src/test/regress/expected/select_views.out
@@ -217,21 +217,20 @@ SELECT * FROM street;
Logan Ct | [(-122.0053,37.492),(-122.0061,37.484)] | Oakland
Magnolia St | [(-122.0971,37.5),(-122.0962,37.484)] | Oakland
Mandalay Road | [(-122.2322,37.397),(-122.2321,37.403)] | Lafayette
Marin Ave | [(-122.2741,37.894),(-122.272,37.901)] | Berkeley
Martin Luther King Jr Way | [(-122.2712,37.608),(-122.2711,37.599)] | Berkeley
Mattos Dr | [(-122.0005,37.502),(-122.000898,37.49683)] | Oakland
Maubert Ave | [(-122.1114,37.009),(-122.1096,37.995)] | Oakland
Maubert Ave | [(-122.1114,37.009),(-122.1096,37.995)] | Berkeley
McClure Ave | [(-122.1431,37.001),(-122.1436,37.998)] | Oakland
McClure Ave | [(-122.1431,37.001),(-122.1436,37.998)] | Berkeley
- Medlar Dr | [(-122.0627,37.378),(-122.0625,37.375)] | Oakland
Mildred Ct | [(-122.0002,37.388),(-121.9998,37.386)] | Oakland
Miller Road | [(-122.0902,37.645),(-122.0865,37.545)] | Berkeley
Miramar Ave | [(-122.1009,37.025),(-122.099089,37.03209)] | Oakland
Mission Blvd | [(-121.918886,37),(-121.9194,37.976),(-121.9198,37.975)] | Oakland
Mission Blvd | [(-121.918886,37),(-121.9194,37.976),(-121.9198,37.975)] | Oakland
Mission Blvd | [(-122.0006,37.896),(-121.9989,37.88)] | Oakland
Mission Blvd | [(-122.0006,37.896),(-121.9989,37.88)] | Berkeley
Moores Ave | [(-122.0087,37.301),(-122.0094,37.292)] | Oakland
National Ave | [(-122.1192,37.5),(-122.1281,37.489)] | Oakland
Navajo Ct | [(-121.8779,37.901),(-121.8783,37.9)] | Oakland
@@ -331,38 +330,35 @@ SELECT * FROM street;
19th Ave | [(-122.2366,37.897),(-122.2359,37.905)] | Berkeley
1st St | [(-121.75508,37.89294),(-121.753581,37.90031)] | Oakland
5th St | [(-122.278,37),(-122.2792,37.005),(-122.2803,37.009)] | Lafayette
5th St | [(-122.296,37.615),(-122.2953,37.598)] | Berkeley
82nd Ave | [(-122.1695,37.596),(-122.1681,37.603)] | Berkeley
85th Ave | [(-122.1877,37.466),(-122.186,37.476)] | Oakland
89th Ave | [(-122.1822,37.459),(-122.1803,37.471)] | Oakland
98th Ave | [(-122.1568,37.498),(-122.1558,37.502)] | Oakland
98th Ave | [(-122.1693,37.438),(-122.1682,37.444)] | Oakland
98th Ave | [(-122.2001,37.258),(-122.1974,37.27)] | Lafayette
-(333 rows)
+(332 rows)
-SELECT name, #thepath FROM iexit ORDER BY 1, 2;
+SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
name | ?column?
------------------------------------+----------
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
I- 580 | 2
- I- 580 | 2
- I- 580 | 3
- I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
I- 580 | 3
@@ -527,25 +523,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
- I- 580 Ramp | 2
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
I- 580 Ramp | 3
@@ -685,21 +676,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
I- 580 Ramp | 4
- I- 580 Ramp | 4
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
I- 580 Ramp | 5
@@ -802,22 +792,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
I- 680 Ramp | 3
- I- 680 Ramp | 3
- I- 680 Ramp | 3
I- 680 Ramp | 4
I- 680 Ramp | 4
I- 680 Ramp | 4
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 5
I- 680 Ramp | 6
@@ -838,21 +826,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
I- 80 | 2
- I- 80 | 2
I- 80 | 3
I- 80 | 3
I- 80 | 3
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 4
I- 80 | 5
I- 80 | 5
@@ -898,22 +885,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 4
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
I- 80 Ramp | 5
- I- 80 Ramp | 5
- I- 80 Ramp | 7
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 80 Ramp | 7
I- 880 | 2
I- 880 | 2
I- 880 | 2
I- 880 | 2
I- 880 | 2
I- 880 | 5
I- 880 | 5
@@ -1051,22 +1036,22 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
I- 880 Ramp | 2
- I- 880 Ramp | 2
- I- 880 Ramp | 2
+ I- 880 Ramp | 3
+ I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
I- 880 Ramp | 3
@@ -1148,24 +1133,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
I- 880 Ramp | 4
- I- 880 Ramp | 4
- I- 880 Ramp | 5
- I- 880 Ramp | 5
- I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
I- 880 Ramp | 5
@@ -1232,21 +1213,21 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 980 | 5
I- 980 | 7
I- 980 | 7
I- 980 | 7
I- 980 | 7
I- 980 | 12
I- 980 Ramp | 3
I- 980 Ramp | 3
I- 980 Ramp | 3
I- 980 Ramp | 7
-(896 rows)
+(878 rows)
SELECT * FROM toyemp WHERE name = 'sharon';
name | age | location | annualsal
--------+-----+----------+-----------
sharon | 25 | (15,12) | 12000
(1 row)
--
-- Test for Leaky view scenario
--
diff --git a/src/test/regress/sql/select_views.sql b/src/test/regress/sql/select_views.sql
index 3b74ab9..e742f13 100644
--- a/src/test/regress/sql/select_views.sql
+++ b/src/test/regress/sql/select_views.sql
@@ -1,18 +1,18 @@
--
-- SELECT_VIEWS
-- test the views defined in CREATE_VIEWS
--
SELECT * FROM street;
-SELECT name, #thepath FROM iexit ORDER BY 1, 2;
+SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
SELECT * FROM toyemp WHERE name = 'sharon';
--
-- Test for Leaky view scenario
--
CREATE ROLE regress_alice;
CREATE FUNCTION f_leak (text)
RETURNS bool LANGUAGE 'plpgsql' COST 0.0000001
--
2.7.4 (Apple Git-66)
On Wed, Sep 28, 2016 at 12:02 PM, Emre Hasegeli <emre@hasegeli.com> wrote:
`make check` finds differences per the attached. Please
investigate why the regression tests are failing and what the
appropriate response is.I fixed the first one and workaround the second with COLLATE "C". I
have how my changes caused this regression."select_views" test runs "SELECT name, #thepath FROM iexit ORDER BY 1,
2" and expects to get rows in this order:I- 580 Ramp | 8
I- 580/I-680 Ramp | 2With the collation on my laptop, this is actually true:
regression=# select 'I- 580/I-680 Ramp' < 'I- 580 Ramp';
?column?
----------
t
(1 row)However, on the Linux server, I am testing it is not:
regression=# select 'I- 580 Ramp' < 'I- 580/I-680 Ramp';
?column?
----------
f
(1 row)Do you know how it is not failing on the master?
Well, those two results are not contradictory -- notice that you
switched the order of the values in the comparison. I don't think
you've really found the explanation yet.
[discussing inline static functions compared to macros for min()/max(), etc.]
I suspect that they will be as fast or faster, and they eliminate
the hazard of multiple evaluation, where a programmer might not be
aware of the multiple evaluation or of some side-effect of an
argument.I reworked the the patches to use inline functions and fixed the
problems I found. The new versions are attached.
Will take a look and post again.
--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Sep 28, 2016 at 2:04 PM, Kevin Grittner <kgrittn@gmail.com> wrote:
Will take a look and post again.
I am moving this patch to the next CF. You'll be hearing from me
sometime after this CF is closed.
--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Well, those two results are not contradictory -- notice that you
switched the order of the values in the comparison. I don't think
you've really found the explanation yet.
I am sorry I copy-pasted the wrong example:
"select_views" test runs:
SELECT name, #thepath FROM iexit ORDER BY 1, 2
and expects to get rows in this order:
I- 580 Ramp | 8
I- 580/I-680 Ramp | 2
with the collation on my laptop, this is actually true:
regression=# select 'I- 580 Ramp' < 'I- 580/I-680 Ramp';
?column?
----------
t
(1 row)
on the Linux server I am testing, it is not:
regression=# select 'I- 580 Ramp' < 'I- 580/I-680 Ramp';
?column?
----------
f
(1 row)
The later should be the case on your environment as the test was also
failing for you. This is not consistent with the expected test
result. Do you know how this test can still pass on the master?
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Thu, 29 Sep 2016 10:37:30 +0200, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzxijWKwJ-ZPD--QHM+SxMd+vL_81_3Xt0spnCbsqFH=Ug@mail.gmail.com>
regression=# select 'I- 580 Ramp' < 'I- 580/I-680 Ramp';
?column?
----------
t
(1 row)on the Linux server I am testing, it is not:
regression=# select 'I- 580 Ramp' < 'I- 580/I-680 Ramp';
?column?
----------
f
(1 row)The later should be the case on your environment as the test was also
failing for you. This is not consistent with the expected test
result. Do you know how this test can still pass on the master?
Perhaps you ran the test under the environment LC_COLLATE (or
LANG) of something other than C. The reson for the result is
collation. The following returns expected result.
=# select 'I- 580 Ramp' < ('I- 580/I-680 Ramp' COLLATE "C");
?column?
----------
t
(1 row)
For a reason I don't know, say, en_US locale behaves in an
unintuitive way.
regression=# select ' ' < ('/' COLLATE "en_US"), ' x' < ('/a' COLLATE "en_US");
?column? | ?column?
----------+----------
t | f
(1 row)
Returning to the issue, the following query should give you the
expected result.
SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Returning to the issue, the following query should give you the
expected result.SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
Yes, I have worked around it like this. What I couldn't understand is
how my patch can cause this regression. How is it passes on master
without COLLATE?
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
Returning to the issue, the following query should give you the
expected result.SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
Yes, I have worked around it like this. What I couldn't understand is
how my patch can cause this regression. How is it passes on master
without COLLATE?
Ah, sorry, I understand that *you* added the COLLATE. Revering
select_views.sql/out to master actually causes a regression error.
The reason for that is that you forgot to edit an alternative
exptect file, which matches for en_US locale.
But the test doesn't for collation and the only difference
between the alternative expect files comes from the difference of
collation for the query. "the workaround" seems to be the right
way to do it. I recommend rather to leave the workaroud as it is
and remove select_views_1.out from the "expected" directory.
Aside from that, I'd like to comment this patch on other points
later.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
Aside from that, I'd like to comment this patch on other points
later.
The start of this patch is that the fact that most of but not all
geometric operators use fuzzy comparson. But Tom pointed out that
the fixed fuzz factor is not reasonable but hard to find how to
modify it.
We can remove the fuzz factor altogether but I think we also
should provide a means usable to do similar things. At least "is
a point on a line" might be useless for most cases without any
fuzzing feature. (Nevertheless, it is a problem only when it is
being used to do that:) If we don't find reasonable policy on
fuzzing operations, it would be the proof that we shouldn't
change the behavior.
The 0001 patch adds many FP comparison functions individually
considering NaN. As the result the sort order logic involving NaN
is scattered around into the functions, then, you implement
generic comparison function using them. It seems inside-out to
me. Defining ordering at one place, then comparison using it
seems to be reasonable.
The 0002 patch repalces many native operators for floating point
numbers by functions having sanity check. This seems to be
addressing to Tom's comment. It is reasonable for comparison
operators but I don't think replacing all arithmetics is so. For
example, float8_div checks that
- If both of operands are not INF, result should not be INF.
- If the devident is not exactly zero, the result should not be zero.
The second assumption is wrong by itself. For an extreme case,
4.9e-324 / 1.7e+308 becomes exactly zero (by underflow). We
canont assert it to be wrong but the devedent is not zero. The
validity of the result varies according to its meaning. For the
case of box_cn,
center->x = float8_div(float8_pl(box->high.x, box->low.x), 2.0);
center->y = float8_div(float8_pl(box->high.y, box->low.y), 2.0);
If the center somehow goes extremely near to the origin, it could
result in a false error.
=# select @@ box'(-8e-324, -8e-324), (4.9e-324, 4.9e-324)';
ERROR: value out of range: underflow
I don't think this underflow is an error, and actually it is a
change of the current behavior without a reasonable reason. More
significant (and maybe unacceptable) side-effect is that it
changes the behavior of ordinary operators. I don't think this is
acceptable. More consideration is needed.
=# select ('-8e-324'::float8 + '4.9e-324'::float8) / 2.0;
ERROR: value out of range: underflow
In regard to fuzzy operations, libgeos seems to have several
types of this kind of feature. (I haven't looked closer into
them). Other than reducing precision seems overkill or
unappliable for PostgreSQL bulitins. As Jim said, can we replace
the fixed scale fuzz factor by precision reduction? Maybe, with a
GUC variable (I hear someone's roaring..) to specify the amount
defaults to fit the current assumption.
/*
* Define this to use PrecisionReduction policy
* in an attempt at by-passing binary operation
* robustness problems (handles TopologyExceptions)
*/
...
* Define this to use TopologyPreserving simplification policy
* in an attempt at by-passing binary operation
* robustness problems (handles TopologyExceptions)
(seems not activated)
...
* Use common bits removal policy.
* If enabled, this would be tried /before/
* Geometry snapping.
...
/*
* Use snapping policy
*/
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
The reason for that is that you forgot to edit an alternative
exptect file, which matches for en_US locale.
Now I understand. Thank you for the explanation.
But the test doesn't for collation and the only difference
between the alternative expect files comes from the difference of
collation for the query. "the workaround" seems to be the right
way to do it. I recommend rather to leave the workaroud as it is
and remove select_views_1.out from the "expected" directory.
I will do.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
We can remove the fuzz factor altogether but I think we also
should provide a means usable to do similar things. At least "is
a point on a line" might be useless for most cases without any
fuzzing feature. (Nevertheless, it is a problem only when it is
being used to do that:) If we don't find reasonable policy on
fuzzing operations, it would be the proof that we shouldn't
change the behavior.
It was my initial idea to keep the fuzzy comparison behaviour on some
places, but the more I get into I realised that it is almost
impossible to get this right. Instead, I re-implemented some
operators to keep precision as much as possible. The previous "is a
point on a line" operator would *never* give the true result without
the fuzzy comparison. The new implementation would return true, when
precision is not lost. I think this is a behaviour people, who are
working with floating points, are prepared to deal with. By the way,
"is a point on a line" operator is quite wrong with the fuzzy
comparison at the moment [1]/messages/by-id/CAE2gYzw_-z=V2kh8QqFjenu=8MJXzOP44wRW=AzzeamrmTT1=Q@mail.gmail.com.
The 0001 patch adds many FP comparison functions individually
considering NaN. As the result the sort order logic involving NaN
is scattered around into the functions, then, you implement
generic comparison function using them. It seems inside-out to
me. Defining ordering at one place, then comparison using it
seems to be reasonable.
I agree that it would be simpler to use the comparison function for
implementing other operators. I have done it other way around to make
them more optimised. They are called very often. I don't think
checking exit code of the comparison function would be optimised the
same way. I could leave the comparison functions as they are, but
re-implemented them using the others to keep documentation of NaN
comparison in the single place.
If the center somehow goes extremely near to the origin, it could
result in a false error.=# select @@ box'(-8e-324, -8e-324), (4.9e-324, 4.9e-324)';
ERROR: value out of range: underflowI don't think this underflow is an error, and actually it is a
change of the current behavior without a reasonable reason. More
significant (and maybe unacceptable) side-effect is that it
changes the behavior of ordinary operators. I don't think this is
acceptable. More consideration is needed.=# select ('-8e-324'::float8 + '4.9e-324'::float8) / 2.0;
ERROR: value out of range: underflow
This is the current behaviour of float datatype. My patch doesn't
change that. This problem would probably also apply to multiplying
very small values. I agree that this is not the ideal behaviour.
Though I am not sure, if we should go to a different direction than
the float datatypes.
I think there is value in making geometric types compatible with the
float. Users are going to mix them, anyway. For example, users can
calculate the center of a box manually, and confuse when the built-in
operator behaves differently.
In regard to fuzzy operations, libgeos seems to have several
types of this kind of feature. (I haven't looked closer into
them). Other than reducing precision seems overkill or
unappliable for PostgreSQL bulitins. As Jim said, can we replace
the fixed scale fuzz factor by precision reduction? Maybe, with a
GUC variable (I hear someone's roaring..) to specify the amount
defaults to fit the current assumption.
I am disinclined to try to implement something complicated for the
geometric types. I think they are mostly useful for 2 purposes: uses
simple enough to not worth looking for better solutions, and
demonstrating our indexing capabilities. The inconsistencies harm
both of those.
[1]: /messages/by-id/CAE2gYzw_-z=V2kh8QqFjenu=8MJXzOP44wRW=AzzeamrmTT1=Q@mail.gmail.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Sun, 13 Nov 2016 22:41:01 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzymeQXGGmhU1Vc35DpugwfRd-QRK3BM-6TGg0rwHcDN_w@mail.gmail.com>
We can remove the fuzz factor altogether but I think we also
should provide a means usable to do similar things. At least "is
a point on a line" might be useless for most cases without any
fuzzing feature. (Nevertheless, it is a problem only when it is
being used to do that:) If we don't find reasonable policy on
fuzzing operations, it would be the proof that we shouldn't
change the behavior.It was my initial idea to keep the fuzzy comparison behaviour on some
places, but the more I get into I realised that it is almost
impossible to get this right. Instead, I re-implemented some
operators to keep precision as much as possible. The previous "is a
point on a line" operator would *never* give the true result without
the fuzzy comparison. The new implementation would return true, when
precision is not lost.
There's no way to accurately store numbers other than some
special ones in floating point format where mantissa precision is
limited. Even 0.1 is not stored accurately with a binary
mantissa. (It would be concealed by machine rounding for most
cases though.) The same is said for numeric. It cannot store 1/3
accurately and doesn't even conceal the incaccuracy. Even if they
could store numbers accurately, anyway , say, the constant pi
does not have infinite precision. As the result, we have a
tradition that equal operator on real numbers should be avoided
generally. Numerics are provided mainly for financial use, on
which finite-but-high precision decimal arithmetic are performed.
I think this is a behaviour people, who are
working with floating points, are prepared to deal with.
What way to deal with it is in your mind? The problem hides
behind operators. To fix it a user should rewrite a expression
using more primitive operators. For example, (line_a # point_a)
should be rewritten as ((point_a <-> lineseg_a) < EPSILON), or in
more primitive way. I regared this that the operator # just
become useless.
By the way, "is a point on a line" operator is quite wrong with
the fuzzy comparison at the moment [1].
Th bug is a isolate problem from the epsilon behavior. It of
course should be fixed, but in "appropriate" way that may defined
in this discussion.
The 0001 patch adds many FP comparison functions individually
considering NaN. As the result the sort order logic involving NaN
is scattered around into the functions, then, you implement
generic comparison function using them. It seems inside-out to
me. Defining ordering at one place, then comparison using it
seems to be reasonable.I agree that it would be simpler to use the comparison function for
implementing other operators. I have done it other way around to make
them more optimised. They are called very often. I don't think
checking exit code of the comparison function would be optimised the
same way. I could leave the comparison functions as they are, but
re-implemented them using the others to keep documentation of NaN
comparison in the single place.
Regarding optimization, at least gcc generates seemingly not so
different code for the two. The both generally generates extended
code directly calling isnan() and so. Have you measured the
performance of the two implement (with -O2, without
--enable-cassert)? This kind of hand-optimization gets
legitimacy when we see a siginificant difference, according to
the convention here.. I suppose.
At least the comment you dropped by the patch,
int
float4_cmp_internal(float4 a, float4 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */
seems very significant and should be kept anywhere relevant.
If the center somehow goes extremely near to the origin, it could
result in a false error.=# select @@ box'(-8e-324, -8e-324), (4.9e-324, 4.9e-324)';
ERROR: value out of range: underflowI don't think this underflow is an error, and actually it is a
change of the current behavior without a reasonable reason. More
significant (and maybe unacceptable) side-effect is that it
changes the behavior of ordinary operators. I don't think this is
acceptable. More consideration is needed.=# select ('-8e-324'::float8 + '4.9e-324'::float8) / 2.0;
ERROR: value out of range: underflowThis is the current behaviour of float datatype. My patch doesn't
change that.
Very sorry for the mistake! I somehow saw float8pl on master and
float8_div with your patch. Pleas forget it.
This problem would probably also apply to multiplying
very small values. I agree that this is not the ideal behaviour.
Though I am not sure, if we should go to a different direction than
the float datatypes.
Yes, perhaps the behavior of geometric arithmetic should be
considered differently from bare float calculation, but it would
not be the topic for now.
I think there is value in making geometric types compatible with the
float. Users are going to mix them, anyway. For example, users can
calculate the center of a box manually, and confuse when the built-in
operator behaves differently.
That doesn't resolve the problem that some comparison operators
are practically useless, and means that we (maybe) should create
another geometirc data type based on numeric. But it also would
be a crap from the same reason with float-based.
At a glance, by no-tolerance comparison, the following geometric
operators seem to be hardly usable.
#, &&, ?#, ?-, ?|, ?-|, ?||, @>, <@, ~=
And including(le, ge) and exclusing(lt, gt) comparisons seem to
lose their distinction practically.
<<, >>, &<, &>, <<|, |>>, &<|, |&>, <^, >^
I seached pgsql-general ML but counldn't find a complaint about
the current behavior. Even though I'm not familar with PostGIS, I
found that it uses exactly the same EPSILON method with
PostgreSQL. And libgeos also takes tolerance on comparison by
several ways. From the facts, I think that the users of geometric
types requires any kind of tolerance/fuzzyness factor on
comparison.
If we had an apparent plan to use them for other than
earth-scale(?) geometric usage, we could design what they should
be. But without such a plan it is just a breakage of the current
usage.
# By the way, 2D geometric functions are inaccurate for
# earth-scale spherical figures , but we ignoring that?
In regard to fuzzy operations, libgeos seems to have several
types of this kind of feature. (I haven't looked closer into
them). Other than reducing precision seems overkill or
unappliable for PostgreSQL bulitins. As Jim said, can we replace
the fixed scale fuzz factor by precision reduction? Maybe, with a
GUC variable (I hear someone's roaring..) to specify the amount
defaults to fit the current assumption.I am disinclined to try to implement something complicated for the
geometric types.
I think they are mostly useful for 2 purposes: uses
simple enough to not worth looking for better solutions, and
demonstrating our indexing capabilities. The inconsistencies harm
both of those.
About What kind of (needless) complication you are saying? The
fuzziness seems to me essential for geometric comparisons to work
practically. Addition to that, I don't think that we're not
allowed to change the behavior in such area of released versions
the time after time.
I don't think index scan and tolerant comparison are not
contradicting. Could you let me have an description about the
indexing capabilities and the inconsistencies?
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
What way to deal with it is in your mind? The problem hides
behind operators. To fix it a user should rewrite a expression
using more primitive operators. For example, (line_a # point_a)
should be rewritten as ((point_a <-> lineseg_a) < EPSILON), or in
more primitive way. I regared this that the operator # just
become useless.
Simple equations like this works well with the current algorithm:
select '(0.1,0.1)'::point @ '(0,0),(1,1)'::line;
The operator does what you expect from it. Users can use something
like you described to get fuzzy behaviour with an epsilon they choose.
Regarding optimization, at least gcc generates seemingly not so
different code for the two. The both generally generates extended
code directly calling isnan() and so. Have you measured the
performance of the two implement (with -O2, without
--enable-cassert)? This kind of hand-optimization gets
legitimacy when we see a siginificant difference, according to
the convention here.. I suppose.
I tested it with this program:
int main()
{
double i,
j;
int result = 0;for (i = 0.1; i < 10000.0; i += 1.0)
for (j = 0.1; j < 10000.0; j += 1.0)
if (float8_lt(i, j))
result = (result + 1) % 10;return result;
}
The one calling cmp() was noticeable slower.
./test1 0.74s user 0.00s system 99% cpu 0.748 total
./test2 0.89s user 0.00s system 99% cpu 0.897 total
This would probably be not much noticeable by calling SQL functions
which are doing a few comparisons only, but it may be necessary to do
many more comparisons on some places. I don't find the optimised
versions less clear than calling the cmp(). I can change it the other
way, if you find it more clear.
At least the comment you dropped by the patch,
int
float4_cmp_internal(float4 a, float4 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */seems very significant and should be kept anywhere relevant.
I will add it back on the next version.
I seached pgsql-general ML but counldn't find a complaint about
the current behavior. Even though I'm not familar with PostGIS, I
found that it uses exactly the same EPSILON method with
PostgreSQL.
Is it? I understood from Paul Ramsey's comment on this thread [1]/messages/by-id/CACowWR0DBEjCfBscKKumdRLJUkObjB7D=iw7-0_ZwSFJM9_gpw@mail.gmail.com
that they don't.
If we had an apparent plan to use them for other than
earth-scale(?) geometric usage, we could design what they should
be. But without such a plan it is just a breakage of the current
usage.
We give no promises about the geometric types being useful in earth scale.
About What kind of (needless) complication you are saying? The
fuzziness seems to me essential for geometric comparisons to work
practically. Addition to that, I don't think that we're not
allowed to change the behavior in such area of released versions
the time after time.
Even when it is a total mess?
I don't think index scan and tolerant comparison are not
contradicting. Could you let me have an description about the
indexing capabilities and the inconsistencies?
The first problem is that some operators are not using the epsilon.
This requires special treatment while developing index support for
operators. I have tried to support point for BRIN using the box
operators, and failed because of that.
Comparing differences with epsilon is not applicable the same way to
every operator. Even with simple operators like "point in box" it
covers different distances outside the box depending on where the
point is. For example, "point <-> box < EPSILON" wouldn't be
equivalent with "point <@ box", when the point is outside corner of
the box. Things get more complicated with lines. Because of these,
we are easily violating basic expectations of the operators:
regression=# select '{1000,0.000001,0}'::line ?|| '{90000,0.00009,0}'::line;
?column?
----------
f
(1 row)regression=# select '{90000,0.00009,0}'::line ?|| '{1000,0.000001,0}'::line;
?column?
----------
t
(1 row)
Another problem is lack of hash and btree operator classes. In my
experience, the point datatype is by far most used one. People often
try to use it on DISTINCT, GROUP BY, or ORDER BY clauses and complain
when it doesn't work. There are many complaints like this on the
archives. If we get rid of the epsilon, we can easily add those
operator classes.
[1]: /messages/by-id/CACowWR0DBEjCfBscKKumdRLJUkObjB7D=iw7-0_ZwSFJM9_gpw@mail.gmail.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Mon, 14 Nov 2016 11:41:52 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzyjvRbj4bQZRp-yBo73-77-LDH0WnwPtAtZRFKPxYQRgw@mail.gmail.com>
What way to deal with it is in your mind? The problem hides
behind operators. To fix it a user should rewrite a expression
using more primitive operators. For example, (line_a # point_a)
should be rewritten as ((point_a <-> lineseg_a) < EPSILON), or in
more primitive way. I regared this that the operator # just
become useless.Simple equations like this works well with the current algorithm:
select '(0.1,0.1)'::point @ '(0,0),(1,1)'::line;
That's too simple for this discussion. If it satisfies someone's
requirement, a geometiric type with integer would go. Any
trigonometric calculations (by other than right angles) would
break it.
The operator does what you expect from it. Users can use something
like you described to get fuzzy behaviour with an epsilon they choose.Regarding optimization, at least gcc generates seemingly not so
different code for the two. The both generally generates extended
code directly calling isnan() and so. Have you measured the
performance of the two implement (with -O2, without
--enable-cassert)? This kind of hand-optimization gets
legitimacy when we see a siginificant difference, according to
the convention here.. I suppose.I tested it with this program:
int main()
{
double i,
j;
int result = 0;for (i = 0.1; i < 10000.0; i += 1.0)
for (j = 0.1; j < 10000.0; j += 1.0)
if (float8_lt(i, j))
result = (result + 1) % 10;return result;
}The one calling cmp() was noticeable slower.
./test1 0.74s user 0.00s system 99% cpu 0.748 total
./test2 0.89s user 0.00s system 99% cpu 0.897 totalThis would probably be not much noticeable by calling SQL functions
which are doing a few comparisons only, but it may be necessary to do
many more comparisons on some places. I don't find the optimised
versions less clear than calling the cmp(). I can change it the other
way, if you find it more clear.
Thanks for the measurment. I had similar numbers (14:13) for
separate code from Pg, but probably this difference is buried
under other steps. The law of stay-stupid says that this kind of
optimization should be consider only after it found to be
problematic, I think.
At least the comment you dropped by the patch,
int
float4_cmp_internal(float4 a, float4 b)
{
- /*
- * We consider all NANs to be equal and larger than any non-NAN. This is
- * somewhat arbitrary; the important thing is to have a consistent sort
- * order.
- */seems very significant and should be kept anywhere relevant.
I will add it back on the next version.
I seached pgsql-general ML but counldn't find a complaint about
the current behavior. Even though I'm not familar with PostGIS, I
found that it uses exactly the same EPSILON method with
PostgreSQL.Is it? I understood from Paul Ramsey's comment on this thread [1]
that they don't.
I saw the repository of PostGIS by myself, but only in
box2d.c. Other objects have their own EPSILONs, EPSILOG_SQLMM,
DBL_EPSLON, FLT_EPSILON... Maybe that's all
| * Copyright (C) 2008-2011 Paul Ramsey <pramsey@cleverelephant.ca>
| * Copyright (C) 2008 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
|
| #ifndef EPSILON
| #define EPSILON 1.0E-06
| #endif
| #ifndef FPeq
| #define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
| #endif
I agree that we should have our own tolerance method, and it migh
be an explicit one. But anyway we need any kind of tolerance on
comparing FP numbers. Removing such tolerance as preparation for
implementing another one is quite natural, but released version
should have any kind of tolerance.
If we had an apparent plan to use them for other than
earth-scale(?) geometric usage, we could design what they should
be. But without such a plan it is just a breakage of the current
usage.We give no promises about the geometric types being useful in earth scale.
So, we shouldn't just remove it.
About What kind of (needless) complication you are saying? The
fuzziness seems to me essential for geometric comparisons to work
practically. Addition to that, I don't think that we're not
allowed to change the behavior in such area of released versions
the time after time.Even when it is a total mess?
Yes. The seemingly 'total-mess' is intended for a use with
lon/lat values and probably gave useful result at the time of
development. The mess is rather better than just ruined.
I don't think index scan and tolerant comparison are not
contradicting. Could you let me have an description about the
indexing capabilities and the inconsistencies?The first problem is that some operators are not using the epsilon.
This requires special treatment while developing index support for
operators. I have tried to support point for BRIN using the box
operators, and failed because of that.Comparing differences with epsilon is not applicable the same way to
every operator. Even with simple operators like "point in box" it
covers different distances outside the box depending on where the
point is. For example, "point <-> box < EPSILON" wouldn't be
equivalent with "point <@ box", when the point is outside corner of
the box. Things get more complicated with lines. Because of these,
we are easily violating basic expectations of the operators:
To keep such kind of integrity, we should deeply consider about
errors.
regression=# select '{1000,0.000001,0}'::line ?|| '{90000,0.00009,0}'::line;
?column?
----------
f
(1 row)regression=# select '{90000,0.00009,0}'::line ?|| '{1000,0.000001,0}'::line;
?column?
----------
t
(1 row)
This kind of thing also appears on exact comparison with more
uncertain way. So we have the EPSILON so that the errors would be
reasonable at least on lon/lat geometrics. The values above are
far out of the scope of the EPSILON from the first. But exact
ones should be just unworkable.
Another problem is lack of hash and btree operator classes. In my
experience, the point datatype is by far most used one. People often
try to use it on DISTINCT, GROUP BY, or ORDER BY clauses and complain
when it doesn't work. There are many complaints like this on the
archives. If we get rid of the epsilon, we can easily add those
operator classes.
That's a fate of FP numbers. Btree is uasble for such data since
it is constructed using inequality operators but hash is almost
unsuitable without rounding and normalization because of the
nature of floating point numbers. Range scan on bree will work
find but on the other hand equality doesn't work even on btree
index from the same reason to the below.
DSITINCT, GROUP BY are just inpossible without somehow defining
the equiality of two points. Bare FP numbers doesn't fit since
generally they cannot match each other and should match based on
any resolutions of 0.000001, 0.1, 1, or 1000 according to
objective. I think probably those who complain so have different
resolution in their minds.
Again, FP numbers generally cannot match exactly each other. You
have to avoid that.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
To keep such kind of integrity, we should deeply consider about
errors.
My point is that I don't think we can keep integrity together with the
fuzzy behaviour, or at least I don't have the skills to do that. I
can just leave the more complicated operators like "is a
point on a line" as it is, and only change the basic ones. Do you
think a smaller patch like this would be acceptable?
That's a fate of FP numbers. Btree is uasble for such data since
it is constructed using inequality operators but hash is almost
unsuitable without rounding and normalization because of the
nature of floating point numbers. Range scan on bree will work
find but on the other hand equality doesn't work even on btree
index from the same reason to the below.
Btree is very useful with floats. There is no problem with it.
Again, FP numbers generally cannot match exactly each other. You
have to avoid that.
Again, they do match very well. Floating point numbers are no magic.
They are perfectly deterministic.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
# Sorry for a trash I emitted before..
Perhaps you don't assume any calculations applied on stored
geo-type values. Please be patient to disucuss with me. (Sorry
for perhas hard-to-read English..)
At Fri, 18 Nov 2016 10:58:27 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzxVxKNS7qU74UdHVZTmfXQjxMbFiXH5+16XFy90SRAbXA@mail.gmail.com>
To keep such kind of integrity, we should deeply consider about
errors.My point is that I don't think we can keep integrity together with the
fuzzy behaviour, or at least I don't have the skills to do that. I
can just leave the more complicated operators like "is a
point on a line" as it is, and only change the basic ones. Do you
think a smaller patch like this would be acceptable?
The size of the patch is not a matter. It broken the current
behavior is a matter. It shows strange behavior for some cases
but it designed to work reasonable for some cases.
That's a fate of FP numbers. Btree is uasble for such data since
it is constructed using inequality operators but hash is almost
unsuitable without rounding and normalization because of the
nature of floating point numbers. Range scan on bree will work
find but on the other hand equality doesn't work even on btree
index from the same reason to the below.Btree is very useful with floats. There is no problem with it.
I don't mind btrees on bare floats. It will 'work' if error is
properly considered, or error can be omitted for the
purpose. Point has some kind of physical context or expected
behavior other than that of bare floats which the EPSILON
implies.
Again, FP numbers generally cannot match exactly each other. You
have to avoid that.Again, they do match very well. Floating point numbers are no magic.
They are perfectly deterministic.
No magic, yes, so it is always accompanied by error. If you store
points, then searching for *any of them*, it works fine. It's no
magic. If you obtained a ponit from any physical data source, say
GPS receiver, then search identical points from a table, it would
be almost impossible to find a "exactly the same" location. It's
no magic. Once a calculation has been applied on them, it is no
longer guaranteed to work in the same way. It's also no magic.
CREATE OR REPLACE FUNCTION rotate_point (integer, float8) RETURNS void AS $$ UPDATE p SET x = x * cos($2) - y * sin($2), y = x * sin($2) + y * cos($2) WHERE i = $1 $$ LANGUAGE SQL;
ALTER TABLE p (i int, x float8, y float8);
INSERT INTO p VALUES (0, 1.0, 1.0), (1, 1.0, 1.0);
SELECT rotate_point(0, pi() * 2.0 / 3.0);
SELECT rotate_point(0, pi() * 2.0 / 3.0);
SELECT rotate_point(0, pi() * 2.0 / 3.0);
SELECT * FROM p WHERE i = 0;
i | x | y
---+---+-------------------
0 | 1 | 0.999999999999999
So
SELECT p0.x = p1.x AND p0.y = p1.y FROM p p0, p p1 WHERE p0.i = 0 and p1.i = 1;
?column?
----------
f
(1 row)
This just repeats 1/3 rotation 3 times. Ideally p0 and p1 are
identical but this primitive operation generates visible error
that makes the comparison fail. Even though these two points are
expected be identical in some geometric viewpoint. This is a part
of the meaning of the EPSILON.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Fri, 18 Nov 2016 10:58:27 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzxVxKNS7qU74UdHVZTmfXQjxMbFiXH5+16XFy90SRAbXA@mail.gmail.com>
To keep such kind of integrity, we should deeply consider about
errors.My point is that I don't think we can keep integrity together with the
fuzzy behaviour, or at least I don't have the skills to do that. I
can just leave the more complicated operators like "is a
point on a line" as it is, and only change the basic ones. Do you
think a smaller patch like this would be acceptable?
The size of the patch is not a problem. I regret that I haven't
made your requirement clear. So as the startpoint of the new
discussion, I briefly summarize the current implement of
geometric comparisons.
- Floating point comparisons for gemetric types
Comparison related to geometric types is performed by FPeq
macro and similars defined in geo_decls.h. This intends to give
tolerance to the comparisons.
A
FPeq: |<=e-|-e=>| (<= means inclusive, e = epsilon = tolerance)
FPne: ->| e | e |<- (<- means exclusive)
FPlt: | e |<-
FPle: |<=e |
FPgt: ->| e |
FPge: | e=>|
These seems reasonable ignoring the tolerance amount issue.
- Consistency between index and non-index scans.
GIST supports geometric types.
=# create table tpnt1(id int, p point);
=# insert into tpnt1 (select i + 200, point(i*1.0e-6 / 100.0, i * 1.0e-6 / 100.0) from generate_series(-200, 200) as i);
=# create index on tpnt1 using gist (p);
=# set enable_seqscan to false;
=# set enable_bitmapscan to true;
=# select count(*) from tpnt1 where p ~= point(0, 0);
201
=# select count(*) from tpnt1 where p << point(0, 0);
100
=# set enable_seqscan to true;
=# set enable_bitmapscan to false;
=# select count(*) from tpnt1 where p ~= point(0, 0);
201
=# select count(*) from tpnt1 where p << point(0, 0);
100
At least for the point type, (bitmap) index scan is consistent
with sequential scan. I remember that the issue was
"inconsistency between indexed and non-indexed scans over
geometric types" but they seem consistent with each other.
You mentioned b-tree, which don't have predefined opclass for
geometric types. So the "inconsistency" may be mentioning the
difference between geometric values and combinations of plain
(first-class) values. But the two are different things and
apparently using different operators (~= and = for equality) so
the issue is not fit for this. More specifically, "p ~= p0" and
"x = x0 and y = y0" are completely different.
Could you let me (or any other guys on this ml) have more precise
description on the problem and/or what you want to do with this
patch?
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
- Floating point comparisons for gemetric types
Comparison related to geometric types is performed by FPeq
macro and similars defined in geo_decls.h. This intends to give
tolerance to the comparisons.A
FPeq: |<=e-|-e=>| (<= means inclusive, e = epsilon = tolerance)
FPne: ->| e | e |<- (<- means exclusive)
FPlt: | e |<-
FPle: |<=e |
FPgt: ->| e |
FPge: | e=>|These seems reasonable ignoring the tolerance amount issue.
I tried to explain below why I don't find this method reasonable.
At least for the point type, (bitmap) index scan is consistent
with sequential scan. I remember that the issue was
"inconsistency between indexed and non-indexed scans over
geometric types" but they seem consistent with each other.
You can see on the Git history that it was a lot of trouble to make
the indexes consistent with the operators, and this is limiting
improving indexing of the geometric types.
You mentioned b-tree, which don't have predefined opclass for
geometric types. So the "inconsistency" may be mentioning the
difference between geometric values and combinations of plain
(first-class) values. But the two are different things and
apparently using different operators (~= and = for equality) so
the issue is not fit for this. More specifically, "p ~= p0" and
"x = x0 and y = y0" are completely different.
Yes, the default btree operator class doesn't have to implement
standard equality and basic comparison operator symbols. We can
implement it with different symbols.
Could you let me (or any other guys on this ml) have more precise
description on the problem and/or what you want to do with this
patch?
I will try to itemize the current problems with the epsilon method:
= Operator Inconsistencies
My current patches address all of them.
- Only some operators are using the epsilon.
I included the list of the ones which doesn't use the epsilon on
initial post of this thread. This makes the operators not compatible
with each other. For example, you cannot say "if box_a @> box_b and
box_b @> point then box_a @> box_b".
- The application of epsilon is implementation dependent.
Epsilon works between two numbers. There is not always a single way
of implementing geometric operators. This is surprising to the users.
For example, you cannot say "if box_a @> box_b then box_a <-> box_b <=
epsilon".
This is also creating a high barrier on developing those operators.
Fixing bugs and performance regressions are very likely to change the
results.
- Some operators are just wrong:
Line operators are not well maintained. The following are giving
wrong results when neither A or B of lines are not exactly 1:
* line # line
* line <-> line
* point ## line
* point ## lseg
They are broken independently from the epsilon, though it is not easy
to fix them without changing the results of the cases that are
currently working.
- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
- Some operators are violating transitive property.
For example, you cannot say "if point_a ~= point_b and point_b ~=
point_c then point_a ~= point_c".
- The operators with epsilon are not compatible with float operators.
This is also surprising for the users. For example, you cannot say
"if point_a << point_b then point_a[0] < point_b[0]".
- The operators are not checking for underflow or overflow.
- NaN values are not treated same as the float datatype.
Those are independent problems, though checking them with epsilon
would create additional set of inconsistencies. Epsilon creates
especially more problems around 0.
= Missing Bits on the Operator Classes
I want to address those in the future, if my patches are accepted.
- There is no GiST or SP-GiST support for cross datatype operators
other than containment.
We can develop nice cross-datatype operator families to support more
operators. It would have been easier, if we could rely on some
operators to implement others. Geometric types serve the purpose of
demonstrating our indexing capabilities. We should make them good
examples, not full of special cases with hidden bugs.
- There is no BRIN support for types other than the box.
BRIN inclusion operator class is datatype agnostic. It uses some
operators to implement others which doesn't work for anything more the
box vs box operators, because of the inconsistencies.
- GROUP BY and DISTINCT are not working.
- ORDER BY is not working.
There are regular user complaints about this on the mailing list. We
can easily provide default hash and btree operator classes that would
fix the problem, if we haven't had the epsilon.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello.
(I apologize in advance for possible inaccurate wording on maths,
which might cause confusion..)
At Wed, 11 Jan 2017 16:37:59 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzzNufOZqh4HO3Od8urzamNSeX-OXJxfNkRcU3ex9RD8jw@mail.gmail.com>
- Floating point comparisons for gemetric types
Comparison related to geometric types is performed by FPeq
macro and similars defined in geo_decls.h. This intends to give
tolerance to the comparisons.A
FPeq: |<=e-|-e=>| (<= means inclusive, e = epsilon = tolerance)
FPne: ->| e | e |<- (<- means exclusive)
FPlt: | e |<-
FPle: |<=e |
FPgt: ->| e |
FPge: | e=>|These seems reasonable ignoring the tolerance amount issue.
I tried to explain below why I don't find this method reasonable.
Thank you very much for the explanation.
At least for the point type, (bitmap) index scan is consistent
with sequential scan. I remember that the issue was
"inconsistency between indexed and non-indexed scans over
geometric types" but they seem consistent with each other.You can see on the Git history that it was a lot of trouble to make
the indexes consistent with the operators, and this is limiting
improving indexing of the geometric types.
Yeah. geo_ops.c has the following comment from its first version
in the current repository.
| * Relational operators for Points.
| * Since we do have a sense of coordinates being
| * "equal" to a given accuracy (point_vert, point_horiz),
So, we take it as a requirement for geometric types. All the
difficulties come from the "nature".
You mentioned b-tree, which don't have predefined opclass for
geometric types. So the "inconsistency" may be mentioning the
difference between geometric values and combinations of plain
(first-class) values. But the two are different things and
apparently using different operators (~= and = for equality) so
the issue is not fit for this. More specifically, "p ~= p0" and
"x = x0 and y = y0" are completely different.Yes, the default btree operator class doesn't have to implement
standard equality and basic comparison operator symbols. We can
implement it with different symbols.
Perhaps I don't understand it. Many opclass are defined for
btree. But since (ordinary) btree handles only one-dimentional,
totally-orderable sets, geometric types even point don't fit
it. Even if we relaxed it by removing EPSILON, multidimentional
data still doesn't fit.
Still we can implement equality mechanism *over* multiple btree
indexes, but it cannot be a opclass for btree.
Could you let me (or any other guys on this ml) have more precise
description on the problem and/or what you want to do with this
patch?I will try to itemize the current problems with the epsilon method:
= Operator Inconsistencies
My current patches address all of them.
No. It just removes tolerans that is a "requirement" for
coordinates of geometric types. I think we don't have enough
reason to remove it. We could newly define geometric types with
no-tolerance as 'exact_point" or something but it still doesn't
fit btree.
- Only some operators are using the epsilon.
I included the list of the ones which doesn't use the epsilon on
initial post of this thread. This makes the operators not compatible
with each other. For example, you cannot say "if box_a @> box_b and
box_b @> point then box_a @> box_b".
(It must be "then box_a @> point".) Both of the operators don't
seem to use EPSILON so the transitivity holds, but perhaps we
should change them to more appropriate shape, that is, to use
EPSILON. Even having the tolerance, we can define the operators
to keep the transitivity.
- The application of epsilon is implementation dependent.
Epsilon works between two numbers. There is not always a single way
of implementing geometric operators. This is surprising to the users.
For example, you cannot say "if box_a @> box_b then box_a <-> box_b <=
epsilon".
Maybe it should be like "if box_a ~= box_b && box_b @< box_a then
..". I'm not sure off-hand. But I don't see the relationship is
significant.
This is also creating a high barrier on developing those operators.
Fixing bugs and performance regressions are very likely to change the
results.
I agree to you. The effect of the EPSILON is quite arbitrary and
difficult to see their chemistry.
- Some operators are just wrong:
Line operators are not well maintained. The following are giving
wrong results when neither A or B of lines are not exactly 1:* line # line
* line <-> line
* point ## line
* point ## lseg
These are not comparison operators so EPSILON is unrelated. And I
agree that they needs fix.
They are broken independently from the epsilon, though it is not easy
to fix them without changing the results of the cases that are
currently working.
Although I'm not sure they are using EPSILON, I think they don't
need it.
- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
Whether EPSILON is introduced or not, commutativity cannot be
assured for it from calculation error, I suppose.
- Some operators are violating transitive property.
For example, you cannot say "if point_a ~= point_b and point_b ~=
point_c then point_a ~= point_c".
It holds only in ideal (or mathematical) world, or for similars
of integer (which are strictly implemented mathematica world on
computer). Without the EPSILON, two points hardly match by
floating point error, as I mentioned. I don't have an evidence
though (sorry), mere equality among three floating point numbers
is not assured.
Sorry, I have no more time today, I'll continue tomorrow.
- The operators with epsilon are not compatible with float operators.
This is also surprising for the users. For example, you cannot say
"if point_a << point_b then point_a[0] < point_b[0]".- The operators are not checking for underflow or overflow.
- NaN values are not treated same as the float datatype.Those are independent problems, though checking them with epsilon
would create additional set of inconsistencies. Epsilon creates
especially more problems around 0.= Missing Bits on the Operator Classes
I want to address those in the future, if my patches are accepted.
- There is no GiST or SP-GiST support for cross datatype operators
other than containment.We can develop nice cross-datatype operator families to support more
operators. It would have been easier, if we could rely on some
operators to implement others. Geometric types serve the purpose of
demonstrating our indexing capabilities. We should make them good
examples, not full of special cases with hidden bugs.- There is no BRIN support for types other than the box.
BRIN inclusion operator class is datatype agnostic. It uses some
operators to implement others which doesn't work for anything more the
box vs box operators, because of the inconsistencies.- GROUP BY and DISTINCT are not working.
- ORDER BY is not working.There are regular user complaints about this on the mailing list. We
can easily provide default hash and btree operator classes that would
fix the problem, if we haven't had the epsilon.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Sorry, this is the continuation of the previous comment.
At Wed, 18 Jan 2017 17:36:12 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote in <20170118.173612.13506164.horiguchi.kyotaro@lab.ntt.co.jp>
Hello.
(I apologize in advance for possible inaccurate wording on maths,
which might cause confusion..)At Wed, 11 Jan 2017 16:37:59 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzzNufOZqh4HO3Od8urzamNSeX-OXJxfNkRcU3ex9RD8jw@mail.gmail.com>
- Floating point comparisons for gemetric types
Comparison related to geometric types is performed by FPeq
macro and similars defined in geo_decls.h. This intends to give
tolerance to the comparisons.A
FPeq: |<=e-|-e=>| (<= means inclusive, e = epsilon = tolerance)
FPne: ->| e | e |<- (<- means exclusive)
FPlt: | e |<-
FPle: |<=e |
FPgt: ->| e |
FPge: | e=>|These seems reasonable ignoring the tolerance amount issue.
I tried to explain below why I don't find this method reasonable.
Thank you very much for the explanation.
At least for the point type, (bitmap) index scan is consistent
with sequential scan. I remember that the issue was
"inconsistency between indexed and non-indexed scans over
geometric types" but they seem consistent with each other.You can see on the Git history that it was a lot of trouble to make
the indexes consistent with the operators, and this is limiting
improving indexing of the geometric types.Yeah. geo_ops.c has the following comment from its first version
in the current repository.| * Relational operators for Points.
| * Since we do have a sense of coordinates being
| * "equal" to a given accuracy (point_vert, point_horiz),So, we take it as a requirement for geometric types. All the
difficulties come from the "nature".You mentioned b-tree, which don't have predefined opclass for
geometric types. So the "inconsistency" may be mentioning the
difference between geometric values and combinations of plain
(first-class) values. But the two are different things and
apparently using different operators (~= and = for equality) so
the issue is not fit for this. More specifically, "p ~= p0" and
"x = x0 and y = y0" are completely different.Yes, the default btree operator class doesn't have to implement
standard equality and basic comparison operator symbols. We can
implement it with different symbols.Perhaps I don't understand it. Many opclass are defined for
btree. But since (ordinary) btree handles only one-dimentional,
totally-orderable sets, geometric types even point don't fit
it. Even if we relaxed it by removing EPSILON, multidimentional
data still doesn't fit.Still we can implement equality mechanism *over* multiple btree
indexes, but it cannot be a opclass for btree.Could you let me (or any other guys on this ml) have more precise
description on the problem and/or what you want to do with this
patch?I will try to itemize the current problems with the epsilon method:
= Operator Inconsistencies
My current patches address all of them.
No. It just removes tolerans that is a "requirement" for
coordinates of geometric types. I think we don't have enough
reason to remove it. We could newly define geometric types with
no-tolerance as 'exact_point" or something but it still doesn't
fit btree.- Only some operators are using the epsilon.
I included the list of the ones which doesn't use the epsilon on
initial post of this thread. This makes the operators not compatible
with each other. For example, you cannot say "if box_a @> box_b and
box_b @> point then box_a @> box_b".(It must be "then box_a @> point".) Both of the operators don't
seem to use EPSILON so the transitivity holds, but perhaps we
should change them to more appropriate shape, that is, to use
EPSILON. Even having the tolerance, we can define the operators
to keep the transitivity.- The application of epsilon is implementation dependent.
Epsilon works between two numbers. There is not always a single way
of implementing geometric operators. This is surprising to the users.
For example, you cannot say "if box_a @> box_b then box_a <-> box_b <=
epsilon".Maybe it should be like "if box_a ~= box_b && box_b @< box_a then
..". I'm not sure off-hand. But I don't see the relationship is
significant.This is also creating a high barrier on developing those operators.
Fixing bugs and performance regressions are very likely to change the
results.I agree to you. The effect of the EPSILON is quite arbitrary and
difficult to see their chemistry.- Some operators are just wrong:
Line operators are not well maintained. The following are giving
wrong results when neither A or B of lines are not exactly 1:* line # line
* line <-> line
* point ## line
* point ## lsegThese are not comparison operators so EPSILON is unrelated. And I
agree that they needs fix.They are broken independently from the epsilon, though it is not easy
to fix them without changing the results of the cases that are
currently working.Although I'm not sure they are using EPSILON, I think they don't
need it.- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
Whether EPSILON is introduced or not, commutativity cannot be
assured for it from calculation error, I suppose.- Some operators are violating transitive property.
For example, you cannot say "if point_a ~= point_b and point_b ~=
point_c then point_a ~= point_c".It holds only in ideal (or mathematical) world, or for similars
of integer (which are strictly implemented mathematica world on
computer). Without the EPSILON, two points hardly match by
floating point error, as I mentioned. I don't have an evidence
though (sorry), mere equality among three floating point numbers
is not assured.Sorry, I have no more time today, I'll continue tomorrow.
- The operators with epsilon are not compatible with float operators.
This is also surprising for the users. For example, you cannot say
"if point_a << point_b then point_a[0] < point_b[0]".
It is because "is strictly left of" just doesn't mean their
x-coordinates are in such a relationship. The difference might be
surprising but is a specification (truely not a bug:).
- The operators are not checking for underflow or overflow.
- NaN values are not treated same as the float datatype.Those are independent problems, though checking them with epsilon
would create additional set of inconsistencies. Epsilon creates
especially more problems around 0.
I dont' understand this precisely but it seems to be reasonable.
= Missing Bits on the Operator Classes
I want to address those in the future, if my patches are accepted.
- There is no GiST or SP-GiST support for cross datatype operators
other than containment.We can develop nice cross-datatype operator families to support more
operators. It would have been easier, if we could rely on some
operators to implement others. Geometric types serve the purpose of
demonstrating our indexing capabilities. We should make them good
examples, not full of special cases with hidden bugs.- There is no BRIN support for types other than the box.
BRIN inclusion operator class is datatype agnostic. It uses some
operators to implement others which doesn't work for anything more the
box vs box operators, because of the inconsistencies.- GROUP BY and DISTINCT are not working.
- ORDER BY is not working.There are regular user complaints about this on the mailing list. We
can easily provide default hash and btree operator classes that would
fix the problem, if we haven't had the epsilon.
This final section seems to mention the application but sorry, it
still don't describe for me what application that this patch
aiming to enable. But I can understand that you want to handle
floating point numbers like integers. The epsilon is surely quite
annoying for the purpose.
Ok, returning to the basis of the current geometric types, it is
described as below. It is not perfect (so needs amendment) but
achived to a certain degree.
1. For geometric types, equality needs to be defined with some
tolerance. Concretely an absolute value of 1E-06, which is
quite random but would be suitable to ignore error of
longitude and latitude.
2. Translative comparisons should consider the tolerance to make
a result that does not contradict the equality.
3. It is not sure about other comparsons such like parallel and
perpendicular, but it is hardly usable without any amount of
tolerance.
On the other hand, some people complains about the following.
A. The geometric comparisons are not in relationships that
analogously deducible (or expected) from scalar comparisons.
B. The tolerance seems prevents us from using indexes.
As a presupposition, we have the common knowlegde, or principle,
about floating point handling on a digital computer.
X. We shouldn't detect equalty of two floating point numbers just
using '=' for real usage. It hardly works for arbitrary
numbers including results of any arithmetics, so it should
incorporate any amount of tolerance that differs among
applications.
Sorry for some refrains from the past discussion, but the above X
and 1-3 are the immovable basis in this area. (I belive it . But
any objection are welcome)
You might want to object that float has an equality operator, but
it is because it is an atomic type, which has no specific
purpose. On the contrary geometric types are application types,
which is assumed specific purpose and it should behave
reasonablly against the expected purpose.
That being said, if we want to, for example, use btree index for
each coordinate of geometric types, it is doable by normalizing
the values before applying operators, instead of applying epsilon
arbitrarily in operators.
Normalization may be done both on storing or on-the-fly just
befor usage. It depends on whether we want to preserve precision
of stored values. If we want, normalization should be done just
before comparisons, or on creating indexes.
I don't know much about the normalization method for floating
point numbers but libgeos might give some hints.
And still I don't have an idea how to detect parallelity and
perpendicularity based on the normalization.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I am responding both of your emails together.
Perhaps I don't understand it. Many opclass are defined for
btree. But since (ordinary) btree handles only one-dimentional,
totally-orderable sets, geometric types even point don't fit
it. Even if we relaxed it by removing EPSILON, multidimentional
data still doesn't fit.
Yes, multidimentional data doesn't fit into btree. Let's say we would
have to introduce operators called *<, *<=, *>, *>= to support btree
opclass for point. I agree those operators would be useless, but
btree opclass is used for other purposes too. "ORDER BY point",
merge-joins, btree index support for ~= would be useful.
Lack of ORDER BY, GROUP BY, and DISTINCT support is the major
inconvenience about the geometric types. There are many complaints
about this on the mailing list. Many frameworks and ORMs are not
prepared to deal with getting an error when they use certain types on
those clauses.
- Only some operators are using the epsilon.
I included the list of the ones which doesn't use the epsilon on
initial post of this thread. This makes the operators not compatible
with each other. For example, you cannot say "if box_a @> box_b and
box_b @> point then box_a @> box_b".(It must be "then box_a @> point".)
Yes, I am sorry.
Both of the operators don't
seem to use EPSILON so the transitivity holds, but perhaps we
should change them to more appropriate shape, that is, to use
EPSILON. Even having the tolerance, we can define the operators
to keep the transitivity.
Yes, that would be another way. In my view, this would make things
worse, because I believe epsilon approach is completely broken. The
operators which are not using the epsilon are the only ones people can
sanely use to develop applications.
- The application of epsilon is implementation dependent.
Epsilon works between two numbers. There is not always a single way
of implementing geometric operators. This is surprising to the users.
For example, you cannot say "if box_a @> box_b then box_a <-> box_b <=
epsilon".Maybe it should be like "if box_a ~= box_b && box_b @< box_a then
..". I'm not sure off-hand. But I don't see the relationship is
significant.
What I meant was the behaviour being unclear to even people who knows
about the epsilon. If two boxes are overlapping with each other, one
who knows about EPSILON can expect the distance between them to be
less than EPSILON, but this wouldn't be true.
- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
Whether EPSILON is introduced or not, commutativity cannot be
assured for it from calculation error, I suppose.
It can easily be assured by treating both sides of the operator the
same. It is actually assured on my patch.
- Some operators are violating transitive property.
For example, you cannot say "if point_a ~= point_b and point_b ~=
point_c then point_a ~= point_c".It holds only in ideal (or mathematical) world, or for similars
of integer (which are strictly implemented mathematica world on
computer). Without the EPSILON, two points hardly match by
floating point error, as I mentioned. I don't have an evidence
though (sorry), mere equality among three floating point numbers
is not assured.
Of course, it is assured. Floating point numbers are deterministic.
- The operators with epsilon are not compatible with float operators.
This is also surprising for the users. For example, you cannot say
"if point_a << point_b then point_a[0] < point_b[0]".It is because "is strictly left of" just doesn't mean their
x-coordinates are in such a relationship. The difference might be
surprising but is a specification (truely not a bug:).
Then what does that mean? Every operator with EPSILON is currently
surprising in different way. People use databases to build
applications. They often need to implement same operations on the
application side. It is very hard to be bug-compatible with our
geometric types.
= Missing Bits on the Operator Classes
This final section seems to mention the application but sorry, it
still don't describe for me what application that this patch
aiming to enable. But I can understand that you want to handle
floating point numbers like integers. The epsilon is surely quite
annoying for the purpose.
I will try to itemise what applications I am aiming to enable:
- Applications with very little GIS requirement
PostGIS can be too much work for an application in s different
business domain but has a little requirement to GIS. The built-in
geometric types are incomplete to support real world geography.
Getting rid of epsilon would make this worse. In contrary, it would
allow people to deal with the difficulties on their own.
- Applications with geometric objects
I believe people could make use the geometric types in many different
business areas, if they would be in better shape. I am working for a
gaming company. There is great overlap between the logic of many
games and the geometric types. I could support using them more, if
they wouldn't be so buggy and impractical.
- Demonstrating indexes
We require indexing features to be committed with an example.
Geometric types have served this purpose many times. Many operators
are actually added to demonstrate indexes. These examples are also
used by other projects like PostGIS. They can re-use part of the
implementations, or the implementation as a starting point. I believe
we should make those examples as clean as possible.
Ok, returning to the basis of the current geometric types, it is
described as below. It is not perfect (so needs amendment) but
achived to a certain degree.1. For geometric types, equality needs to be defined with some
tolerance. Concretely an absolute value of 1E-06, which is
quite random but would be suitable to ignore error of
longitude and latitude.
I don't agree this requirement as it is put. Returning true to
something like "point(0.000001, 0.000001) ~= point(0.000002,
0.000002)" is obviously wrong.
2. Translative comparisons should consider the tolerance to make
a result that does not contradict the equality.
The current implementation fails to provide it:
hasegeli=# select point(0.000001, 0.000001) ~= point(0.000002, 0.000002);
?column?
----------
t
(1 row)hasegeli=# select point(0.000001, 0.000001) << point(0.0000021, 0.0000021);
?column?
----------
t
(1 row)hasegeli=# select point(0.000002, 0.000002) << point(0.0000021, 0.0000021);
?column?
----------
f
(1 row)
3. It is not sure about other comparsons such like parallel and
perpendicular, but it is hardly usable without any amount of
tolerance.
I agree. I don't think they are useable with our tolerance either.
On the other hand, some people complains about the following.
I believe have listed enough complaints on my previous post.
As a presupposition, we have the common knowlegde, or principle,
about floating point handling on a digital computer.X. We shouldn't detect equalty of two floating point numbers just
using '=' for real usage. It hardly works for arbitrary
numbers including results of any arithmetics, so it should
incorporate any amount of tolerance that differs among
applications.Sorry for some refrains from the past discussion, but the above X
and 1-3 are the immovable basis in this area. (I belive it . But
any objection are welcome)You might want to object that float has an equality operator, but
it is because it is an atomic type, which has no specific
purpose. On the contrary geometric types are application types,
which is assumed specific purpose and it should behave
reasonablly against the expected purpose.
Are they really? We have really simple geometric types which are not
for any specific kind of application. We are not claiming that they
are for earth scale or any other scale. I think we should aim of
making them predictable, extensible, as general purpose as possible to
let people use them for their purpose.
That being said, if we want to, for example, use btree index for
each coordinate of geometric types, it is doable by normalizing
the values before applying operators, instead of applying epsilon
arbitrarily in operators.Normalization may be done both on storing or on-the-fly just
befor usage. It depends on whether we want to preserve precision
of stored values. If we want, normalization should be done just
before comparisons, or on creating indexes.I don't know much about the normalization method for floating
point numbers but libgeos might give some hints.
Me neither. I have been told that storing the minimum and and maximum
possible value is a good strategy for tolerance. Then all operators
can return true, false, or maybe. This would be a much bigger and
incompatible change that I don't think I can implement.
We cannot come into an agreement. Geometric types are not well
maintained. I am trying my best to put them into a shape. Please
help me do that. Let's try to find a way to leave them better than we
found.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello, Emre.
At Wed, 25 Jan 2017 12:24:18 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzzLWCdYDPLi_UQXLSJ+pqc7g4WGNHsb8tHkmosKK+rO7g@mail.gmail.com>
I am responding both of your emails together.
Perhaps I don't understand it. Many opclass are defined for
btree. But since (ordinary) btree handles only one-dimentional,
totally-orderable sets, geometric types even point don't fit
it. Even if we relaxed it by removing EPSILON, multidimentional
data still doesn't fit.Yes, multidimentional data doesn't fit into btree. Let's say we would
have to introduce operators called *<, *<=, *>, *>= to support btree
opclass for point. I agree those operators would be useless, but
btree opclass is used for other purposes too. "ORDER BY point",
merge-joins, btree index support for ~= would be useful.
Btree cannot be used unless any kind of total-orderedness is
defined but hash index would be usable so.
Lack of ORDER BY, GROUP BY, and DISTINCT support is the major
inconvenience about the geometric types. There are many complaints
about this on the mailing list. Many frameworks and ORMs are not
prepared to deal with getting an error when they use certain types on
those clauses.
Even though I'm not sure but I don't see a "natural" (or
agreeable by many poeple) ordering of geometric types in
general. Anyway it's quite application (not application program
but the relationship with the real world) specific.
- Only some operators are using the epsilon.
I included the list of the ones which doesn't use the epsilon on
initial post of this thread. This makes the operators not compatible
with each other. For example, you cannot say "if box_a @> box_b and
box_b @> point then box_a @> box_b".(It must be "then box_a @> point".)
Yes, I am sorry.
Both of the operators don't
seem to use EPSILON so the transitivity holds, but perhaps we
should change them to more appropriate shape, that is, to use
EPSILON. Even having the tolerance, we can define the operators
to keep the transitivity.Yes, that would be another way. In my view, this would make things
worse, because I believe epsilon approach is completely broken. The
operators which are not using the epsilon are the only ones people can
sanely use to develop applications.
What we should not forget is that PostGIS does the same thing and
it is widly used (I believe..). This means it not broken at least
on a certain context. But it is a fact that it also quite
inconvenient to get performance from, say, indexes.
- The application of epsilon is implementation dependent.
Epsilon works between two numbers. There is not always a single way
of implementing geometric operators. This is surprising to the users.
For example, you cannot say "if box_a @> box_b then box_a <-> box_b <=
epsilon".Maybe it should be like "if box_a ~= box_b && box_b @< box_a then
..". I'm not sure off-hand. But I don't see the relationship is
significant.What I meant was the behaviour being unclear to even people who knows
about the epsilon. If two boxes are overlapping with each other, one
who knows about EPSILON can expect the distance between them to be
less than EPSILON, but this wouldn't be true.
Yeah, the EPSILON is mere a fuzz factor so we cannot expect any
kind of stable behavior for differences under the level. But on
the analogy of comparisons of floating point numbers, I suppose
that inequality comparison could be done without the tolerance.
- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
Whether EPSILON is introduced or not, commutativity cannot be
assured for it from calculation error, I suppose.It can easily be assured by treating both sides of the operator the
same. It is actually assured on my patch.
It surely holds for certain cases. Even how many applicable cases
we guess, finally we cannot proof that it works generally. Just
three times of 1/3 rotation breakes it.
- Some operators are violating transitive property.
For example, you cannot say "if point_a ~= point_b and point_b ~=
point_c then point_a ~= point_c".It holds only in ideal (or mathematical) world, or for similars
of integer (which are strictly implemented mathematica world on
computer). Without the EPSILON, two points hardly match by
floating point error, as I mentioned. I don't have an evidence
though (sorry), mere equality among three floating point numbers
is not assured.Of course, it is assured. Floating point numbers are deterministic.
Hmm, I have nothing more to say if you don't agree that floating
point numbers involving any kind of arithmetic is hardly
deterministic especially not defining its usage.
- The operators with epsilon are not compatible with float operators.
This is also surprising for the users. For example, you cannot say
"if point_a << point_b then point_a[0] < point_b[0]".It is because "is strictly left of" just doesn't mean their
x-coordinates are in such a relationship. The difference might be
surprising but is a specification (truely not a bug:).Then what does that mean? Every operator with EPSILON is currently
surprising in different way. People use databases to build
applications. They often need to implement same operations on the
application side. It is very hard to be bug-compatible with our
geometric types.
The world of the geometric types in PostgreSQL *is* built
so. There is nothing different with that Windows client can make
different results from PostgreSQL on a Linux server for a simple
floating point arithmetics, or even different binaries made by
different version of compilers on the same platoform can. Relying
on such coherency by accident is a bad policy.
= Missing Bits on the Operator Classes
This final section seems to mention the application but sorry, it
still don't describe for me what application that this patch
aiming to enable. But I can understand that you want to handle
floating point numbers like integers. The epsilon is surely quite
annoying for the purpose.I will try to itemise what applications I am aiming to enable:
- Applications with very little GIS requirement
PostGIS can be too much work for an application in s different
business domain but has a little requirement to GIS. The built-in
geometric types are incomplete to support real world geography.
Getting rid of epsilon would make this worse. In contrary, it would
allow people to deal with the difficulties on their own.- Applications with geometric objects
I believe people could make use the geometric types in many different
business areas, if they would be in better shape. I am working for a
gaming company. There is great overlap between the logic of many
games and the geometric types. I could support using them more, if
they wouldn't be so buggy and impractical.- Demonstrating indexes
We require indexing features to be committed with an example.
Geometric types have served this purpose many times. Many operators
are actually added to demonstrate indexes. These examples are also
used by other projects like PostGIS. They can re-use part of the
implementations, or the implementation as a starting point. I believe
we should make those examples as clean as possible.Ok, returning to the basis of the current geometric types, it is
described as below. It is not perfect (so needs amendment) but
achived to a certain degree.1. For geometric types, equality needs to be defined with some
tolerance. Concretely an absolute value of 1E-06, which is
quite random but would be suitable to ignore error of
longitude and latitude.I don't agree this requirement as it is put. Returning true to
something like "point(0.000001, 0.000001) ~= point(0.000002,
0.000002)" is obviously wrong.
No. It can be right. For example, it is right for an application
where the precision of data is 0.1 or 1E-6. But it is wrong if
the tolerance designed for its purpose is 1E-7.
2. Translative comparisons should consider the tolerance to make
a result that does not contradict the equality.The current implementation fails to provide it:
hasegeli=# select point(0.000001, 0.000001) ~= point(0.000002, 0.000002);
?column?
----------
t
(1 row)hasegeli=# select point(0.000001, 0.000001) << point(0.0000021, 0.0000021);
?column?
----------
t
(1 row)hasegeli=# select point(0.000002, 0.000002) << point(0.0000021, 0.0000021);
?column?
----------
f
(1 row)
So this example without any concrete application is
irrelevant.
3. It is not sure about other comparsons such like parallel and
perpendicular, but it is hardly usable without any amount of
tolerance.I agree. I don't think they are useable with our tolerance either.
On the other hand, some people complains about the following.
I believe have listed enough complaints on my previous post.
As a presupposition, we have the common knowlegde, or principle,
about floating point handling on a digital computer.X. We shouldn't detect equalty of two floating point numbers just
using '=' for real usage. It hardly works for arbitrary
numbers including results of any arithmetics, so it should
incorporate any amount of tolerance that differs among
applications.Sorry for some refrains from the past discussion, but the above X
and 1-3 are the immovable basis in this area. (I belive it . But
any objection are welcome)You might want to object that float has an equality operator, but
it is because it is an atomic type, which has no specific
purpose. On the contrary geometric types are application types,
which is assumed specific purpose and it should behave
reasonablly against the expected purpose.Are they really? We have really simple geometric types which are not
PostGIS or libgeos seems to prove it. They are designed exactly
for this purpose and actually used.
for any specific kind of application. We are not claiming that they
are for earth scale or any other scale. I think we should aim of
making them predictable, extensible, as general purpose as possible to
let people use them for their purpose.
If settable tolerance, and preferablly, with definable method
were introduced, it could be said that generic geometric types.
That being said, if we want to, for example, use btree index for
each coordinate of geometric types, it is doable by normalizing
the values before applying operators, instead of applying epsilon
arbitrarily in operators.Normalization may be done both on storing or on-the-fly just
befor usage. It depends on whether we want to preserve precision
of stored values. If we want, normalization should be done just
before comparisons, or on creating indexes.I don't know much about the normalization method for floating
point numbers but libgeos might give some hints.Me neither. I have been told that storing the minimum and and maximum
possible value is a good strategy for tolerance. Then all operators
can return true, false, or maybe. This would be a much bigger and
incompatible change that I don't think I can implement.We cannot come into an agreement. Geometric types are not well
maintained. I am trying my best to put them into a shape. Please
help me do that. Let's try to find a way to leave them better than we
found.
Although some operators have apparently wrong implement and
fixing it would be a progress, it is not a progress toward your
objective if I don't agree to just remove the tolerance.
# Sorry for the long detour..
So, the union of the two requirements seems to be having such
parameter as a GUC.
Geometric types don't work without any amount of tolerance for
many cases, and surely some people needs the EPSILON but the
amout is quite arbitrary. On the other hand, some other people
want no-tolerance. This seems enough reason to make it variable.
If there's no objection for the above, how about proceeding by
the following steps?
1: Replace fixed EPSILON with a guc parameter
'geometric_tolerance' or such and its default value is 1E-6.
This also should have documentation.
2: Fix some currently broken operators to give sane results,
considring geometric_tolerance.
Once we get to this stage, setting geometric_tolerance to 0 would
give the preferable behavior for you, and it is mere a concrete
value for a specific application. No problem.
After that, perhaps we may add some indexing method for geometric
types not considering tolerance. Then someone may want make them
support geometric_tolerance.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Even though I'm not sure but I don't see a "natural" (or
agreeable by many poeple) ordering of geometric types in
general. Anyway it's quite application (not application program
but the relationship with the real world) specific.
We can just define it for point as "ORDER BY point.x, point.y".
What we should not forget is that PostGIS does the same thing and
it is widly used (I believe..). This means it not broken at least
on a certain context. But it is a fact that it also quite
inconvenient to get performance from, say, indexes.
I understand from Paul Ramsey's email [1]/messages/by-id/CACowWR0DBEjCfBscKKumdRLJUkObjB7D=iw7-0_ZwSFJM9_gpw@mail.gmail.com on this thread that PostGIS
doesn't currently have a tolerance.
Yeah, the EPSILON is mere a fuzz factor so we cannot expect any
kind of stable behavior for differences under the level. But on
the analogy of comparisons of floating point numbers, I suppose
that inequality comparison could be done without the tolerance.
What do you mean exactly?
- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
Whether EPSILON is introduced or not, commutativity cannot be
assured for it from calculation error, I suppose.It can easily be assured by treating both sides of the operator the
same. It is actually assured on my patch.It surely holds for certain cases. Even how many applicable cases
we guess, finally we cannot proof that it works generally. Just
three times of 1/3 rotation breakes it.
It is a different story. We cannot talk about commutative property of
rotation function that we currently don't have, because it would be an
asymmetrical operator.
The parallel operator is currently marked as commutative. The planner
is free to switch the sides of the operation. Therefore this is not
only a surprise, but a bug.
Hmm, I have nothing more to say if you don't agree that floating
point numbers involving any kind of arithmetic is hardly
deterministic especially not defining its usage.
The floating point results are not random. There are certain
guarantees. The transitive property of equality is one of them. Our
aim should be making things easier for our users by providing more
guarantees not breaking what is already there.
The world of the geometric types in PostgreSQL *is* built
so. There is nothing different with that Windows client can make
different results from PostgreSQL on a Linux server for a simple
floating point arithmetics, or even different binaries made by
different version of compilers on the same platoform can. Relying
on such coherency by accident is a bad policy.
Yes, the results are not portable. We should only rely on the results
being stable on the same build. The epsilon doesn't cure this
problem. It arguably makes it worse.
PostGIS or libgeos seems to prove it. They are designed exactly
for this purpose and actually used.
Yes, PostGIS is a GIS application. We are not. Geometric types name
suggests to me them being useful for general purpose.
So, the union of the two requirements seems to be having such
parameter as a GUC.
That sounds doable to me. We can use this opportunity to make all
operators consistent. So the epsilon would apply to the ones that it
current is not. We can still add btree and hash opclasses, and make
them give an error when this GUC is not 0. We can even make this or
another GUC apply to floats making whole system more consistent.
Though, I know the community is against behaviour changing GUCs. I
will not spend more time on this, before I get positive feedback from
others.
[1]: /messages/by-id/CACowWR0DBEjCfBscKKumdRLJUkObjB7D=iw7-0_ZwSFJM9_gpw@mail.gmail.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Thu, 26 Jan 2017 11:53:28 +0100, Emre Hasegeli <emre@hasegeli.com> wrote in <CAE2gYzwYLcx-3ffToVm7JEEniYt1fU31y5BAikXzEqvCbQyTMg@mail.gmail.com>
Even though I'm not sure but I don't see a "natural" (or
agreeable by many poeple) ordering of geometric types in
general. Anyway it's quite application (not application program
but the relationship with the real world) specific.We can just define it for point as "ORDER BY point.x, point.y".
It's nonsense. Totally for a convenient. Anyone can do so on
their own application but PostgreSQL cannot have it as a
platform.
What we should not forget is that PostGIS does the same thing and
it is widly used (I believe..). This means it not broken at least
on a certain context. But it is a fact that it also quite
inconvenient to get performance from, say, indexes.I understand from Paul Ramsey's email [1] on this thread that PostGIS
doesn't currently have a tolerance.
Thank you for the pointer. (My memory is too small as 8bit CPU)
Looking into the source of PostGIS 2.3.0 (Maybe the latest)
Surely EPSILON is used is someplaces. FPeq and similars are also
defined. liblwgeom_internal.h even defines another tolerance
EPSILON_SQLMM..
Paul> The real answer for a GIS system is to have an explicit tolerance
Paul> parameter for calculations like distance/touching/containment, but
Paul> unfortunately we didn't do that so now we have our own
Paul> compatibility/boil the ocean problem if we ever wanted/were funded to
Paul> add one.
This doesn't seem saying PostGIS doesn't have fixed-amount
tolerance. Alhtough we don't necessarily need compatibility with
PostGIS, it makes the problem rather complex since we lose an
apparent start point for this. It would be good if we make geom
comparators to have explicit tolerances as Paul said but maybe it
is overdone. So I proposed the varialbe implicit tolerance.
Yeah, the EPSILON is mere a fuzz factor so we cannot expect any
kind of stable behavior for differences under the level. But on
the analogy of comparisons of floating point numbers, I suppose
that inequality comparison could be done without the tolerance.What do you mean exactly?
Sorry for poor wording. I'll try in different way. It means
comparison on numbers that contains certain amount of error gives
unstable result for differences small enough comparing their
precision.
- Some operators are violating commutative property.
For example, you cannot say "if line_a ?|| line_b then line_b ?|| line_a".
Whether EPSILON is introduced or not, commutativity cannot be
assured for it from calculation error, I suppose.It can easily be assured by treating both sides of the operator the
same. It is actually assured on my patch.It surely holds for certain cases. Even how many applicable cases
we guess, finally we cannot proof that it works generally. Just
three times of 1/3 rotation breakes it.It is a different story. We cannot talk about commutative property of
rotation function that we currently don't have, because it would be an
asymmetrical operator.
That's wrong. Any shpaes represented by geometric types assumed
to get any geometric operatsions such as transision, rotation and
others. It is fundamental for geometric types.
The parallel operator is currently marked as commutative. The planner
is free to switch the sides of the operation. Therefore this is not
only a surprise, but a bug.
Strictly it is not commutative, but assuming FP error or EPSILON,
commutation among them would be acceptable. Having larger
tolerance, the defference from commutated expression becomes
relatively small for the type's domain. As far as I know, even
summation of two floating points is not guaranteed to yield
strictly the same result for commutated opeation. But the
difference doesn't affect for most cases and programmers make a
program so that such differences don't matter in the
objective. This is basically the same thing with the case of
geo-types.
Hmm, I have nothing more to say if you don't agree that floating
point numbers involving any kind of arithmetic is hardly
deterministic especially not defining its usage.The floating point results are not random. There are certain
guarantees. The transitive property of equality is one of them. Our
aim should be making things easier for our users by providing more
guarantees not breaking what is already there.
Sorry for repeating the same thing but floating point numbers
after getting arithmetic operations must considered that they
have fuzziness or error (the same can occur for even just after
assigning). They must not be handled as exact numbers. Please
study about handling floating point numbers. If such strictness
is required and no arithmetic involved, it is proper to store
them, say, in a string form.
If such a behavior is required but want to use floating points,
maybe it is easier to create a extension conforming such a
specification, rather than chainging the core behavior.
The world of the geometric types in PostgreSQL *is* built
so. There is nothing different with that Windows client can make
different results from PostgreSQL on a Linux server for a simple
floating point arithmetics, or even different binaries made by
different version of compilers on the same platoform can. Relying
on such coherency by accident is a bad policy.Yes, the results are not portable. We should only rely on the results
being stable on the same build. The epsilon doesn't cure this
problem. It arguably makes it worse.
Yes, EPSILON doesn't improve such cross-platform consistency. It
is totally a different issue. Such exact consistency should not
be expected regardless of EPSILON.
PostGIS or libgeos seems to prove it. They are designed exactly
for this purpose and actually used.Yes, PostGIS is a GIS application. We are not. Geometric types name
suggests to me them being useful for general purpose.So, the union of the two requirements seems to be having such
parameter as a GUC.That sounds doable to me. We can use this opportunity to make all
operators consistent. So the epsilon would apply to the ones that it
current is not. We can still add btree and hash opclasses, and make
them give an error when this GUC is not 0. We can even make this or
another GUC apply to floats making whole system more consistent.
Maybe.
Though, I know the community is against behaviour changing GUCs. I
will not spend more time on this, before I get positive feedback from
others.
That's too bad. I'm sorry that I wans't very helpful..
[1] /messages/by-id/CACowWR0DBEjCfBscKKumdRLJUkObjB7D=iw7-0_ZwSFJM9_gpw@mail.gmail.com
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jan 26, 2017 at 9:11 PM, Kyotaro HORIGUCHI
<horiguchi.kyotaro@lab.ntt.co.jp> wrote:
Though, I know the community is against behaviour changing GUCs. I
will not spend more time on this, before I get positive feedback from
others.That's too bad. I'm sorry that I wasn't very helpful..
You make a constructive discussion in a way that you think makes sense
by providing feedback, there's nothing bad in that IMO, quite the
contrary to be honest.
(I am just showing up here to tell that I have moved this patch to CF 2017-03).
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jan 26, 2017 at 5:53 AM, Emre Hasegeli <emre@hasegeli.com> wrote:
Though, I know the community is against behaviour changing GUCs. I
will not spend more time on this, before I get positive feedback from
others.
As if on cue, let me say that a behavior-changing GUC sounds like a
terrible idea to me. It's almost never good when a GUC can cause the
same queries to return answers in different sessions, and even worse,
it seems like the GUC might have the effect of letting us build
indexes that are only valid for the value of the GUC with which they
were built.
Backing up a bit here, have we lost track of the problem that we're
trying to solve? Tom gave his opinion here:
/messages/by-id/3895.1464791274@sss.pgh.pa.us
But I don't see that the latest patch I can find does anything to fix
that. Now maybe that's not the problem that Emre is trying to solve,
but then it is not very clear to me what problem he IS trying to
solve. And I think Kyotaro Horiguchi is trying to solve yet another
problem which is again different. So IMHO the first task here is to
agree on a clear statement of what we'd like to fix, and then, given a
patch, we can judge whether it's fixed.
Maybe I'm being dumb here and it's clear to you guys what the issues
under discussion are. If so, apologies for that, but the thread has
gotten too theoretical for me and I can't figure out what the
top-level concern is any more. I believe we all agree these macros
are bad, but there seems to be no agreement that I can discern on what
would be better or why.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Backing up a bit here, have we lost track of the problem that we're
trying to solve? Tom gave his opinion here:/messages/by-id/3895.1464791274@sss.pgh.pa.us
But I don't see that the latest patch I can find does anything to fix
that.
This is what he wrote:
As I understand it, the key problem is that tests like "is point on line"
would basically never succeed except in the most trivial cases, because of
roundoff error. That's not very nice, and it might cascade to larger
problems like object-containment tests failing unexpectedly. We would
need to go through all the geometric operations and figure out where that
kind of gotcha is significant and what we can do about it. Seems like a
fair amount of work :-(. If somebody's willing to do that kind of
investigation, then sure, but I don't think just blindly removing these
macros is going to lead to anything good.
I re-implemented "is point on line" operator on my patch so that it
would, at least, work on the most trivial cases. This is not very
nice, but it is predictable. I have tried to prevent it cascade to
larger problems like object-containment tests failing unexpectedly. I
have gone through all of the geometric operations and re-implemented
the ones with similar problems, too. It is no work at all compared to
discussions we have to have :-).
My initial idea was to keep the fuzzy behaviour for some operators
like "is point on line", but the more I get into it the less value I
see in doing so. The same family operators like "is point on line" is
currently badly broken:
postgres=# select '(1,0)'::point ## '{1,2,0}'::line;
?column?
----------
(2,2)
(1 row)
This makes me wonder if anybody is ever using those operators. In the
end, I don't care about those operators. They are unlikely to be
supported by indexes. I can simplify my patch to leave them as they
are.
Now maybe that's not the problem that Emre is trying to solve,
but then it is not very clear to me what problem he IS trying to
solve. And I think Kyotaro Horiguchi is trying to solve yet another
problem which is again different. So IMHO the first task here is to
agree on a clear statement of what we'd like to fix, and then, given a
patch, we can judge whether it's fixed.
I listed the problems I am trying to solve in here:
/messages/by-id/CAE2gYzzNufOZqh4HO3Od8urzamNSeX-OXJxfNkRcU3ex9RD8jw@mail.gmail.com
Maybe I'm being dumb here and it's clear to you guys what the issues
under discussion are. If so, apologies for that, but the thread has
gotten too theoretical for me and I can't figure out what the
top-level concern is any more. I believe we all agree these macros
are bad, but there seems to be no agreement that I can discern on what
would be better or why.
We couldn't agree on how we should treat on floating point numbers. I
think we should threat them just like the "float" datatype.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Jan 31, 2017 at 1:06 PM, Emre Hasegeli <emre@hasegeli.com> wrote:
This is what he wrote:
As I understand it, the key problem is that tests like "is point on line"
would basically never succeed except in the most trivial cases, because of
roundoff error. That's not very nice, and it might cascade to larger
problems like object-containment tests failing unexpectedly. We would
need to go through all the geometric operations and figure out where that
kind of gotcha is significant and what we can do about it. Seems like a
fair amount of work :-(. If somebody's willing to do that kind of
investigation, then sure, but I don't think just blindly removing these
macros is going to lead to anything good.I re-implemented "is point on line" operator on my patch so that it
would, at least, work on the most trivial cases.
Right. So you and he do not agree on the goal. He wants it to work
in MORE than the most trivial cases, and you wrote it so that it will
work in AT LEAST the most trivial cases. Those are not the same.
I listed the problems I am trying to solve in here:
/messages/by-id/CAE2gYzzNufOZqh4HO3Od8urzamNSeX-OXJxfNkRcU3ex9RD8jw@mail.gmail.com
Those problems boil down to "let's get rid of the tolerance
completely", and there's no consensus on that being a good thing to
do. In particular, I can't remember anybody but you being
unequivocally in favor of it.
We couldn't agree on how we should treat on floating point numbers. I
think we should threat them just like the "float" datatype.
Got it, but if other people don't agree then this is going nowhere.
Personally, I've got my doubts about how sane it is to make anything
work here with the built-in floating point types. The CPU is going to
do some kind of black magic, which may differ between machines, and
good luck getting consistent semantics that work everywhere. For
example it seems entirely plausible (with some implementation) that
you might find that (1, 2) is on the line from (0, 0) to (2, 4) but
that (1, 3) is not on the line from (0, 0) to (2, 6) because the slope
1/2 can be represented exactly in base 2 and the slope 1/3 cannot. If
you try to fix that kind of thing by introducing a tolerance, then you
might get it wrong when the line goes from (0, 0) to (1000000000,
2000000001). Now, if we represented values using home-grown
arbitrary-precision arithmetic - like numeric does - then it might be
possible to get such cases right, especially if you supported exact
rather than approximate representations of fractional quantities.
Otherwise, I think there are inevitably going to be artifacts, and
tinkering with this is just changing which artifacts we get without
really solving the problem. But I am notorious for my low opinion of
floating-point arithmetic; maybe somebody who understands it better or
likes it more can come up with something that's more clearly an
improvement. Regardless, we have to have an agreement in order to
commit anything here, and I don't see one.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Got it, but if other people don't agree then this is going nowhere.
Yes. As I wrote, I don't particularly care about functions like "is
point on line". I can prepare a patch to fix as many problems as
possible around those operators by preserving the current epsilon.
I though we were arguing about *all* operators. Having containment
and placement operators consistent with each other, is the primary
thing I am trying to fix. Is removing epsilon from them is
acceptable?
We can also stay away from changing operators like "~=" to minimise
compatibility break, if we keep the epsilon on some places. We can
instead document this operator as "close enough", and introduce
another symbol for really "the same" operator.
That said, there are some places where it is hard to decide to apply
the epsilon or not. For example, we can keep the epsilon to check for
two lines being parallel, but then should we return the intersection
point, or not? Those issues may become more clear when I start
working on it, if preserving epsilon for those operators is the way to
go forward.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2/1/17 6:36 AM, Emre Hasegeli wrote:
Got it, but if other people don't agree then this is going nowhere.
Yes. As I wrote, I don't particularly care about functions like "is
point on line". I can prepare a patch to fix as many problems as
possible around those operators by preserving the current epsilon.I though we were arguing about *all* operators. Having containment
and placement operators consistent with each other, is the primary
thing I am trying to fix. Is removing epsilon from them is
acceptable?We can also stay away from changing operators like "~=" to minimise
compatibility break, if we keep the epsilon on some places. We can
instead document this operator as "close enough", and introduce
another symbol for really "the same" operator.That said, there are some places where it is hard to decide to apply
the epsilon or not. For example, we can keep the epsilon to check for
two lines being parallel, but then should we return the intersection
point, or not? Those issues may become more clear when I start
working on it, if preserving epsilon for those operators is the way to
go forward.
The current patches do not apply cleanly at cccbdde:
$ git apply ../other/0001-float-header-v03.patch
error: patch failed: contrib/btree_gist/btree_ts.c:1
error: contrib/btree_gist/btree_ts.c: patch does not apply
error: patch failed: contrib/postgres_fdw/postgres_fdw.c:26
error: contrib/postgres_fdw/postgres_fdw.c: patch does not apply
error: patch failed: src/backend/access/gist/gistutil.c:14
error: src/backend/access/gist/gistutil.c: patch does not apply
error: patch failed: src/backend/utils/adt/float.c:339
error: src/backend/utils/adt/float.c: patch does not apply
error: patch failed: src/backend/utils/adt/geo_ops.c:14
error: src/backend/utils/adt/geo_ops.c: patch does not apply
error: patch failed: src/backend/utils/misc/guc.c:68
error: src/backend/utils/misc/guc.c: patch does not apply
error: patch failed: src/include/utils/builtins.h:334
error: src/include/utils/builtins.h: patch does not apply
I don't believe this patch should be in the "Needs review" state anyway.
There are clearly a number of issues that need work and agreement.
Given that this thread has been idle since the beginning of February and
no resolution is likely for v10, I'm marking this submission "Returned
with Feedback".
--
-David
david@pgmasters.net
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers