Re: Issue with point_ops and NaN - Mailing list pgsql-hackers

From Kyotaro Horiguchi
Subject Re: Issue with point_ops and NaN
Date
Msg-id 20210401.154606.322292475912950124.horikyota.ntt@gmail.com
Whole thread Raw
In response to Re: Issue with point_ops and NaN  (Kyotaro Horiguchi <horikyota.ntt@gmail.com>)
List pgsql-hackers
At Thu, 01 Apr 2021 09:34:40 +0900 (JST), Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote in 
> I have to change almost all boolean-returning functions to
> tri-state-boolean ones. I'll give it try a bit futther.

The attached is a rush work of that, on top of the (rebased version of
the) base patch.  Disregarding its uneffectiveness, it gives a rough
estimate of how large that would be and how that affects other parts.

Maybe one of the largest issue with that would be that GiST doesn't
seem to like NULL to be returned from comparison functions.


regression=# set enable_seqscan to off;
regression=# set enable_indexscan to on;
regression=# SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1) ORDER BY area(f1);
ERROR:  function 0x9d7bf6 returned NULL
(function 0x9d7bf6 is box_overlap())

That seems like the reason not to make the functions tri-state.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index a2e798ff95..b9ff60f56b 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -79,7 +79,7 @@ static inline void point_add_point(Point *result, Point *pt1, Point *pt2);
 static inline void point_sub_point(Point *result, Point *pt1, Point *pt2);
 static inline void point_mul_point(Point *result, Point *pt1, Point *pt2);
 static inline void point_div_point(Point *result, Point *pt1, Point *pt2);
-static inline bool point_eq_point(Point *pt1, Point *pt2);
+static inline tsbool point_eq_point(Point *pt1, Point *pt2);
 static inline float8 point_dt(Point *pt1, Point *pt2);
 static inline float8 point_sl(Point *pt1, Point *pt2);
 static int    point_inside(Point *p, int npts, Point *plist);
@@ -88,18 +88,18 @@ static int    point_inside(Point *p, int npts, Point *plist);
 static inline void line_construct(LINE *result, Point *pt, float8 m);
 static inline float8 line_sl(LINE *line);
 static inline float8 line_invsl(LINE *line);
-static bool line_interpt_line(Point *result, LINE *l1, LINE *l2);
-static bool line_contain_point(LINE *line, Point *point);
+static tsbool line_interpt_line(Point *result, LINE *l1, LINE *l2);
+static tsbool line_contain_point(LINE *line, Point *point);
 static float8 line_closept_point(Point *result, LINE *line, Point *pt);
 
 /* Routines for line segments */
 static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
 static inline float8 lseg_sl(LSEG *lseg);
 static inline float8 lseg_invsl(LSEG *lseg);
-static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
-static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
+static tsbool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
+static tsbool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
 static int    lseg_crossing(float8 x, float8 y, float8 px, float8 py);
-static bool lseg_contain_point(LSEG *lseg, Point *point);
+static tsbool lseg_contain_point(LSEG *lseg, Point *point);
 static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
 static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
 static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
@@ -107,14 +107,14 @@ static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
 /* Routines for boxes */
 static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
 static void box_cn(Point *center, BOX *box);
-static bool box_ov(BOX *box1, BOX *box2);
+static tsbool box_ov(BOX *box1, BOX *box2);
 static float8 box_ar(BOX *box);
 static float8 box_ht(BOX *box);
 static float8 box_wd(BOX *box);
-static bool box_contain_point(BOX *box, Point *point);
-static bool box_contain_box(BOX *contains_box, BOX *contained_box);
-static bool box_contain_lseg(BOX *box, LSEG *lseg);
-static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
+static tsbool box_contain_point(BOX *box, Point *point);
+static tsbool box_contain_box(BOX *contains_box, BOX *contained_box);
+static tsbool box_contain_lseg(BOX *box, LSEG *lseg);
+static tsbool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
 static float8 box_closept_point(Point *result, BOX *box, Point *point);
 static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
 
@@ -124,9 +124,9 @@ static float8 circle_ar(CIRCLE *circle);
 /* Routines for polygons */
 static void make_bound_box(POLYGON *poly);
 static void poly_to_circle(CIRCLE *result, POLYGON *poly);
-static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
-static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
-static bool plist_same(int npts, Point *p1, Point *p2);
+static tsbool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
+static tsbool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
+static tsbool plist_same(int npts, Point *p1, Point *p2);
 static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
 
 /* Routines for encoding and decoding */
@@ -540,9 +540,13 @@ box_same(PG_FUNCTION_ARGS)
 {
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
+    tsbool        res1 = point_eq_point(&box1->high, &box2->high);
+    tsbool        res2 = point_eq_point(&box1->low, &box2->low);
 
-    PG_RETURN_BOOL(point_eq_point(&box1->high, &box2->high) &&
-                   point_eq_point(&box1->low, &box2->low));
+    if (res1 == TS_NULL || res2 == TS_NULL)
+        PG_RETURN_NULL();
+
+    PG_RETURN_BOOL(res1 && res2);
 }
 
 /*        box_overlap        -        does box1 overlap box2?
@@ -553,16 +557,16 @@ 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));
+    PG_RETURN_TSBOOL(box_ov(box1, box2));
 }
 
-static bool
+static tsbool
 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 (TS_AND4(FPTle(box1->low.x, box2->high.x),
+                    FPTle(box2->low.x, box1->high.x),
+                    FPTle(box1->low.y, box2->high.y),
+                    FPTle(box2->low.y, box1->high.y)));
 }
 
 /*        box_left        -        is box1 strictly left of box2?
@@ -573,7 +577,7 @@ 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_TSBOOL(FPTlt(box1->high.x, box2->low.x));
 }
 
 /*        box_overleft    -        is the right edge of box1 at or left of
@@ -588,7 +592,7 @@ 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_TSBOOL(FPTle(box1->high.x, box2->high.x));
 }
 
 /*        box_right        -        is box1 strictly right of box2?
@@ -599,7 +603,7 @@ 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_TSBOOL(FPTgt(box1->low.x, box2->high.x));
 }
 
 /*        box_overright    -        is the left edge of box1 at or right of
@@ -614,7 +618,7 @@ 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_TSBOOL(FPTge(box1->low.x, box2->low.x));
 }
 
 /*        box_below        -        is box1 strictly below box2?
@@ -625,7 +629,7 @@ 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_TSBOOL(FPTlt(box1->high.y, box2->low.y));
 }
 
 /*        box_overbelow    -        is the upper edge of box1 at or below
@@ -637,7 +641,7 @@ 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_TSBOOL(FPTle(box1->high.y, box2->high.y));
 }
 
 /*        box_above        -        is box1 strictly above box2?
@@ -648,7 +652,7 @@ 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_TSBOOL(FPTgt(box1->low.y, box2->high.y));
 }
 
 /*        box_overabove    -        is the lower edge of box1 at or above
@@ -660,7 +664,7 @@ 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_TSBOOL(FPTge(box1->low.y, box2->low.y));
 }
 
 /*        box_contained    -        is box1 contained by box2?
@@ -671,7 +675,7 @@ box_contained(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_box(box2, box1));
+    PG_RETURN_TSBOOL(box_contain_box(box2, box1));
 }
 
 /*        box_contain        -        does box1 contain box2?
@@ -682,19 +686,19 @@ box_contain(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_box(box1, box2));
+    PG_RETURN_TSBOOL(box_contain_box(box1, box2));
 }
 
 /*
  * Check whether the second box is in the first box or on its border
  */
-static bool
+static tsbool
 box_contain_box(BOX *contains_box, BOX *contained_box)
 {
-    return FPge(contains_box->high.x, contained_box->high.x) &&
-        FPle(contains_box->low.x, contained_box->low.x) &&
-        FPge(contains_box->high.y, contained_box->high.y) &&
-        FPle(contains_box->low.y, contained_box->low.y);
+    return TS_AND4(FPTge(contains_box->high.x, contained_box->high.x),
+                   FPTle(contains_box->low.x, contained_box->low.x),
+                   FPTge(contains_box->high.y, contained_box->high.y),
+                   FPTle(contains_box->low.y, contained_box->low.y));
 }
 
 
@@ -712,7 +716,7 @@ 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_TSBOOL(FPTle(box1->high.y, box2->low.y));
 }
 
 Datum
@@ -721,7 +725,7 @@ 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_TSBOOL(FPTge(box1->low.y, box2->high.y));
 }
 
 
@@ -734,7 +738,7 @@ 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_TSBOOL(FPTlt(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -743,7 +747,7 @@ 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_TSBOOL(FPTgt(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -752,7 +756,7 @@ 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_TSBOOL(FPTeq(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -761,7 +765,7 @@ 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_TSBOOL(FPTle(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -770,7 +774,7 @@ 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_TSBOOL(FPTge(box_ar(box1), box_ar(box2)));
 }
 
 
@@ -981,7 +985,7 @@ line_in(PG_FUNCTION_ARGS)
     else
     {
         path_decode(s, true, 2, &lseg.p[0], &isopen, NULL, "line", str);
-        if (point_eq_point(&lseg.p[0], &lseg.p[1]))
+        if (point_eq_point(&lseg.p[0], &lseg.p[1]) == TS_TRUE)
             ereport(ERROR,
                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                      errmsg("invalid line specification: must be two distinct points")));
@@ -1105,7 +1109,7 @@ line_construct_pp(PG_FUNCTION_ARGS)
     LINE       *result = (LINE *) palloc(sizeof(LINE));
 
     /* NaNs are considered to be equal by point_eq_point */
-    if (point_eq_point(pt1, pt2))
+    if (point_eq_point(pt1, pt2) == TS_TRUE)
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                  errmsg("invalid line specification: must be two distinct points")));
@@ -1126,11 +1130,16 @@ line_intersect(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
     Point        xp;
+    tsbool        r;
 
-    if (line_interpt_line(&xp, l1, l2) && !isnan(xp.x) && !isnan(xp.y))
-        PG_RETURN_BOOL(true);
+    if ((r = line_interpt_line(&xp, l1, l2)) == TS_TRUE)
+    {
+        if (!isnan(xp.x) && !isnan(xp.y))
+            PG_RETURN_BOOL(true);
+        PG_RETURN_NULL();
+    }
     else
-        PG_RETURN_BOOL(false);
+        PG_RETURN_TSBOOL(r);
 }
 
 Datum
@@ -1139,7 +1148,7 @@ line_parallel(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(!line_interpt_line(NULL, l1, l2));
+    PG_RETURN_TSBOOL(TS_NOT(line_interpt_line(NULL, l1, l2)));
 }
 
 Datum
@@ -1148,17 +1157,18 @@ line_perp(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
 
-    if (unlikely(isnan(l1->C) || isnan(l2->C)))
-        return false;
+    if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
+                 isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
+        PG_RETURN_NULL();
 
     if (FPzero(l1->A))
-        PG_RETURN_BOOL(FPzero(l2->B) && !isnan(l1->B) && !isnan(l2->A));
+        PG_RETURN_BOOL(FPzero(l2->B));
     if (FPzero(l2->A))
-        PG_RETURN_BOOL(FPzero(l1->B) && !isnan(l2->B) && !isnan(l1->A));
+        PG_RETURN_BOOL(FPzero(l1->B));
     if (FPzero(l1->B))
-        PG_RETURN_BOOL(FPzero(l2->A) && !isnan(l1->A) && !isnan(l2->B));
+        PG_RETURN_BOOL(FPzero(l2->A));
     if (FPzero(l2->B))
-        PG_RETURN_BOOL(FPzero(l1->A) && !isnan(l2->A) && !isnan(l1->B));
+        PG_RETURN_BOOL(FPzero(l1->A));
 
     PG_RETURN_BOOL(FPeq(float8_div(float8_mul(l1->A, l2->A),
                                    float8_mul(l1->B, l2->B)), -1.0));
@@ -1177,7 +1187,10 @@ line_horizontal(PG_FUNCTION_ARGS)
 {
     LINE       *line = PG_GETARG_LINE_P(0);
 
-    PG_RETURN_BOOL(FPzero(line->A) && !isnan(line->B) && !isnan(line->C));
+    if (!isnan(line->B) && !isnan(line->C))
+        PG_RETURN_TSBOOL(FPzero(line->A));
+
+    PG_RETURN_NULL();
 }
 
 
@@ -1191,14 +1204,10 @@ line_eq(PG_FUNCTION_ARGS)
     LINE       *l2 = PG_GETARG_LINE_P(1);
     float8        ratio;
 
-    /* If any NaNs are involved, insist on exact equality */
+    /* If any NaNs are involved, the two cannot be equal */
     if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
                  isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
-    {
-        PG_RETURN_BOOL(float8_eq(l1->A, l2->A) &&
-                       float8_eq(l1->B, l2->B) &&
-                       float8_eq(l1->C, l2->C));
-    }
+        PG_RETURN_NULL();
 
     /* Otherwise, lines whose parameters are proportional are the same */
     if (!FPzero(l2->A))
@@ -1281,7 +1290,7 @@ line_distance(PG_FUNCTION_ARGS)
     Point        xp;
     float8        ratio;
 
-    if (line_interpt_line(&xp, l1, l2)) /* intersecting? */
+    if (line_interpt_line(&xp, l1, l2) == TS_TRUE) /* intersecting? */
     {
         /* return NaN if NaN is involved */
         if (isnan(xp.x) || isnan(xp.y))
@@ -1316,7 +1325,7 @@ line_interpt(PG_FUNCTION_ARGS)
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (!line_interpt_line(result, l1, l2) ||
+    if (line_interpt_line(result, l1, l2) != TS_TRUE ||
         isnan(result->x) || isnan(result->y))
     {
         pfree(result);
@@ -1340,17 +1349,24 @@ line_interpt(PG_FUNCTION_ARGS)
  * point would have NaN coordinates.  We shouldn't return false in this case
  * because that would mean the lines are parallel.
  */
-static bool
+static tsbool
 line_interpt_line(Point *result, LINE *l1, LINE *l2)
 {
     float8        x,
                 y;
+    tsbool        r;
 
-    if (!FPzero(l1->B))
+    if ((r = FPTzero(l1->B)) == TS_FALSE)
     {
         /* l1 is not virtucal */
-        if (FPeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
-            return false;
+        if ((r = FPTeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
+            != TS_FALSE)
+        {
+            if (r == TS_TRUE)
+                return TS_FALSE;
+            else
+                return TS_NULL;
+        }
 
         x = float8_div(float8_mi(float8_mul(l1->B, l2->C),
                                  float8_mul(l2->B, l1->C)),
@@ -1367,7 +1383,7 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
                        float8_mi(float8_mul(l2->A, l1->B),
                                  float8_mul(l1->A, l2->B)));
     }
-    else if (!FPzero(l2->B))
+    else if ((r = FPzero(l2->B)) == TS_FALSE)
     {
         /* l2 is not virtical */
         /*
@@ -1380,13 +1396,17 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
          * When l2->A is zero, y is determined independently from x even if it
          * is Inf.
          */
-        if (FPzero(l2->A))
+        if ((r = FPzero(l2->A)) == TS_TRUE)
             y = -float8_div(l2->C, l2->B);
-        else
+        else if (r == TS_FALSE)
             y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
+        else
+            return TS_NULL;
     }
+    else if (r != TS_NULL)
+        return TS_FALSE;
     else
-        return false;
+        return TS_NULL;
 
     /* On some platforms, the preceding expressions tend to produce -0. */
     if (x == 0.0)
@@ -1397,7 +1417,7 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
     if (result != NULL)
         point_construct(result, x, y);
 
-    return true;
+    return TS_TRUE;
 }
 
 
@@ -1704,6 +1724,7 @@ path_inter(PG_FUNCTION_ARGS)
                 j;
     LSEG        seg1,
                 seg2;
+    tsbool        r;
 
     Assert(p1->npts > 0 && p2->npts > 0);
 
@@ -1716,6 +1737,7 @@ path_inter(PG_FUNCTION_ARGS)
         b1.low.x = float8_min_nan(p1->p[i].x, b1.low.x);
         b1.low.y = float8_min_nan(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++)
@@ -1725,8 +1747,8 @@ path_inter(PG_FUNCTION_ARGS)
         b2.low.x = float8_min_nan(p2->p[i].x, b2.low.x);
         b2.low.y = float8_min_nan(p2->p[i].y, b2.low.y);
     }
-    if (!box_ov(&b1, &b2))
-        PG_RETURN_BOOL(false);
+    if ((r = box_ov(&b1, &b2)) != TS_TRUE)
+        PG_RETURN_TSBOOL(r);
 
     /* pairwise check lseg intersections */
     for (i = 0; i < p1->npts; i++)
@@ -2004,8 +2026,12 @@ point_eq(PG_FUNCTION_ARGS)
 {
     Point       *pt1 = PG_GETARG_POINT_P(0);
     Point       *pt2 = PG_GETARG_POINT_P(1);
+    tsbool        res = point_eq_point(pt1, pt2);
 
-    PG_RETURN_BOOL(point_eq_point(pt1, pt2));
+    if (res == TS_NULL)
+        PG_RETURN_NULL();
+
+    PG_RETURN_BOOL(res);
 }
 
 Datum
@@ -2013,23 +2039,22 @@ point_ne(PG_FUNCTION_ARGS)
 {
     Point       *pt1 = PG_GETARG_POINT_P(0);
     Point       *pt2 = PG_GETARG_POINT_P(1);
+    tsbool        res = point_eq_point(pt1, pt2);
 
-    PG_RETURN_BOOL(!point_eq_point(pt1, pt2));
+    if (res == TS_NULL)
+        PG_RETURN_NULL();
+
+    PG_RETURN_BOOL(!res);
 }
 
 
 /*
  * Check whether the two points are the same
  */
-static inline bool
+static inline tsbool
 point_eq_point(Point *pt1, Point *pt2)
 {
-    /* If any NaNs are involved, insist on exact equality */
-    if (unlikely(isnan(pt1->x) || isnan(pt1->y) ||
-                 isnan(pt2->x) || isnan(pt2->y)))
-        return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y));
-
-    return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
+    return TS_AND2(FPTeq(pt1->x, pt2->x), FPTeq(pt1->y, pt2->y));
 }
 
 
@@ -2070,6 +2095,7 @@ point_slope(PG_FUNCTION_ARGS)
 static inline float8
 point_sl(Point *pt1, Point *pt2)
 {
+    /* NaN doesn't equal to NaN, so don't bother using a tri-state value */
     if (FPeq(pt1->x, pt2->x))
     {
         if (unlikely(isnan(pt1->y) || isnan(pt2->y)))
@@ -2096,6 +2122,7 @@ point_sl(Point *pt1, Point *pt2)
 static inline float8
 point_invsl(Point *pt1, Point *pt2)
 {
+    /* NaN doesn't equal to NaN, so don't bother using a tri-state value */
     if (FPeq(pt1->x, pt2->x))
     {
         if (unlikely(isnan(pt1->y) || isnan(pt2->y)))
@@ -2254,7 +2281,7 @@ lseg_intersect(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(lseg_interpt_lseg(NULL, l1, l2));
+    PG_RETURN_TSBOOL(lseg_interpt_lseg(NULL, l1, l2));
 }
 
 
@@ -2264,7 +2291,7 @@ lseg_parallel(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_sl(l2)));
+    PG_RETURN_TSBOOL(FPTeq(lseg_sl(l1), lseg_sl(l2)));
 }
 
 /*
@@ -2276,7 +2303,7 @@ lseg_perp(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_invsl(l2)));
+    PG_RETURN_TSBOOL(FPTeq(lseg_sl(l1), lseg_invsl(l2)));
 }
 
 Datum
@@ -2284,7 +2311,7 @@ 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_TSBOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
 }
 
 Datum
@@ -2292,7 +2319,7 @@ 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_TSBOOL(FPTeq(lseg->p[0].y, lseg->p[1].y));
 }
 
 
@@ -2302,8 +2329,8 @@ lseg_eq(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(point_eq_point(&l1->p[0], &l2->p[0]) &&
-                   point_eq_point(&l1->p[1], &l2->p[1]));
+    PG_RETURN_TSBOOL(TS_AND2(point_eq_point(&l1->p[0], &l2->p[0]),
+                             point_eq_point(&l1->p[1], &l2->p[1])));
 }
 
 Datum
@@ -2312,8 +2339,9 @@ lseg_ne(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(!point_eq_point(&l1->p[0], &l2->p[0]) ||
-                   !point_eq_point(&l1->p[1], &l2->p[1]));
+    PG_RETURN_TSBOOL(TS_OR2(
+                         TS_NOT(point_eq_point(&l1->p[0], &l2->p[0])),
+                         TS_NOT(point_eq_point(&l1->p[1], &l2->p[1]))));
 }
 
 Datum
@@ -2322,8 +2350,8 @@ 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_TSBOOL(FPTlt(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 Datum
@@ -2332,8 +2360,8 @@ 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_TSBOOL(FPTle(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 Datum
@@ -2342,8 +2370,8 @@ 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_TSBOOL(FPTgt(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 Datum
@@ -2352,8 +2380,8 @@ 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_TSBOOL(FPTge(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 
@@ -2398,27 +2426,28 @@ lseg_center(PG_FUNCTION_ARGS)
  * This function is almost perfectly symmetric, even though it doesn't look
  * like it.  See lseg_interpt_line() for the other half of it.
  */
-static bool
+static tsbool
 lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
 {
     Point        interpt;
     LINE        tmp;
+    tsbool        r;
 
     line_construct(&tmp, &l2->p[0], lseg_sl(l2));
-    if (!lseg_interpt_line(&interpt, l1, &tmp))
-        return false;
+    if ((r = lseg_interpt_line(&interpt, l1, &tmp)) != TS_TRUE)
+        return r;
 
     /*
      * If the line intersection point isn't within l2, there is no valid
      * segment intersection point at all.
      */
-    if (!lseg_contain_point(l2, &interpt))
-        return false;
+    if ((r = lseg_contain_point(l2, &interpt)) != TS_TRUE)
+        return r;
 
     if (result != NULL)
         *result = interpt;
 
-    return true;
+    return TS_TRUE;
 }
 
 Datum
@@ -2427,10 +2456,11 @@ lseg_interpt(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
     Point       *result;
+    tsbool        r;
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (!lseg_interpt_lseg(result, l1, l2))
+    if ((r = lseg_interpt_lseg(result, l1, l2)) != TS_TRUE)
         PG_RETURN_NULL();
     PG_RETURN_POINT_P(result);
 }
@@ -2740,8 +2770,13 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
     float8        d;
     int            i;
     LSEG        seg;
+    int            inside;
 
-    if (point_inside(pt, poly->npts, poly->p) != 0)
+    inside = point_inside(pt, poly->npts, poly->p);
+    if (inside == -1)
+        return get_float8_nan();
+
+    if (inside != 0)
         return 0.0;
 
     /* initialize distance with segment between first and last points */
@@ -2780,11 +2815,12 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
  * Return whether the line segment intersect with the line. If *result is not
  * NULL, it is set to the intersection point.
  */
-static bool
+static tsbool
 lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
 {
     Point        interpt;
     LINE        tmp;
+    tsbool        r;
 
     /*
      * First, we promote the line segment to a line, because we know how to
@@ -2792,32 +2828,40 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
      * intersection point, we are done.
      */
     line_construct(&tmp, &lseg->p[0], lseg_sl(lseg));
-    if (!line_interpt_line(&interpt, &tmp, line) ||
-        unlikely(isnan(interpt.x) || isnan(interpt.y)))
-        return false;
+    if ((r = line_interpt_line(&interpt, &tmp, line)) != TS_TRUE)
+        return r;
+
+    if (unlikely(isnan(interpt.x) || isnan(interpt.y)))
+        return TS_FALSE;
 
     /*
      * Then, we check whether the intersection point is actually on the line
      * segment.
      */
-    if (!lseg_contain_point(lseg, &interpt))
-        return false;
+    if ((r = lseg_contain_point(lseg, &interpt)) != TS_TRUE)
+        return r;
+
     if (result != NULL)
     {
+        tsbool    r;
+
         /*
          * If there is an intersection, then check explicitly for matching
          * endpoints since there may be rounding effects with annoying LSB
          * residue.
          */
-        if (point_eq_point(&lseg->p[0], &interpt))
+        if ((r = point_eq_point(&lseg->p[0], &interpt)) == TS_TRUE)
             *result = lseg->p[0];
-        else if (point_eq_point(&lseg->p[1], &interpt))
+        else if (r == TS_FALSE &&
+                 (r = point_eq_point(&lseg->p[1], &interpt)) == TS_TRUE)
             *result = lseg->p[1];
-        else
+        else if (r == TS_FALSE)
             *result = interpt;
+        else
+            return r;
     }
 
-    return true;
+    return TS_TRUE;
 }
 
 /*---------------------------------------------------------------------
@@ -3178,11 +3222,14 @@ close_sl(PG_FUNCTION_ARGS)
     Point       *result;
     float8        d1,
                 d2;
+    tsbool        r;
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (lseg_interpt_line(result, lseg, line))
+    if ((r = lseg_interpt_line(result, lseg, line)) == TS_TRUE)
         PG_RETURN_POINT_P(result);
+    else if (r == TS_NULL)
+        PG_RETURN_NULL;
 
     d1 = line_closept_point(NULL, line, &lseg->p[0]);
     d2 = line_closept_point(NULL, line, &lseg->p[1]);
@@ -3219,9 +3266,12 @@ lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
 {
     float8        dist1,
                 dist2;
+    tsbool        r;
 
-    if (lseg_interpt_line(result, lseg, line))
+    if ((r = lseg_interpt_line(result, lseg, line)) == TS_TRUE)
         return 0.0;
+    else if (r == TS_NULL)
+        return get_float8_nan();
 
     dist1 = line_closept_point(NULL, line, &lseg->p[0]);
     dist2 = line_closept_point(NULL, line, &lseg->p[1]);
@@ -3365,7 +3415,7 @@ close_lb(PG_FUNCTION_ARGS)
 /*
  *        Does the point satisfy the equation?
  */
-static bool
+static tsbool
 line_contain_point(LINE *line, Point *point)
 {
     /*
@@ -3379,7 +3429,7 @@ line_contain_point(LINE *line, Point *point)
         Assert(line->B != 0.0);
 
         /* inf == inf here */
-        return FPeq(point->y, -line->C / line->B);
+        return FPTeq(point->y, -line->C / line->B);
     }
     else if (line->B == 0.0)
     {
@@ -3387,13 +3437,13 @@ line_contain_point(LINE *line, Point *point)
         Assert(line->A != 0.0);
 
         /* inf == inf here */
-        return FPeq(point->x, -line->C / line->A);
+        return FPTeq(point->x, -line->C / line->A);
     }
 
-    return FPzero(float8_pl(
-                      float8_pl(float8_mul(line->A, point->x),
-                                float8_mul(line->B, point->y)),
-                      line->C));
+    return FPTzero(float8_pl(
+                       float8_pl(float8_mul(line->A, point->x),
+                                 float8_mul(line->B, point->y)),
+                       line->C));
 }
 
 Datum
@@ -3402,7 +3452,7 @@ on_pl(PG_FUNCTION_ARGS)
     Point       *pt = PG_GETARG_POINT_P(0);
     LINE       *line = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(line_contain_point(line, pt));
+    PG_RETURN_TSBOOL(line_contain_point(line, pt));
 }
 
 
@@ -3410,12 +3460,12 @@ on_pl(PG_FUNCTION_ARGS)
  *        Determine colinearity by detecting a triangle inequality.
  * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
  */
-static bool
+static tsbool
 lseg_contain_point(LSEG *lseg, Point *pt)
 {
-    return FPeq(point_dt(pt, &lseg->p[0]) +
-                point_dt(pt, &lseg->p[1]),
-                point_dt(&lseg->p[0], &lseg->p[1]));
+    return FPTeq(point_dt(pt, &lseg->p[0]) +
+                 point_dt(pt, &lseg->p[1]),
+                 point_dt(&lseg->p[0], &lseg->p[1]));
 }
 
 Datum
@@ -3424,18 +3474,25 @@ on_ps(PG_FUNCTION_ARGS)
     Point       *pt = PG_GETARG_POINT_P(0);
     LSEG       *lseg = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(lseg_contain_point(lseg, pt));
+    PG_RETURN_TSBOOL(lseg_contain_point(lseg, pt));
 }
 
 
 /*
  * Check whether the point is in the box or on its border
  */
-static bool
+static tsbool
 box_contain_point(BOX *box, Point *point)
 {
-    return box->high.x >= point->x && box->low.x <= point->x &&
-        box->high.y >= point->y && box->low.y <= point->y;
+    if (box->high.x >= point->x && box->low.x <= point->x &&
+        box->high.y >= point->y && box->low.y <= point->y)
+        return TS_TRUE;
+    else if (!isnan(box->high.x) && !isnan(box->high.y) &&
+             !isnan(box->low.x) && !isnan(box->low.y) &&
+             !isnan(point->x) && !isnan(point->y))
+        return TS_FALSE;
+
+    return TS_NULL;
 }
 
 Datum
@@ -3444,7 +3501,7 @@ on_pb(PG_FUNCTION_ARGS)
     Point       *pt = PG_GETARG_POINT_P(0);
     BOX           *box = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_point(box, pt));
+    PG_RETURN_TSBOOL(box_contain_point(box, pt));
 }
 
 Datum
@@ -3453,7 +3510,7 @@ box_contain_pt(PG_FUNCTION_ARGS)
     BOX           *box = PG_GETARG_BOX_P(0);
     Point       *pt = PG_GETARG_POINT_P(1);
 
-    PG_RETURN_BOOL(box_contain_point(box, pt));
+    PG_RETURN_TSBOOL(box_contain_point(box, pt));
 }
 
 /* on_ppath -
@@ -3476,24 +3533,38 @@ on_ppath(PG_FUNCTION_ARGS)
                 n;
     float8        a,
                 b;
+    int            inside;
 
     /*-- OPEN --*/
     if (!path->closed)
     {
+        tsbool r;
+
         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(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1])))
-                PG_RETURN_BOOL(true);
+            r = FPTeq(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1]));
+            if (r != TS_FALSE)
+                PG_RETURN_TSBOOL(r);
             a = b;
         }
+        /* See the PG_RETURN_BOOL at the end of this function */
         PG_RETURN_BOOL(false);
     }
 
     /*-- CLOSED --*/
-    PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
+    inside = point_inside(pt, path->npts, path->p);
+
+    if (inside < 0)
+        PG_RETURN_NULL();
+
+    /*
+     * PG_RETURN_BOOL is compatible and faster than PG_RETURN_TSBOOL when the
+     * value is guaranteed to be in bool.
+     */
+    PG_RETURN_BOOL(inside != 0);
 }
 
 
@@ -3508,8 +3579,8 @@ on_sl(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     LINE       *line = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(line_contain_point(line, &lseg->p[0]) &&
-                   line_contain_point(line, &lseg->p[1]));
+    PG_RETURN_TSBOOL(TS_AND2(line_contain_point(line, &lseg->p[0]),
+                             line_contain_point(line, &lseg->p[1])));
 }
 
 
@@ -3518,11 +3589,11 @@ on_sl(PG_FUNCTION_ARGS)
  *
  * It is, if both of its points are in the box or on its border.
  */
-static bool
+static tsbool
 box_contain_lseg(BOX *box, LSEG *lseg)
 {
-    return box_contain_point(box, &lseg->p[0]) &&
-        box_contain_point(box, &lseg->p[1]);
+    return TS_AND2(box_contain_point(box, &lseg->p[0]),
+                   box_contain_point(box, &lseg->p[1]));
 }
 
 Datum
@@ -3531,7 +3602,7 @@ on_sb(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     BOX           *box = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_lseg(box, lseg));
+    PG_RETURN_TSBOOL(box_contain_lseg(box, lseg));
 }
 
 /*---------------------------------------------------------------------
@@ -3545,7 +3616,7 @@ inter_sl(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     LINE       *line = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(lseg_interpt_line(NULL, lseg, line));
+    PG_RETURN_TSBOOL(lseg_interpt_line(NULL, lseg, line));
 }
 
 
@@ -3564,12 +3635,13 @@ inter_sl(PG_FUNCTION_ARGS)
  * Optimize for non-intersection by checking for box intersection first.
  * - thomas 1998-01-30
  */
-static bool
+static tsbool
 box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
 {
     BOX            lbox;
     LSEG        bseg;
     Point        point;
+    tsbool        r;
 
     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);
@@ -3577,8 +3649,8 @@ box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
     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))
-        return false;
+    if ((r = box_ov(&lbox, box)) != TS_TRUE)
+        return r;
 
     if (result != NULL)
     {
@@ -3587,30 +3659,30 @@ box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
     }
 
     /* an endpoint of segment is inside box? then clearly intersects */
-    if (box_contain_point(box, &lseg->p[0]) ||
-        box_contain_point(box, &lseg->p[1]))
-        return true;
+    if ((r = TS_OR2(box_contain_point(box, &lseg->p[0]),
+                    box_contain_point(box, &lseg->p[1]))) != TS_FALSE)
+        return r;
 
     /* pairwise check lseg intersections */
     point.x = box->low.x;
     point.y = box->high.y;
     statlseg_construct(&bseg, &box->low, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     statlseg_construct(&bseg, &box->high, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     point.x = box->high.x;
     point.y = box->low.y;
     statlseg_construct(&bseg, &box->low, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     statlseg_construct(&bseg, &box->high, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     /* if we dropped through, no two segs intersected */
     return false;
@@ -3622,7 +3694,7 @@ inter_sb(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     BOX           *box = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_interpt_lseg(NULL, box, lseg));
+    PG_RETURN_TSBOOL(box_interpt_lseg(NULL, box, lseg));
 }
 
 
@@ -3637,6 +3709,7 @@ inter_lb(PG_FUNCTION_ARGS)
     LSEG        bseg;
     Point        p1,
                 p2;
+    tsbool        r;
 
     /* pairwise check lseg intersections */
     p1.x = box->low.x;
@@ -3644,25 +3717,28 @@ inter_lb(PG_FUNCTION_ARGS)
     p2.x = box->low.x;
     p2.y = box->high.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
     p1.x = box->high.x;
     p1.y = box->high.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
     p2.x = box->high.x;
     p2.y = box->low.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
     p1.x = box->low.x;
     p1.y = box->low.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
 
-    /* if we dropped through, no intersection */
+    /*
+     * if we dropped through, no intersection "false" doesn't need
+     * PG_RETURN_TSBOOL()
+     */
     PG_RETURN_BOOL(false);
 }
 
@@ -3844,6 +3920,9 @@ poly_left(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.x < polyb->boundbox.low.x;
 
     /*
@@ -3867,6 +3946,9 @@ poly_overleft(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.x <= polyb->boundbox.high.x;
 
     /*
@@ -3890,6 +3972,9 @@ poly_right(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.x > polyb->boundbox.high.x;
 
     /*
@@ -3913,6 +3998,9 @@ poly_overright(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.x >= polyb->boundbox.low.x;
 
     /*
@@ -3936,6 +4024,9 @@ poly_below(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.y < polyb->boundbox.low.y;
 
     /*
@@ -3959,6 +4050,9 @@ poly_overbelow(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.y <= polyb->boundbox.high.y;
 
     /*
@@ -3982,6 +4076,9 @@ poly_above(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.y > polyb->boundbox.high.y;
 
     /*
@@ -4005,6 +4102,9 @@ poly_overabove(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.y >= polyb->boundbox.low.y;
 
     /*
@@ -4023,16 +4123,20 @@ poly_overabove(PG_FUNCTION_ARGS)
  * Check all points for matches in both forward and reverse
  *    direction since polygons are non-directional and are
  *    closed shapes.
+ *
+ * XXX: returns TS_FALSE when the two polygons consists of
+ * different number of points even if any of the points were
+ * NaN.  It might be thewrong defintion.
  *-------------------------------------------------------*/
 Datum
 poly_same(PG_FUNCTION_ARGS)
 {
     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
-    bool        result;
+    tsbool        result;
 
     if (polya->npts != polyb->npts)
-        result = false;
+        result = TS_FALSE;
     else
         result = plist_same(polya->npts, polya->p, polyb->p);
 
@@ -4053,13 +4157,16 @@ poly_overlap(PG_FUNCTION_ARGS)
 {
     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
-    bool        result;
+    tsbool        result;
 
     Assert(polya->npts > 0 && polyb->npts > 0);
 
     /* Quick check by bounding box */
     result = box_ov(&polya->boundbox, &polyb->boundbox);
 
+    if (result == TS_NULL)
+        PG_RETURN_NULL();
+
     /*
      * Brute-force algorithm - try to find intersected edges, if so then
      * polygons are overlapped else check is one polygon inside other or not
@@ -4074,9 +4181,9 @@ poly_overlap(PG_FUNCTION_ARGS)
 
         /* Init first of polya's edge with last point */
         sa.p[0] = polya->p[polya->npts - 1];
-        result = false;
+        result = TS_FALSE;
 
-        for (ia = 0; ia < polya->npts && !result; ia++)
+        for (ia = 0; ia < polya->npts && result != TS_TRUE; ia++)
         {
             /* Second point of polya's edge is a current one */
             sa.p[1] = polya->p[ia];
@@ -4097,8 +4204,12 @@ poly_overlap(PG_FUNCTION_ARGS)
             sa.p[0] = sa.p[1];
         }
 
-        if (!result)
+        if (result == TS_NULL)
+            PG_RETURN_NULL();
+
+        if (result == TS_FALSE)
         {
+            /* in the case of NaN is handled ealier */
             result = (point_inside(polya->p, polyb->npts, polyb->p) ||
                       point_inside(polyb->p, polya->npts, polya->p));
         }
@@ -4133,6 +4244,12 @@ touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
     t.p[0] = *a;
     t.p[1] = *b;
 
+    /*
+     * assume no parameters have NaN, so the tsbool functions shouldn't return
+     * TS_NULL.
+     */
+    Assert(!isnan(a->x) && !isnan(a->y) && !isnan(b->x) && !isnan(b->y) &&
+           !isnan(poly->boundbox.high.x));
     if (point_eq_point(a, s->p))
     {
         if (lseg_contain_point(&t, s->p + 1))
@@ -4160,7 +4277,7 @@ touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
  * start is used for optimization - function checks
  * polygon's edges starting from start
  */
-static bool
+static tsbool
 lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
 {
     LSEG        s,
@@ -4168,14 +4285,21 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
     int            i;
     bool        res = true,
                 intersection = false;
+    tsbool        res1;
+    tsbool        res2;
 
     t.p[0] = *a;
     t.p[1] = *b;
     s.p[0] = poly->p[(start == 0) ? (poly->npts - 1) : (start - 1)];
 
     /* Fast path. Check against boundbox. Also checks NaNs. */
-    if (!box_contain_point(&poly->boundbox, a) ||
-        !box_contain_point(&poly->boundbox, b))
+    res1 = box_contain_point(&poly->boundbox, a);
+    res2 = box_contain_point(&poly->boundbox, b);
+
+    if (res1 == TS_NULL || res2 == TS_NULL)
+        return TS_NULL;
+
+    if (!res1 || !res2)
         return false;
 
     for (i = start; i < poly->npts && res; i++)
@@ -4206,6 +4330,8 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
              */
 
             intersection = true;
+
+            /* the calls below won't return TS_NULL */
             res = lseg_inside_poly(t.p, &interpt, poly, i + 1);
             if (res)
                 res = lseg_inside_poly(t.p + 1, &interpt, poly, i + 1);
@@ -4234,11 +4360,12 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
 /*
  * Check whether the first polygon contains the second
  */
-static bool
+static tsbool
 poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
 {
     int            i;
     LSEG        s;
+    tsbool        r;
 
     Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
 
@@ -4246,20 +4373,22 @@ poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
      * Quick check to see if contained's bounding box is contained in
      * contains' bb.
      */
-    if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
-        return false;
+    r = box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox);
+    if (r != TS_TRUE)
+        return r;
 
     s.p[0] = contained_poly->p[contained_poly->npts - 1];
 
     for (i = 0; i < contained_poly->npts; i++)
     {
         s.p[1] = contained_poly->p[i];
-        if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
-            return false;
+        r = lseg_inside_poly(s.p, s.p + 1, contains_poly, 0);
+        if (r != TS_TRUE)
+            return r;
         s.p[0] = s.p[1];
     }
 
-    return true;
+    return TS_TRUE;
 }
 
 Datum
@@ -4277,7 +4406,7 @@ poly_contain(PG_FUNCTION_ARGS)
     PG_FREE_IF_COPY(polya, 0);
     PG_FREE_IF_COPY(polyb, 1);
 
-    PG_RETURN_BOOL(result);
+    PG_RETURN_TSBOOL(result);
 }
 
 
@@ -4300,7 +4429,7 @@ poly_contained(PG_FUNCTION_ARGS)
     PG_FREE_IF_COPY(polya, 0);
     PG_FREE_IF_COPY(polyb, 1);
 
-    PG_RETURN_BOOL(result);
+    PG_RETURN_TSBOOL(result);
 }
 
 
@@ -4310,7 +4439,7 @@ poly_contain_pt(PG_FUNCTION_ARGS)
     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
     Point       *p = PG_GETARG_POINT_P(1);
 
-    PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
+    PG_RETURN_TSBOOL(point_inside(p, poly->npts, poly->p) != 0);
 }
 
 Datum
@@ -4319,7 +4448,7 @@ pt_contained_poly(PG_FUNCTION_ARGS)
     Point       *p = PG_GETARG_POINT_P(0);
     POLYGON    *poly = PG_GETARG_POLYGON_P(1);
 
-    PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
+    PG_RETURN_TSBOOL(point_inside(p, poly->npts, poly->p) != 0);
 }
 
 
@@ -5015,9 +5144,9 @@ circle_same(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(((isnan(circle1->radius) && isnan(circle1->radius)) ||
-                    FPeq(circle1->radius, circle2->radius)) &&
-                   point_eq_point(&circle1->center, &circle2->center));
+    PG_RETURN_TSBOOL(
+        TS_AND2(FPTeq(circle1->radius, circle2->radius),
+                point_eq_point(&circle1->center, &circle2->center)));
 }
 
 /*        circle_overlap    -        does circle1 overlap circle2?
@@ -5028,8 +5157,8 @@ 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),
-                        float8_pl(circle1->radius, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(point_dt(&circle1->center, &circle2->center),
+                           float8_pl(circle1->radius, circle2->radius)));
 }
 
 /*        circle_overleft -        is the right edge of circle1 at or left of
@@ -5041,8 +5170,8 @@ circle_overleft(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(float8_pl(circle1->center.x, circle1->radius),
-                        float8_pl(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(float8_pl(circle1->center.x, circle1->radius),
+                           float8_pl(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_left        -        is circle1 strictly left of circle2?
@@ -5053,8 +5182,8 @@ circle_left(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.x, circle1->radius),
-                        float8_mi(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTlt(float8_pl(circle1->center.x, circle1->radius),
+                           float8_mi(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_right    -        is circle1 strictly right of circle2?
@@ -5065,8 +5194,8 @@ circle_right(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.x, circle1->radius),
-                        float8_pl(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTgt(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
@@ -5078,8 +5207,8 @@ circle_overright(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPge(float8_mi(circle1->center.x, circle1->radius),
-                        float8_mi(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTge(float8_mi(circle1->center.x, circle1->radius),
+                           float8_mi(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_contained        -        is circle1 contained by circle2?
@@ -5102,8 +5231,8 @@ 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),
-                        float8_mi(circle1->radius, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(point_dt(&circle1->center, &circle2->center),
+                           float8_mi(circle1->radius, circle2->radius)));
 }
 
 
@@ -5115,8 +5244,8 @@ circle_below(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.y, circle1->radius),
-                        float8_mi(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTlt(float8_pl(circle1->center.y, circle1->radius),
+                           float8_mi(circle2->center.y, circle2->radius)));
 }
 
 /*        circle_above    -        is circle1 strictly above circle2?
@@ -5127,8 +5256,8 @@ circle_above(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.y, circle1->radius),
-                        float8_pl(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTgt(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
@@ -5140,8 +5269,8 @@ circle_overbelow(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(float8_pl(circle1->center.y, circle1->radius),
-                        float8_pl(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(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
@@ -5153,13 +5282,15 @@ circle_overabove(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPge(float8_mi(circle1->center.y, circle1->radius),
-                        float8_mi(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTge(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?
+ *
+ *  XXX; area comparison doen't consider the NaN-ness of the center location.
  */
 Datum
 circle_eq(PG_FUNCTION_ARGS)
@@ -5167,7 +5298,7 @@ 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_TSBOOL(FPTeq(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5176,7 +5307,7 @@ 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_TSBOOL(FPTne(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5185,7 +5316,7 @@ 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_TSBOOL(FPTlt(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5194,7 +5325,7 @@ 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_TSBOOL(FPTgt(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5203,7 +5334,7 @@ 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_TSBOOL(FPTle(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5212,7 +5343,7 @@ 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_TSBOOL(FPTge(circle_ar(circle1), circle_ar(circle2)));
 }
 
 
@@ -5348,6 +5479,11 @@ circle_contain_pt(PG_FUNCTION_ARGS)
     float8        d;
 
     d = point_dt(&circle->center, point);
+
+    if (isnan(d) || isnan(circle->radius))
+        PG_RETURN_NULL();
+
+    /*  XXX: why don't we use FP(T)le? */
     PG_RETURN_BOOL(d <= circle->radius);
 }
 
@@ -5360,6 +5496,10 @@ pt_contained_circle(PG_FUNCTION_ARGS)
     float8        d;
 
     d = point_dt(&circle->center, point);
+    if (isnan(d) || isnan(circle->radius))
+        PG_RETURN_NULL();
+
+    /*  XXX: why don't we use FP(T)le? */
     PG_RETURN_BOOL(d <= circle->radius);
 }
 
@@ -5586,8 +5726,9 @@ poly_circle(PG_FUNCTION_ARGS)
  ***********************************************************************/
 
 /*
- *    Test to see if the point is inside the polygon, returns 1/0, or 2 if
- *    the point is on the polygon.
+ *    Test to see if the point is inside the polygon, returns 1/0, or 2 if the
+ *    point is on the polygon. -1 means undetermined, in case any operand is an
+ *    invalid object. (-1, 0 and 1 are compatible with tsbool type)
  *    Code adapted but not copied from integer-based routines in WN: A
  *    Server for the HTTP
  *    version 1.15.1, file wn/image.c
@@ -5619,7 +5760,7 @@ point_inside(Point *p, int npts, Point *plist)
 
     /* NaN makes the point cannot be inside the polygon */
     if (unlikely(isnan(x0) || isnan(y0) || isnan(p->x) || isnan(p->y)))
-        return 0;
+        return -1;
 
     prev_x = x0;
     prev_y = y0;
@@ -5632,7 +5773,7 @@ point_inside(Point *p, int npts, Point *plist)
 
         /* NaN makes the point cannot be inside the polygon */
         if (unlikely(isnan(x) || isnan(y)))
-            return 0;
+            return -1;
 
         /* compute previous to current point crossing */
         if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
@@ -5659,6 +5800,8 @@ 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).
+ * This function doesn't check if the parameters contain NaNs, it's the
+ * responsibility of the callers.
  * Wow, that is one confusing API, but it is used above, and when summed,
  * can tell is if a point is in a polygon.
  */
@@ -5723,17 +5866,18 @@ lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y)
 }
 
 
-static bool
+static tsbool
 plist_same(int npts, Point *p1, Point *p2)
 {
     int            i,
                 ii,
                 j;
+    tsbool        r;
 
     /* find match for first point */
     for (i = 0; i < npts; i++)
     {
-        if (point_eq_point(&p2[i], &p1[0]))
+        if ((r = point_eq_point(&p2[i], &p1[0])) == TS_TRUE)
         {
 
             /* match found? then look forward through remaining points */
@@ -5741,26 +5885,29 @@ plist_same(int npts, Point *p1, Point *p2)
             {
                 if (j >= npts)
                     j = 0;
-                if (!point_eq_point(&p2[j], &p1[ii]))
+                if ((r = point_eq_point(&p2[j], &p1[ii])) != TS_TRUE)
                     break;
             }
             if (ii == npts)
-                return true;
+                return TS_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 (!point_eq_point(&p2[j], &p1[ii]))
+                if ((r = point_eq_point(&p2[j], &p1[ii])) != TS_TRUE)
                     break;
             }
             if (ii == npts)
-                return true;
+                return TS_TRUE;
         }
+
+        if (r == TS_NULL)
+            return TS_NULL;
     }
 
-    return false;
+    return TS_FALSE;
 }
 
 
diff --git a/src/include/c.h b/src/include/c.h
index c8ede08273..03e541936e 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -402,6 +402,13 @@ typedef unsigned char bool;
 #endif                            /* not PG_USE_STDBOOL */
 #endif                            /* not C++ */
 
+/* tri-state boolean, false/true are compatible with bool */
+typedef enum tsbool
+{
+    TS_NULL = -1,
+    TS_FALSE = false,
+    TS_TRUE = true
+} tsbool;
 
 /* ----------------------------------------------------------------
  *                Section 3:    standard system types
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index 4ab3f9d8ef..a983d6d8d0 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -353,6 +353,115 @@ float8_max(const float8 val1, const float8 val2)
     return float8_gt(val1, val2) ? val1 : val2;
 }
 
+/* tri-state equivalents */
+static inline tsbool
+float4_teq(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 == val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_teq(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 == val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tne(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 != val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tne(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 != val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tlt(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 < val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tlt(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 < val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tle(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 <= val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tle(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 <= val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tgt(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 > val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tgt(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 > val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tge(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 >= val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tge(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 >= val2;
+
+    return TS_NULL;
+}
+
 /*
  * These two functions return NaN if either input is NaN, else the smaller
  * of the two inputs.  This does NOT follow our usual sort rule, but it is
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 0b87437d83..7b8a0dbdf7 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -40,6 +40,19 @@
 
 #define EPSILON                    1.0E-06
 
+/* helper function for tri-state checking */
+static inline tsbool
+FP_TRICHECK(double A, double B, bool cond)
+{
+    if (cond)
+        return TS_TRUE;
+    if (!isnan(A) && !isnan(B))
+        return TS_FALSE;
+
+    return TS_NULL;
+}
+
+
 #ifdef EPSILON
 #define FPzero(A)                (fabs(A) <= EPSILON)
 
@@ -78,6 +91,53 @@ FPge(double A, double B)
 {
     return A + EPSILON >= B;
 }
+
+/* tri-state comparisons, don't use define to avoid duplicate evaluation */
+static inline tsbool
+FPTzero(double A)
+{
+    if (fabs(A) <= EPSILON)
+        return TS_TRUE;
+    if (isnan(A))
+        return TS_NULL;
+    return TS_FALSE;
+}
+
+static inline tsbool
+FPTeq(double A, double B)
+{
+    return FP_TRICHECK(A, B, A == B || fabs(A - B) <= EPSILON);
+}
+
+static inline tsbool
+FPTne(double A, double B)
+{
+    return FP_TRICHECK(A, B, A != B && fabs(A - B) > EPSILON);
+}
+
+static inline tsbool
+FPTlt(double A, double B)
+{
+    return FP_TRICHECK(A, B, A + EPSILON < B);
+}
+
+static inline tsbool
+FPTle(double A, double B)
+{
+    return FP_TRICHECK(A, B, A <= B + EPSILON);
+}
+
+static inline tsbool
+FPTgt(double A, double B)
+{
+    return FP_TRICHECK(A, B, A > B + EPSILON);
+}
+
+static inline tsbool
+FPTge(double A, double B)
+{
+    return FP_TRICHECK(A, B, A + EPSILON >= B);
+}
 #else
 #define FPzero(A)                ((A) == 0)
 #define FPeq(A,B)                ((A) == (B))
@@ -86,10 +146,109 @@ FPge(double A, double B)
 #define FPle(A,B)                ((A) <= (B))
 #define FPgt(A,B)                ((A) > (B))
 #define FPge(A,B)                ((A) >= (B))
+
+/* define as inline functions to avoid duplicate evaluation */
+static inline tsbool
+FPTzero(double A)
+{
+    if (fabs(A) <= EPSILON)
+        return TS_TRUE;
+    if (isnan(A))
+        return TS_NULL;
+    return TS_FALSE;
+}
+
+static inline tsbool
+FPTeq(double A, double B)
+{
+    return FP_TRICHECK(A, B, A == B);
+}
+
+static inline tsbool
+FPTne(double A, double B)
+{
+    return FP_TRICHECK(A, B, A != B);
+}
+
+static inline tsbool
+FPTlt(double A, double B)
+{
+    return FP_TRICHECK(A, B, A < B);
+}
+
+static inline tsbool
+FPTle(double A, double B)
+{
+    return FP_TRICHECK(A, B, A <= B);
+}
+
+static inline tsbool
+FPTgt(double A, double B)
+{
+    return FP_TRICHECK(A, B, A > B);
+}
+
+static inline tsbool
+FPge(double A, double B)
+{
+    return FP_TRICHECK(A, B, A >= B);
+}
 #endif
 
 #define HYPOT(A, B)                pg_hypot(A, B)
 
+static inline tsbool
+TS_NOT(tsbool a)
+{
+    if (a != TS_NULL)
+        return !a;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+TS_OR2(tsbool p1, tsbool p2)
+{
+    if (p1 == TS_TRUE || p2 == TS_TRUE)
+        return TS_TRUE;
+    if (p1 == TS_NULL || p2 == TS_NULL)
+        return TS_NULL;
+    else
+        return TS_FALSE;
+}
+
+static inline tsbool
+TS_AND2(tsbool p1, tsbool p2)
+{
+    if (p1 == TS_TRUE && p2 == TS_TRUE)
+        return TS_TRUE;
+    if (p1 == TS_NULL || p2 == TS_NULL)
+        return TS_NULL;
+    else
+        return TS_FALSE;
+}
+
+static inline tsbool
+TS_AND4(tsbool p1, tsbool p2, tsbool p3, tsbool p4)
+{
+    if (p1 == TS_TRUE && p2 == TS_TRUE && p3 == TS_TRUE && p4 == TS_TRUE)
+        return TS_TRUE;
+    if (p1 == TS_NULL || p2 == TS_NULL || p3 == TS_NULL || p4 ==TS_NULL)
+        return TS_NULL;
+    else
+        return TS_FALSE;
+}
+
+#define PG_RETURN_TSBOOL(e)            \
+    do                                \
+    {                                \
+        tsbool _tmpsb = (e);        \
+        if (_tmpsb != TS_NULL)        \
+            PG_RETURN_BOOL(_tmpsb);    \
+        else                        \
+            PG_RETURN_NULL();        \
+    } while (0)
+
 /*---------------------------------------------------------------------
  * Point - (x,y)
  *-------------------------------------------------------------------*/

pgsql-hackers by date:

Previous
From: Michael Paquier
Date:
Subject: Re: DROP INDEX docs - explicit lock naming
Next
From: Laurenz Albe
Date:
Subject: Re: Issue with point_ops and NaN