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)
  *-------------------------------------------------------------------*/
