LCOV - code coverage report
Current view: top level - src/backend/utils/adt - geo_ops.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.8 % 2091 1898
Test Date: 2026-05-22 00:16:36 Functions: 95.1 % 268 255
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * geo_ops.c
       4              :  *    2D geometric operations
       5              :  *
       6              :  * This module implements the geometric functions and operators.  The
       7              :  * geometric types are (from simple to more complicated):
       8              :  *
       9              :  * - point
      10              :  * - line
      11              :  * - line segment
      12              :  * - box
      13              :  * - circle
      14              :  * - polygon
      15              :  *
      16              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      17              :  * Portions Copyright (c) 1994, Regents of the University of California
      18              :  *
      19              :  *
      20              :  * IDENTIFICATION
      21              :  *    src/backend/utils/adt/geo_ops.c
      22              :  *
      23              :  *-------------------------------------------------------------------------
      24              :  */
      25              : #include "postgres.h"
      26              : 
      27              : #include <math.h>
      28              : #include <limits.h>
      29              : #include <float.h>
      30              : #include <ctype.h>
      31              : 
      32              : #include "libpq/pqformat.h"
      33              : #include "miscadmin.h"
      34              : #include "nodes/miscnodes.h"
      35              : #include "utils/float.h"
      36              : #include "utils/fmgrprotos.h"
      37              : #include "utils/geo_decls.h"
      38              : #include "varatt.h"
      39              : 
      40              : /*
      41              :  * * Type constructors have this form:
      42              :  *   void type_construct(Type *result, ...);
      43              :  *
      44              :  * * Operators commonly have signatures such as
      45              :  *   void type1_operator_type2(Type *result, Type1 *obj1, Type2 *obj2);
      46              :  *
      47              :  * Common operators are:
      48              :  * * Intersection point:
      49              :  *   bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
      50              :  *      Return whether the two objects intersect. If *result is not NULL,
      51              :  *      it is set to the intersection point.
      52              :  *
      53              :  * * Containment:
      54              :  *   bool type1_contain_type2(Type1 *obj1, Type2 *obj2);
      55              :  *      Return whether obj1 contains obj2.
      56              :  *   bool type1_contain_type2(Type1 *contains_obj, Type1 *contained_obj);
      57              :  *      Return whether obj1 contains obj2 (used when types are the same)
      58              :  *
      59              :  * * Distance of closest point in or on obj1 to obj2:
      60              :  *   float8 type1_closept_type2(Point *result, Type1 *obj1, Type2 *obj2);
      61              :  *      Returns the shortest distance between two objects.  If *result is not
      62              :  *      NULL, it is set to the closest point in or on obj1 to obj2.
      63              :  *
      64              :  * These functions may be used to implement multiple SQL-level operators.  For
      65              :  * example, determining whether two lines are parallel is done by checking
      66              :  * whether they don't intersect.
      67              :  */
      68              : 
      69              : /*
      70              :  * Internal routines
      71              :  */
      72              : 
      73              : enum path_delim
      74              : {
      75              :     PATH_NONE, PATH_OPEN, PATH_CLOSED
      76              : };
      77              : 
      78              : /* Routines for points */
      79              : static inline void point_construct(Point *result, float8 x, float8 y);
      80              : static inline void point_add_point(Point *result, Point *pt1, Point *pt2, Node *escontext);
      81              : static inline void point_sub_point(Point *result, Point *pt1, Point *pt2);
      82              : static inline void point_mul_point(Point *result, Point *pt1, Point *pt2);
      83              : static inline void point_div_point(Point *result, Point *pt1, Point *pt2);
      84              : static inline bool point_eq_point(Point *pt1, Point *pt2);
      85              : static inline float8 point_dt(Point *pt1, Point *pt2, Node *escontext);
      86              : static inline float8 point_sl(Point *pt1, Point *pt2);
      87              : static int  point_inside(Point *p, int npts, Point *plist);
      88              : 
      89              : /* Routines for lines */
      90              : static inline void line_construct(LINE *result, Point *pt, float8 m);
      91              : static inline float8 line_sl(LINE *line);
      92              : static inline float8 line_invsl(LINE *line);
      93              : static bool line_interpt_line(Point *result, LINE *l1, LINE *l2);
      94              : static bool line_contain_point(LINE *line, Point *point);
      95              : static float8 line_closept_point(Point *result, LINE *line, Point *point);
      96              : 
      97              : /* Routines for line segments */
      98              : static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
      99              : static inline float8 lseg_sl(LSEG *lseg);
     100              : static inline float8 lseg_invsl(LSEG *lseg);
     101              : static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
     102              : static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
     103              : static int  lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y);
     104              : static bool lseg_contain_point(LSEG *lseg, Point *pt);
     105              : static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
     106              : static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
     107              : static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
     108              : 
     109              : /* Routines for boxes */
     110              : static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
     111              : static void box_cn(Point *center, BOX *box, Node *escontext);
     112              : static bool box_ov(BOX *box1, BOX *box2);
     113              : static float8 box_ar(BOX *box);
     114              : static float8 box_ht(BOX *box);
     115              : static float8 box_wd(BOX *box);
     116              : static bool box_contain_point(BOX *box, Point *point);
     117              : static bool box_contain_box(BOX *contains_box, BOX *contained_box);
     118              : static bool box_contain_lseg(BOX *box, LSEG *lseg);
     119              : static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
     120              : static float8 box_closept_point(Point *result, BOX *box, Point *pt);
     121              : static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
     122              : 
     123              : /* Routines for circles */
     124              : static float8 circle_ar(CIRCLE *circle);
     125              : 
     126              : /* Routines for polygons */
     127              : static void make_bound_box(POLYGON *poly);
     128              : static POLYGON *circle_poly_internal(int32 npts, const CIRCLE *circle, FunctionCallInfo fcinfo);
     129              : static void poly_to_circle(CIRCLE *result, POLYGON *poly, Node *escontext);
     130              : static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
     131              : static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
     132              : static bool plist_same(int npts, Point *p1, Point *p2);
     133              : static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
     134              : 
     135              : /* Routines for encoding and decoding */
     136              : static bool single_decode(char *num, float8 *x, char **endptr_p,
     137              :                           const char *type_name, const char *orig_string,
     138              :                           Node *escontext);
     139              : static void single_encode(float8 x, StringInfo str);
     140              : static bool pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
     141              :                         const char *type_name, const char *orig_string,
     142              :                         Node *escontext);
     143              : static void pair_encode(float8 x, float8 y, StringInfo str);
     144              : static int  pair_count(char *s, char delim);
     145              : static bool path_decode(char *str, bool opentype, int npts, Point *p,
     146              :                         bool *isopen, char **endptr_p,
     147              :                         const char *type_name, const char *orig_string,
     148              :                         Node *escontext);
     149              : static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
     150              : 
     151              : 
     152              : /*
     153              :  * Delimiters for input and output strings.
     154              :  * LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively.
     155              :  * LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints.
     156              :  */
     157              : 
     158              : #define LDELIM          '('
     159              : #define RDELIM          ')'
     160              : #define DELIM           ','
     161              : #define LDELIM_EP       '['
     162              : #define RDELIM_EP       ']'
     163              : #define LDELIM_C        '<'
     164              : #define RDELIM_C        '>'
     165              : #define LDELIM_L        '{'
     166              : #define RDELIM_L        '}'
     167              : 
     168              : 
     169              : /*
     170              :  * Geometric data types are composed of points.
     171              :  * This code tries to support a common format throughout the data types,
     172              :  *  to allow for more predictable usage and data type conversion.
     173              :  * The fundamental unit is the point. Other units are line segments,
     174              :  *  open paths, boxes, closed paths, and polygons (which should be considered
     175              :  *  non-intersecting closed paths).
     176              :  *
     177              :  * Data representation is as follows:
     178              :  *  point:              (x,y)
     179              :  *  line segment:       [(x1,y1),(x2,y2)]
     180              :  *  box:                (x1,y1),(x2,y2)
     181              :  *  open path:          [(x1,y1),...,(xn,yn)]
     182              :  *  closed path:        ((x1,y1),...,(xn,yn))
     183              :  *  polygon:            ((x1,y1),...,(xn,yn))
     184              :  *
     185              :  * For boxes, the points are opposite corners with the first point at the top right.
     186              :  * For closed paths and polygons, the points should be reordered to allow
     187              :  *  fast and correct equality comparisons.
     188              :  *
     189              :  * XXX perhaps points in complex shapes should be reordered internally
     190              :  *  to allow faster internal operations, but should keep track of input order
     191              :  *  and restore that order for text output - tgl 97/01/16
     192              :  */
     193              : 
     194              : static bool
     195       182290 : single_decode(char *num, float8 *x, char **endptr_p,
     196              :               const char *type_name, const char *orig_string,
     197              :               Node *escontext)
     198              : {
     199       182290 :     *x = float8in_internal(num, endptr_p, type_name, orig_string, escontext);
     200       182254 :     return (!SOFT_ERROR_OCCURRED(escontext));
     201              : }                               /* single_decode() */
     202              : 
     203              : static void
     204         6104 : single_encode(float8 x, StringInfo str)
     205              : {
     206         6104 :     char       *xstr = float8out_internal(x);
     207              : 
     208         6104 :     appendStringInfoString(str, xstr);
     209         6104 :     pfree(xstr);
     210         6104 : }                               /* single_encode() */
     211              : 
     212              : static bool
     213        90924 : pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
     214              :             const char *type_name, const char *orig_string,
     215              :             Node *escontext)
     216              : {
     217              :     bool        has_delim;
     218              : 
     219        90990 :     while (isspace((unsigned char) *str))
     220           66 :         str++;
     221        90924 :     if ((has_delim = (*str == LDELIM)))
     222        64956 :         str++;
     223              : 
     224        90924 :     if (!single_decode(str, x, &str, type_name, orig_string, escontext))
     225            0 :         return false;
     226              : 
     227        90900 :     if (*str++ != DELIM)
     228           36 :         goto fail;
     229              : 
     230        90864 :     if (!single_decode(str, y, &str, type_name, orig_string, escontext))
     231           36 :         return false;
     232              : 
     233        90820 :     if (has_delim)
     234              :     {
     235        64904 :         if (*str++ != RDELIM)
     236           12 :             goto fail;
     237        64934 :         while (isspace((unsigned char) *str))
     238           42 :             str++;
     239              :     }
     240              : 
     241              :     /* report stopping point if wanted, else complain if not end of string */
     242        90808 :     if (endptr_p)
     243        73461 :         *endptr_p = str;
     244        17347 :     else if (*str != '\0')
     245            4 :         goto fail;
     246        90804 :     return true;
     247              : 
     248           52 : fail:
     249           52 :     ereturn(escontext, false,
     250              :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     251              :              errmsg("invalid input syntax for type %s: \"%s\"",
     252              :                     type_name, orig_string)));
     253              : }
     254              : 
     255              : static void
     256       909275 : pair_encode(float8 x, float8 y, StringInfo str)
     257              : {
     258       909275 :     char       *xstr = float8out_internal(x);
     259       909275 :     char       *ystr = float8out_internal(y);
     260              : 
     261       909275 :     appendStringInfo(str, "%s,%s", xstr, ystr);
     262       909275 :     pfree(xstr);
     263       909275 :     pfree(ystr);
     264       909275 : }
     265              : 
     266              : static bool
     267        34203 : path_decode(char *str, bool opentype, int npts, Point *p,
     268              :             bool *isopen, char **endptr_p,
     269              :             const char *type_name, const char *orig_string,
     270              :             Node *escontext)
     271              : {
     272        34203 :     int         depth = 0;
     273              :     char       *cp;
     274              :     int         i;
     275              : 
     276        34207 :     while (isspace((unsigned char) *str))
     277            4 :         str++;
     278        34203 :     if ((*isopen = (*str == LDELIM_EP)))
     279              :     {
     280              :         /* no open delimiter allowed? */
     281        20273 :         if (!opentype)
     282            4 :             goto fail;
     283        20269 :         depth++;
     284        20269 :         str++;
     285              :     }
     286        13930 :     else if (*str == LDELIM)
     287              :     {
     288        13814 :         cp = (str + 1);
     289        13824 :         while (isspace((unsigned char) *cp))
     290           10 :             cp++;
     291        13814 :         if (*cp == LDELIM)
     292              :         {
     293          818 :             depth++;
     294          818 :             str = cp;
     295              :         }
     296        12996 :         else if (strrchr(str, LDELIM) == str)
     297              :         {
     298        12706 :             depth++;
     299        12706 :             str = cp;
     300              :         }
     301              :     }
     302              : 
     303       107406 :     for (i = 0; i < npts; i++)
     304              :     {
     305        73283 :         if (!pair_decode(str, &(p->x), &(p->y), &str, type_name, orig_string,
     306              :                          escontext))
     307           48 :             return false;
     308        73207 :         if (*str == DELIM)
     309        39060 :             str++;
     310        73207 :         p++;
     311              :     }
     312              : 
     313        67832 :     while (depth > 0)
     314              :     {
     315        33737 :         if (*str == RDELIM || (*str == RDELIM_EP && *isopen && depth == 1))
     316              :         {
     317        33709 :             depth--;
     318        33709 :             str++;
     319        33721 :             while (isspace((unsigned char) *str))
     320           12 :                 str++;
     321              :         }
     322              :         else
     323           28 :             goto fail;
     324              :     }
     325              : 
     326              :     /* report stopping point if wanted, else complain if not end of string */
     327        34095 :     if (endptr_p)
     328        20585 :         *endptr_p = str;
     329        13510 :     else if (*str != '\0')
     330            4 :         goto fail;
     331        34091 :     return true;
     332              : 
     333           36 : fail:
     334           36 :     ereturn(escontext, false,
     335              :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     336              :              errmsg("invalid input syntax for type %s: \"%s\"",
     337              :                     type_name, orig_string)));
     338              : }                               /* path_decode() */
     339              : 
     340              : static char *
     341       307863 : path_encode(enum path_delim path_delim, int npts, Point *pt)
     342              : {
     343              :     StringInfoData str;
     344              :     int         i;
     345              : 
     346       307863 :     initStringInfo(&str);
     347              : 
     348       307863 :     switch (path_delim)
     349              :     {
     350        52090 :         case PATH_CLOSED:
     351        52090 :             appendStringInfoChar(&str, LDELIM);
     352        52090 :             break;
     353        32407 :         case PATH_OPEN:
     354        32407 :             appendStringInfoChar(&str, LDELIM_EP);
     355        32407 :             break;
     356       223366 :         case PATH_NONE:
     357       223366 :             break;
     358              :     }
     359              : 
     360      1211034 :     for (i = 0; i < npts; i++)
     361              :     {
     362       903171 :         if (i > 0)
     363       595308 :             appendStringInfoChar(&str, DELIM);
     364       903171 :         appendStringInfoChar(&str, LDELIM);
     365       903171 :         pair_encode(pt->x, pt->y, &str);
     366       903171 :         appendStringInfoChar(&str, RDELIM);
     367       903171 :         pt++;
     368              :     }
     369              : 
     370       307863 :     switch (path_delim)
     371              :     {
     372        52090 :         case PATH_CLOSED:
     373        52090 :             appendStringInfoChar(&str, RDELIM);
     374        52090 :             break;
     375        32407 :         case PATH_OPEN:
     376        32407 :             appendStringInfoChar(&str, RDELIM_EP);
     377        32407 :             break;
     378       223366 :         case PATH_NONE:
     379       223366 :             break;
     380              :     }
     381              : 
     382       307863 :     return str.data;
     383              : }                               /* path_encode() */
     384              : 
     385              : /*-------------------------------------------------------------
     386              :  * pair_count - count the number of points
     387              :  * allow the following notation:
     388              :  * '((1,2),(3,4))'
     389              :  * '(1,3,2,4)'
     390              :  * require an odd number of delim characters in the string
     391              :  *-------------------------------------------------------------*/
     392              : static int
     393        20882 : pair_count(char *s, char delim)
     394              : {
     395        20882 :     int         ndelim = 0;
     396              : 
     397        93326 :     while ((s = strchr(s, delim)) != NULL)
     398              :     {
     399        72444 :         ndelim++;
     400        72444 :         s++;
     401              :     }
     402        20882 :     return (ndelim % 2) ? ((ndelim + 1) / 2) : -1;
     403              : }
     404              : 
     405              : 
     406              : /***********************************************************************
     407              :  **
     408              :  **     Routines for two-dimensional boxes.
     409              :  **
     410              :  ***********************************************************************/
     411              : 
     412              : /*----------------------------------------------------------
     413              :  * Formatting and conversion routines.
     414              :  *---------------------------------------------------------*/
     415              : 
     416              : /*
     417              :  * box_in   -       convert a string to internal form.
     418              :  *
     419              :  *      External format: (two corners of box)
     420              :  *              "(f8, f8), (f8, f8)"
     421              :  *              also supports the older style "(f8, f8, f8, f8)"
     422              :  */
     423              : Datum
     424        13240 : box_in(PG_FUNCTION_ARGS)
     425              : {
     426        13240 :     char       *str = PG_GETARG_CSTRING(0);
     427        13240 :     Node       *escontext = fcinfo->context;
     428        13240 :     BOX        *box = palloc_object(BOX);
     429              :     bool        isopen;
     430              :     float8      x,
     431              :                 y;
     432              : 
     433        13240 :     if (!path_decode(str, false, 2, &(box->high), &isopen, NULL, "box", str,
     434              :                      escontext))
     435           16 :         PG_RETURN_NULL();
     436              : 
     437              :     /* reorder corners if necessary... */
     438        13204 :     if (float8_lt(box->high.x, box->low.x))
     439              :     {
     440          608 :         x = box->high.x;
     441          608 :         box->high.x = box->low.x;
     442          608 :         box->low.x = x;
     443              :     }
     444        13204 :     if (float8_lt(box->high.y, box->low.y))
     445              :     {
     446          616 :         y = box->high.y;
     447          616 :         box->high.y = box->low.y;
     448          616 :         box->low.y = y;
     449              :     }
     450              : 
     451        13204 :     PG_RETURN_BOX_P(box);
     452              : }
     453              : 
     454              : /*
     455              :  * box_out -        convert a box to external form.
     456              :  */
     457              : Datum
     458        89728 : box_out(PG_FUNCTION_ARGS)
     459              : {
     460        89728 :     BOX        *box = PG_GETARG_BOX_P(0);
     461              : 
     462        89728 :     PG_RETURN_CSTRING(path_encode(PATH_NONE, 2, &(box->high)));
     463              : }
     464              : 
     465              : /*
     466              :  *      box_recv            - converts external binary format to box
     467              :  */
     468              : Datum
     469            0 : box_recv(PG_FUNCTION_ARGS)
     470              : {
     471            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     472              :     BOX        *box;
     473              :     float8      x,
     474              :                 y;
     475              : 
     476            0 :     box = palloc_object(BOX);
     477              : 
     478            0 :     box->high.x = pq_getmsgfloat8(buf);
     479            0 :     box->high.y = pq_getmsgfloat8(buf);
     480            0 :     box->low.x = pq_getmsgfloat8(buf);
     481            0 :     box->low.y = pq_getmsgfloat8(buf);
     482              : 
     483              :     /* reorder corners if necessary... */
     484            0 :     if (float8_lt(box->high.x, box->low.x))
     485              :     {
     486            0 :         x = box->high.x;
     487            0 :         box->high.x = box->low.x;
     488            0 :         box->low.x = x;
     489              :     }
     490            0 :     if (float8_lt(box->high.y, box->low.y))
     491              :     {
     492            0 :         y = box->high.y;
     493            0 :         box->high.y = box->low.y;
     494            0 :         box->low.y = y;
     495              :     }
     496              : 
     497            0 :     PG_RETURN_BOX_P(box);
     498              : }
     499              : 
     500              : /*
     501              :  *      box_send            - converts box to binary format
     502              :  */
     503              : Datum
     504            0 : box_send(PG_FUNCTION_ARGS)
     505              : {
     506            0 :     BOX        *box = PG_GETARG_BOX_P(0);
     507              :     StringInfoData buf;
     508              : 
     509            0 :     pq_begintypsend(&buf);
     510            0 :     pq_sendfloat8(&buf, box->high.x);
     511            0 :     pq_sendfloat8(&buf, box->high.y);
     512            0 :     pq_sendfloat8(&buf, box->low.x);
     513            0 :     pq_sendfloat8(&buf, box->low.y);
     514            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     515              : }
     516              : 
     517              : 
     518              : /*
     519              :  * box_construct    -       fill in a new box.
     520              :  */
     521              : static inline void
     522       173684 : box_construct(BOX *result, Point *pt1, Point *pt2)
     523              : {
     524       173684 :     if (float8_gt(pt1->x, pt2->x))
     525              :     {
     526        12372 :         result->high.x = pt1->x;
     527        12372 :         result->low.x = pt2->x;
     528              :     }
     529              :     else
     530              :     {
     531       161312 :         result->high.x = pt2->x;
     532       161312 :         result->low.x = pt1->x;
     533              :     }
     534       173684 :     if (float8_gt(pt1->y, pt2->y))
     535              :     {
     536        12386 :         result->high.y = pt1->y;
     537        12386 :         result->low.y = pt2->y;
     538              :     }
     539              :     else
     540              :     {
     541       161298 :         result->high.y = pt2->y;
     542       161298 :         result->low.y = pt1->y;
     543              :     }
     544       173684 : }
     545              : 
     546              : 
     547              : /*----------------------------------------------------------
     548              :  *  Relational operators for BOXes.
     549              :  *      <, >, <=, >=, and == are based on box area.
     550              :  *---------------------------------------------------------*/
     551              : 
     552              : /*
     553              :  * box_same     -       are two boxes identical?
     554              :  */
     555              : Datum
     556         9173 : box_same(PG_FUNCTION_ARGS)
     557              : {
     558         9173 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     559         9173 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     560              : 
     561         9173 :     PG_RETURN_BOOL(point_eq_point(&box1->high, &box2->high) &&
     562              :                    point_eq_point(&box1->low, &box2->low));
     563              : }
     564              : 
     565              : /*
     566              :  * box_overlap      -       does box1 overlap box2?
     567              :  */
     568              : Datum
     569        43156 : box_overlap(PG_FUNCTION_ARGS)
     570              : {
     571        43156 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     572        43156 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     573              : 
     574        43156 :     PG_RETURN_BOOL(box_ov(box1, box2));
     575              : }
     576              : 
     577              : static bool
     578       984120 : box_ov(BOX *box1, BOX *box2)
     579              : {
     580      1486696 :     return (FPle(box1->low.x, box2->high.x) &&
     581       584524 :             FPle(box2->low.x, box1->high.x) &&
     582      1568644 :             FPle(box1->low.y, box2->high.y) &&
     583        65648 :             FPle(box2->low.y, box1->high.y));
     584              : }
     585              : 
     586              : /*
     587              :  * box_left     -       is box1 strictly left of box2?
     588              :  */
     589              : Datum
     590        33348 : box_left(PG_FUNCTION_ARGS)
     591              : {
     592        33348 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     593        33348 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     594              : 
     595        33348 :     PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
     596              : }
     597              : 
     598              : /*
     599              :  * box_overleft -       is the right edge of box1 at or left of
     600              :  *                              the right edge of box2?
     601              :  *
     602              :  *      This is "less than or equal" for the end of a time range,
     603              :  *      when time ranges are stored as rectangles.
     604              :  */
     605              : Datum
     606        66472 : box_overleft(PG_FUNCTION_ARGS)
     607              : {
     608        66472 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     609        66472 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     610              : 
     611        66472 :     PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
     612              : }
     613              : 
     614              : /*
     615              :  * box_right        -       is box1 strictly right of box2?
     616              :  */
     617              : Datum
     618        87828 : box_right(PG_FUNCTION_ARGS)
     619              : {
     620        87828 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     621        87828 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     622              : 
     623        87828 :     PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
     624              : }
     625              : 
     626              : /*
     627              :  * box_overright    -       is the left edge of box1 at or right of
     628              :  *                              the left edge of box2?
     629              :  *
     630              :  *      This is "greater than or equal" for time ranges, when time ranges
     631              :  *      are stored as rectangles.
     632              :  */
     633              : Datum
     634        74932 : box_overright(PG_FUNCTION_ARGS)
     635              : {
     636        74932 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     637        74932 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     638              : 
     639        74932 :     PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
     640              : }
     641              : 
     642              : /*
     643              :  * box_below        -       is box1 strictly below box2?
     644              :  */
     645              : Datum
     646        18452 : box_below(PG_FUNCTION_ARGS)
     647              : {
     648        18452 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     649        18452 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     650              : 
     651        18452 :     PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
     652              : }
     653              : 
     654              : /*
     655              :  * box_overbelow    -       is the upper edge of box1 at or below
     656              :  *                              the upper edge of box2?
     657              :  */
     658              : Datum
     659        53624 : box_overbelow(PG_FUNCTION_ARGS)
     660              : {
     661        53624 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     662        53624 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     663              : 
     664        53624 :     PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
     665              : }
     666              : 
     667              : /*
     668              :  * box_above        -       is box1 strictly above box2?
     669              :  */
     670              : Datum
     671        38480 : box_above(PG_FUNCTION_ARGS)
     672              : {
     673        38480 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     674        38480 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     675              : 
     676        38480 :     PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
     677              : }
     678              : 
     679              : /*
     680              :  * box_overabove    -       is the lower edge of box1 at or above
     681              :  *                              the lower edge of box2?
     682              :  */
     683              : Datum
     684        74100 : box_overabove(PG_FUNCTION_ARGS)
     685              : {
     686        74100 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     687        74100 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     688              : 
     689        74100 :     PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
     690              : }
     691              : 
     692              : /*
     693              :  * box_contained    -       is box1 contained by box2?
     694              :  */
     695              : Datum
     696       105528 : box_contained(PG_FUNCTION_ARGS)
     697              : {
     698       105528 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     699       105528 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     700              : 
     701       105528 :     PG_RETURN_BOOL(box_contain_box(box2, box1));
     702              : }
     703              : 
     704              : /*
     705              :  * box_contain      -       does box1 contain box2?
     706              :  */
     707              : Datum
     708         8168 : box_contain(PG_FUNCTION_ARGS)
     709              : {
     710         8168 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     711         8168 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     712              : 
     713         8168 :     PG_RETURN_BOOL(box_contain_box(box1, box2));
     714              : }
     715              : 
     716              : /*
     717              :  * Check whether the second box is in the first box or on its border
     718              :  */
     719              : static bool
     720       170316 : box_contain_box(BOX *contains_box, BOX *contained_box)
     721              : {
     722       278368 :     return FPge(contains_box->high.x, contained_box->high.x) &&
     723       185236 :         FPle(contains_box->low.x, contained_box->low.x) &&
     724       355552 :         FPge(contains_box->high.y, contained_box->high.y) &&
     725        64996 :         FPle(contains_box->low.y, contained_box->low.y);
     726              : }
     727              : 
     728              : 
     729              : /*
     730              :  * box_positionop   -
     731              :  *              is box1 entirely {above,below} box2?
     732              :  *
     733              :  * box_below_eq and box_above_eq are obsolete versions that (probably
     734              :  * erroneously) accept the equal-boundaries case.  Since these are not
     735              :  * in sync with the box_left and box_right code, they are deprecated and
     736              :  * not supported in the PG 8.1 rtree operator class extension.
     737              :  */
     738              : Datum
     739          100 : box_below_eq(PG_FUNCTION_ARGS)
     740              : {
     741          100 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     742          100 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     743              : 
     744          100 :     PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
     745              : }
     746              : 
     747              : Datum
     748          100 : box_above_eq(PG_FUNCTION_ARGS)
     749              : {
     750          100 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     751          100 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     752              : 
     753          100 :     PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
     754              : }
     755              : 
     756              : 
     757              : /*
     758              :  * box_relop        -       is area(box1) relop area(box2), within
     759              :  *                              our accuracy constraint?
     760              :  */
     761              : Datum
     762           20 : box_lt(PG_FUNCTION_ARGS)
     763              : {
     764           20 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     765           20 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     766              : 
     767           20 :     PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
     768              : }
     769              : 
     770              : Datum
     771           20 : box_gt(PG_FUNCTION_ARGS)
     772              : {
     773           20 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     774           20 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     775              : 
     776           20 :     PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
     777              : }
     778              : 
     779              : Datum
     780           20 : box_eq(PG_FUNCTION_ARGS)
     781              : {
     782           20 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     783           20 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     784              : 
     785           20 :     PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
     786              : }
     787              : 
     788              : Datum
     789           20 : box_le(PG_FUNCTION_ARGS)
     790              : {
     791           20 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     792           20 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     793              : 
     794           20 :     PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
     795              : }
     796              : 
     797              : Datum
     798           20 : box_ge(PG_FUNCTION_ARGS)
     799              : {
     800           20 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     801           20 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     802              : 
     803           20 :     PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
     804              : }
     805              : 
     806              : 
     807              : /*----------------------------------------------------------
     808              :  *  "Arithmetic" operators on boxes.
     809              :  *---------------------------------------------------------*/
     810              : 
     811              : /*
     812              :  * box_area     -       returns the area of the box.
     813              :  */
     814              : Datum
     815           20 : box_area(PG_FUNCTION_ARGS)
     816              : {
     817           20 :     BOX        *box = PG_GETARG_BOX_P(0);
     818              : 
     819           20 :     PG_RETURN_FLOAT8(box_ar(box));
     820              : }
     821              : 
     822              : 
     823              : /*
     824              :  * box_width        -       returns the width of the box
     825              :  *                                (horizontal magnitude).
     826              :  */
     827              : Datum
     828           20 : box_width(PG_FUNCTION_ARGS)
     829              : {
     830           20 :     BOX        *box = PG_GETARG_BOX_P(0);
     831              : 
     832           20 :     PG_RETURN_FLOAT8(box_wd(box));
     833              : }
     834              : 
     835              : 
     836              : /*
     837              :  * box_height       -       returns the height of the box
     838              :  *                                (vertical magnitude).
     839              :  */
     840              : Datum
     841           20 : box_height(PG_FUNCTION_ARGS)
     842              : {
     843           20 :     BOX        *box = PG_GETARG_BOX_P(0);
     844              : 
     845           20 :     PG_RETURN_FLOAT8(box_ht(box));
     846              : }
     847              : 
     848              : 
     849              : /*
     850              :  * box_distance -       returns the distance between the
     851              :  *                                center points of two boxes.
     852              :  */
     853              : Datum
     854          100 : box_distance(PG_FUNCTION_ARGS)
     855              : {
     856          100 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     857          100 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     858              :     Point       a,
     859              :                 b;
     860              : 
     861          100 :     box_cn(&a, box1, NULL);
     862          100 :     box_cn(&b, box2, NULL);
     863              : 
     864          100 :     PG_RETURN_FLOAT8(point_dt(&a, &b, NULL));
     865              : }
     866              : 
     867              : 
     868              : /*
     869              :  * box_center       -       returns the center point of the box.
     870              :  */
     871              : Datum
     872           60 : box_center(PG_FUNCTION_ARGS)
     873              : {
     874           60 :     BOX        *box = PG_GETARG_BOX_P(0);
     875           60 :     Point      *result = palloc_object(Point);
     876              : 
     877           60 :     box_cn(result, box, fcinfo->context);
     878           60 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
     879            0 :         PG_RETURN_NULL();
     880              : 
     881           60 :     PG_RETURN_POINT_P(result);
     882              : }
     883              : 
     884              : 
     885              : /*
     886              :  * box_ar   -       returns the area of the box.
     887              :  */
     888              : static float8
     889          220 : box_ar(BOX *box)
     890              : {
     891          220 :     return float8_mul(box_wd(box), box_ht(box));
     892              : }
     893              : 
     894              : 
     895              : /*
     896              :  * box_cn   -       stores the centerpoint of the box into *center.
     897              :  */
     898              : static void
     899          316 : box_cn(Point *center, BOX *box, Node *escontext)
     900              : {
     901              :     float8      x;
     902              :     float8      y;
     903              : 
     904          316 :     x = float8_pl_safe(box->high.x, box->low.x, escontext);
     905          316 :     if (SOFT_ERROR_OCCURRED(escontext))
     906            0 :         return;
     907              : 
     908          316 :     center->x = float8_div_safe(x, 2.0, escontext);
     909          316 :     if (SOFT_ERROR_OCCURRED(escontext))
     910            0 :         return;
     911              : 
     912          316 :     y = float8_pl_safe(box->high.y, box->low.y, escontext);
     913          316 :     if (SOFT_ERROR_OCCURRED(escontext))
     914            0 :         return;
     915              : 
     916          316 :     center->y = float8_div_safe(y, 2.0, escontext);
     917          316 :     if (SOFT_ERROR_OCCURRED(escontext))
     918            0 :         return;
     919              : }
     920              : 
     921              : /*
     922              :  * box_wd   -       returns the width (length) of the box
     923              :  *                                (horizontal magnitude).
     924              :  */
     925              : static float8
     926          240 : box_wd(BOX *box)
     927              : {
     928          240 :     return float8_mi(box->high.x, box->low.x);
     929              : }
     930              : 
     931              : 
     932              : /*
     933              :  * box_ht   -       returns the height of the box
     934              :  *                                (vertical magnitude).
     935              :  */
     936              : static float8
     937          240 : box_ht(BOX *box)
     938              : {
     939          240 :     return float8_mi(box->high.y, box->low.y);
     940              : }
     941              : 
     942              : 
     943              : /*----------------------------------------------------------
     944              :  *  Funky operations.
     945              :  *---------------------------------------------------------*/
     946              : 
     947              : /*
     948              :  * box_intersect    -
     949              :  *              returns the overlapping portion of two boxes,
     950              :  *                or NULL if they do not intersect.
     951              :  */
     952              : Datum
     953          100 : box_intersect(PG_FUNCTION_ARGS)
     954              : {
     955          100 :     BOX        *box1 = PG_GETARG_BOX_P(0);
     956          100 :     BOX        *box2 = PG_GETARG_BOX_P(1);
     957              :     BOX        *result;
     958              : 
     959          100 :     if (!box_ov(box1, box2))
     960           56 :         PG_RETURN_NULL();
     961              : 
     962           44 :     result = palloc_object(BOX);
     963              : 
     964           44 :     result->high.x = float8_min(box1->high.x, box2->high.x);
     965           44 :     result->low.x = float8_max(box1->low.x, box2->low.x);
     966           44 :     result->high.y = float8_min(box1->high.y, box2->high.y);
     967           44 :     result->low.y = float8_max(box1->low.y, box2->low.y);
     968              : 
     969           44 :     PG_RETURN_BOX_P(result);
     970              : }
     971              : 
     972              : 
     973              : /*
     974              :  * box_diagonal -
     975              :  *              returns a line segment which happens to be the
     976              :  *                positive-slope diagonal of "box".
     977              :  */
     978              : Datum
     979           20 : box_diagonal(PG_FUNCTION_ARGS)
     980              : {
     981           20 :     BOX        *box = PG_GETARG_BOX_P(0);
     982           20 :     LSEG       *result = palloc_object(LSEG);
     983              : 
     984           20 :     statlseg_construct(result, &box->high, &box->low);
     985              : 
     986           20 :     PG_RETURN_LSEG_P(result);
     987              : }
     988              : 
     989              : /***********************************************************************
     990              :  **
     991              :  **     Routines for 2D lines.
     992              :  **
     993              :  ***********************************************************************/
     994              : 
     995              : static bool
     996           92 : line_decode(char *s, const char *str, LINE *line, Node *escontext)
     997              : {
     998              :     /* s was already advanced over leading '{' */
     999           92 :     if (!single_decode(s, &line->A, &s, "line", str, escontext))
    1000            0 :         return false;
    1001           88 :     if (*s++ != DELIM)
    1002            4 :         goto fail;
    1003           84 :     if (!single_decode(s, &line->B, &s, "line", str, escontext))
    1004            0 :         return false;
    1005           84 :     if (*s++ != DELIM)
    1006           12 :         goto fail;
    1007           72 :     if (!single_decode(s, &line->C, &s, "line", str, escontext))
    1008           16 :         return false;
    1009           56 :     if (*s++ != RDELIM_L)
    1010            4 :         goto fail;
    1011           56 :     while (isspace((unsigned char) *s))
    1012            4 :         s++;
    1013           52 :     if (*s != '\0')
    1014            4 :         goto fail;
    1015           48 :     return true;
    1016              : 
    1017           24 : fail:
    1018           24 :     ereturn(escontext, false,
    1019              :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1020              :              errmsg("invalid input syntax for type %s: \"%s\"",
    1021              :                     "line", str)));
    1022              : }
    1023              : 
    1024              : Datum
    1025          140 : line_in(PG_FUNCTION_ARGS)
    1026              : {
    1027          140 :     char       *str = PG_GETARG_CSTRING(0);
    1028          140 :     Node       *escontext = fcinfo->context;
    1029          140 :     LINE       *line = palloc_object(LINE);
    1030              :     LSEG        lseg;
    1031              :     bool        isopen;
    1032              :     char       *s;
    1033              : 
    1034          140 :     s = str;
    1035          144 :     while (isspace((unsigned char) *s))
    1036            4 :         s++;
    1037          140 :     if (*s == LDELIM_L)
    1038              :     {
    1039           92 :         if (!line_decode(s + 1, str, line, escontext))
    1040           24 :             PG_RETURN_NULL();
    1041           48 :         if (FPzero(line->A) && FPzero(line->B))
    1042           12 :             ereturn(escontext, (Datum) 0,
    1043              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1044              :                      errmsg("invalid line specification: A and B cannot both be zero")));
    1045              :     }
    1046              :     else
    1047              :     {
    1048           48 :         if (!path_decode(s, true, 2, &lseg.p[0], &isopen, NULL, "line", str,
    1049              :                          escontext))
    1050            8 :             PG_RETURN_NULL();
    1051           24 :         if (point_eq_point(&lseg.p[0], &lseg.p[1]))
    1052            4 :             ereturn(escontext, (Datum) 0,
    1053              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1054              :                      errmsg("invalid line specification: must be two distinct points")));
    1055              : 
    1056              :         /*
    1057              :          * XXX lseg_sl() and line_construct() can throw overflow/underflow
    1058              :          * errors.  Eventually we should allow those to be soft, but the
    1059              :          * notational pain seems to outweigh the value for now.
    1060              :          */
    1061           20 :         line_construct(line, &lseg.p[0], lseg_sl(&lseg));
    1062              :     }
    1063              : 
    1064           56 :     PG_RETURN_LINE_P(line);
    1065              : }
    1066              : 
    1067              : 
    1068              : Datum
    1069         4628 : line_out(PG_FUNCTION_ARGS)
    1070              : {
    1071         4628 :     LINE       *line = PG_GETARG_LINE_P(0);
    1072         4628 :     char       *astr = float8out_internal(line->A);
    1073         4628 :     char       *bstr = float8out_internal(line->B);
    1074         4628 :     char       *cstr = float8out_internal(line->C);
    1075              : 
    1076         4628 :     PG_RETURN_CSTRING(psprintf("%c%s%c%s%c%s%c", LDELIM_L, astr, DELIM, bstr,
    1077              :                                DELIM, cstr, RDELIM_L));
    1078              : }
    1079              : 
    1080              : /*
    1081              :  *      line_recv           - converts external binary format to line
    1082              :  */
    1083              : Datum
    1084            0 : line_recv(PG_FUNCTION_ARGS)
    1085              : {
    1086            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    1087              :     LINE       *line;
    1088              : 
    1089            0 :     line = palloc_object(LINE);
    1090              : 
    1091            0 :     line->A = pq_getmsgfloat8(buf);
    1092            0 :     line->B = pq_getmsgfloat8(buf);
    1093            0 :     line->C = pq_getmsgfloat8(buf);
    1094              : 
    1095            0 :     if (FPzero(line->A) && FPzero(line->B))
    1096            0 :         ereport(ERROR,
    1097              :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    1098              :                  errmsg("invalid line specification: A and B cannot both be zero")));
    1099              : 
    1100            0 :     PG_RETURN_LINE_P(line);
    1101              : }
    1102              : 
    1103              : /*
    1104              :  *      line_send           - converts line to binary format
    1105              :  */
    1106              : Datum
    1107            0 : line_send(PG_FUNCTION_ARGS)
    1108              : {
    1109            0 :     LINE       *line = PG_GETARG_LINE_P(0);
    1110              :     StringInfoData buf;
    1111              : 
    1112            0 :     pq_begintypsend(&buf);
    1113            0 :     pq_sendfloat8(&buf, line->A);
    1114            0 :     pq_sendfloat8(&buf, line->B);
    1115            0 :     pq_sendfloat8(&buf, line->C);
    1116            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    1117              : }
    1118              : 
    1119              : 
    1120              : /*----------------------------------------------------------
    1121              :  *  Conversion routines from one line formula to internal.
    1122              :  *      Internal form:  Ax+By+C=0
    1123              :  *---------------------------------------------------------*/
    1124              : 
    1125              : /*
    1126              :  * Fill already-allocated LINE struct from the point and the slope
    1127              :  */
    1128              : static inline void
    1129      4363641 : line_construct(LINE *result, Point *pt, float8 m)
    1130              : {
    1131      4363641 :     if (isinf(m))
    1132              :     {
    1133              :         /* vertical - use "x = C" */
    1134       996465 :         result->A = -1.0;
    1135       996465 :         result->B = 0.0;
    1136       996465 :         result->C = pt->x;
    1137              :     }
    1138      3367176 :     else if (m == 0)
    1139              :     {
    1140              :         /* horizontal - use "y = C" */
    1141       985612 :         result->A = 0.0;
    1142       985612 :         result->B = -1.0;
    1143       985612 :         result->C = pt->y;
    1144              :     }
    1145              :     else
    1146              :     {
    1147              :         /* use "mx - y + yinter = 0" */
    1148      2381564 :         result->A = m;
    1149      2381564 :         result->B = -1.0;
    1150      2381564 :         result->C = float8_mi(pt->y, float8_mul(m, pt->x));
    1151              :         /* on some platforms, the preceding expression tends to produce -0 */
    1152      2381564 :         if (result->C == 0.0)
    1153         3240 :             result->C = 0.0;
    1154              :     }
    1155      4363641 : }
    1156              : 
    1157              : /*
    1158              :  * line_construct_pp()
    1159              :  * two points
    1160              :  */
    1161              : Datum
    1162          361 : line_construct_pp(PG_FUNCTION_ARGS)
    1163              : {
    1164          361 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    1165          361 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    1166          361 :     LINE       *result = palloc_object(LINE);
    1167              : 
    1168          361 :     if (point_eq_point(pt1, pt2))
    1169            4 :         ereport(ERROR,
    1170              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1171              :                  errmsg("invalid line specification: must be two distinct points")));
    1172              : 
    1173          357 :     line_construct(result, pt1, point_sl(pt1, pt2));
    1174              : 
    1175          357 :     PG_RETURN_LINE_P(result);
    1176              : }
    1177              : 
    1178              : 
    1179              : /*----------------------------------------------------------
    1180              :  *  Relative position routines.
    1181              :  *---------------------------------------------------------*/
    1182              : 
    1183              : Datum
    1184          400 : line_intersect(PG_FUNCTION_ARGS)
    1185              : {
    1186          400 :     LINE       *l1 = PG_GETARG_LINE_P(0);
    1187          400 :     LINE       *l2 = PG_GETARG_LINE_P(1);
    1188              : 
    1189          400 :     PG_RETURN_BOOL(line_interpt_line(NULL, l1, l2));
    1190              : }
    1191              : 
    1192              : Datum
    1193          400 : line_parallel(PG_FUNCTION_ARGS)
    1194              : {
    1195          400 :     LINE       *l1 = PG_GETARG_LINE_P(0);
    1196          400 :     LINE       *l2 = PG_GETARG_LINE_P(1);
    1197              : 
    1198          400 :     PG_RETURN_BOOL(!line_interpt_line(NULL, l1, l2));
    1199              : }
    1200              : 
    1201              : Datum
    1202          400 : line_perp(PG_FUNCTION_ARGS)
    1203              : {
    1204          400 :     LINE       *l1 = PG_GETARG_LINE_P(0);
    1205          400 :     LINE       *l2 = PG_GETARG_LINE_P(1);
    1206              : 
    1207          400 :     if (FPzero(l1->A))
    1208          120 :         PG_RETURN_BOOL(FPzero(l2->B));
    1209          280 :     if (FPzero(l2->A))
    1210           84 :         PG_RETURN_BOOL(FPzero(l1->B));
    1211          196 :     if (FPzero(l1->B))
    1212           56 :         PG_RETURN_BOOL(FPzero(l2->A));
    1213          140 :     if (FPzero(l2->B))
    1214           40 :         PG_RETURN_BOOL(FPzero(l1->A));
    1215              : 
    1216          100 :     PG_RETURN_BOOL(FPeq(float8_div(float8_mul(l1->A, l2->A),
    1217              :                                    float8_mul(l1->B, l2->B)), -1.0));
    1218              : }
    1219              : 
    1220              : Datum
    1221           40 : line_vertical(PG_FUNCTION_ARGS)
    1222              : {
    1223           40 :     LINE       *line = PG_GETARG_LINE_P(0);
    1224              : 
    1225           40 :     PG_RETURN_BOOL(FPzero(line->B));
    1226              : }
    1227              : 
    1228              : Datum
    1229           40 : line_horizontal(PG_FUNCTION_ARGS)
    1230              : {
    1231           40 :     LINE       *line = PG_GETARG_LINE_P(0);
    1232              : 
    1233           40 :     PG_RETURN_BOOL(FPzero(line->A));
    1234              : }
    1235              : 
    1236              : 
    1237              : /*
    1238              :  * Check whether the two lines are the same
    1239              :  */
    1240              : Datum
    1241          410 : line_eq(PG_FUNCTION_ARGS)
    1242              : {
    1243          410 :     LINE       *l1 = PG_GETARG_LINE_P(0);
    1244          410 :     LINE       *l2 = PG_GETARG_LINE_P(1);
    1245              :     float8      ratio;
    1246              : 
    1247              :     /* If any NaNs are involved, insist on exact equality */
    1248          410 :     if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
    1249              :                  isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
    1250              :     {
    1251          154 :         PG_RETURN_BOOL(float8_eq(l1->A, l2->A) &&
    1252              :                        float8_eq(l1->B, l2->B) &&
    1253              :                        float8_eq(l1->C, l2->C));
    1254              :     }
    1255              : 
    1256              :     /* Otherwise, lines whose parameters are proportional are the same */
    1257          256 :     if (!FPzero(l2->A))
    1258          160 :         ratio = float8_div(l1->A, l2->A);
    1259           96 :     else if (!FPzero(l2->B))
    1260           96 :         ratio = float8_div(l1->B, l2->B);
    1261            0 :     else if (!FPzero(l2->C))
    1262            0 :         ratio = float8_div(l1->C, l2->C);
    1263              :     else
    1264            0 :         ratio = 1.0;
    1265              : 
    1266          256 :     PG_RETURN_BOOL(FPeq(l1->A, float8_mul(ratio, l2->A)) &&
    1267              :                    FPeq(l1->B, float8_mul(ratio, l2->B)) &&
    1268              :                    FPeq(l1->C, float8_mul(ratio, l2->C)));
    1269              : }
    1270              : 
    1271              : 
    1272              : /*----------------------------------------------------------
    1273              :  *  Line arithmetic routines.
    1274              :  *---------------------------------------------------------*/
    1275              : 
    1276              : /*
    1277              :  * Return slope of the line
    1278              :  */
    1279              : static inline float8
    1280          320 : line_sl(LINE *line)
    1281              : {
    1282          320 :     if (FPzero(line->A))
    1283           96 :         return 0.0;
    1284          224 :     if (FPzero(line->B))
    1285           64 :         return get_float8_infinity();
    1286          160 :     return float8_div(line->A, -line->B);
    1287              : }
    1288              : 
    1289              : 
    1290              : /*
    1291              :  * Return inverse slope of the line
    1292              :  */
    1293              : static inline float8
    1294      1195440 : line_invsl(LINE *line)
    1295              : {
    1296      1195440 :     if (FPzero(line->A))
    1297       476880 :         return get_float8_infinity();
    1298       718560 :     if (FPzero(line->B))
    1299       463736 :         return 0.0;
    1300       254824 :     return float8_div(line->B, line->A);
    1301              : }
    1302              : 
    1303              : 
    1304              : /*
    1305              :  * line_distance()
    1306              :  * Distance between two lines.
    1307              :  */
    1308              : Datum
    1309          400 : line_distance(PG_FUNCTION_ARGS)
    1310              : {
    1311          400 :     LINE       *l1 = PG_GETARG_LINE_P(0);
    1312          400 :     LINE       *l2 = PG_GETARG_LINE_P(1);
    1313              :     float8      ratio;
    1314              : 
    1315          400 :     if (line_interpt_line(NULL, l1, l2))    /* intersecting? */
    1316          336 :         PG_RETURN_FLOAT8(0.0);
    1317              : 
    1318           64 :     if (!FPzero(l1->A) && !isnan(l1->A) && !FPzero(l2->A) && !isnan(l2->A))
    1319           28 :         ratio = float8_div(l1->A, l2->A);
    1320           36 :     else if (!FPzero(l1->B) && !isnan(l1->B) && !FPzero(l2->B) && !isnan(l2->B))
    1321           36 :         ratio = float8_div(l1->B, l2->B);
    1322              :     else
    1323            0 :         ratio = 1.0;
    1324              : 
    1325           64 :     PG_RETURN_FLOAT8(float8_div(fabs(float8_mi(l1->C,
    1326              :                                                float8_mul(ratio, l2->C))),
    1327              :                                 hypot(l1->A, l1->B)));
    1328              : }
    1329              : 
    1330              : /*
    1331              :  * line_interpt()
    1332              :  * Point where two lines l1, l2 intersect (if any)
    1333              :  */
    1334              : Datum
    1335          400 : line_interpt(PG_FUNCTION_ARGS)
    1336              : {
    1337          400 :     LINE       *l1 = PG_GETARG_LINE_P(0);
    1338          400 :     LINE       *l2 = PG_GETARG_LINE_P(1);
    1339              :     Point      *result;
    1340              : 
    1341          400 :     result = palloc_object(Point);
    1342              : 
    1343          400 :     if (!line_interpt_line(result, l1, l2))
    1344           64 :         PG_RETURN_NULL();
    1345          336 :     PG_RETURN_POINT_P(result);
    1346              : }
    1347              : 
    1348              : /*
    1349              :  * Internal version of line_interpt
    1350              :  *
    1351              :  * Return whether two lines intersect. If *result is not NULL, it is set to
    1352              :  * the intersection point.
    1353              :  *
    1354              :  * NOTE: If the lines are identical then we will find they are parallel
    1355              :  * and report "no intersection".  This is a little weird, but since
    1356              :  * there's no *unique* intersection, maybe it's appropriate behavior.
    1357              :  *
    1358              :  * If the lines have NaN constants, we will return true, and the intersection
    1359              :  * point would have NaN coordinates.  We shouldn't return false in this case
    1360              :  * because that would mean the lines are parallel.
    1361              :  */
    1362              : static bool
    1363      2781900 : line_interpt_line(Point *result, LINE *l1, LINE *l2)
    1364              : {
    1365              :     float8      x,
    1366              :                 y;
    1367              : 
    1368      2781900 :     if (!FPzero(l1->B))
    1369              :     {
    1370      2024300 :         if (FPeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
    1371         6392 :             return false;
    1372              : 
    1373      2017908 :         x = float8_div(float8_mi(float8_mul(l1->B, l2->C),
    1374              :                                  float8_mul(l2->B, l1->C)),
    1375              :                        float8_mi(float8_mul(l1->A, l2->B),
    1376              :                                  float8_mul(l2->A, l1->B)));
    1377      2017908 :         y = float8_div(-float8_pl(float8_mul(l1->A, x), l1->C), l1->B);
    1378              :     }
    1379       757600 :     else if (!FPzero(l2->B))
    1380              :     {
    1381       755628 :         if (FPeq(l1->A, float8_mul(l2->A, float8_div(l1->B, l2->B))))
    1382            0 :             return false;
    1383              : 
    1384       755628 :         x = float8_div(float8_mi(float8_mul(l2->B, l1->C),
    1385              :                                  float8_mul(l1->B, l2->C)),
    1386              :                        float8_mi(float8_mul(l2->A, l1->B),
    1387              :                                  float8_mul(l1->A, l2->B)));
    1388       755628 :         y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
    1389              :     }
    1390              :     else
    1391         1972 :         return false;
    1392              : 
    1393              :     /* On some platforms, the preceding expressions tend to produce -0. */
    1394      2773536 :     if (x == 0.0)
    1395        81140 :         x = 0.0;
    1396      2773536 :     if (y == 0.0)
    1397        79952 :         y = 0.0;
    1398              : 
    1399      2773536 :     if (result != NULL)
    1400      2772528 :         point_construct(result, x, y);
    1401              : 
    1402      2773536 :     return true;
    1403              : }
    1404              : 
    1405              : 
    1406              : /***********************************************************************
    1407              :  **
    1408              :  **     Routines for 2D paths (sequences of line segments, also
    1409              :  **             called `polylines').
    1410              :  **
    1411              :  **             This is not a general package for geometric paths,
    1412              :  **             which of course include polygons; the emphasis here
    1413              :  **             is on (for example) usefulness in wire layout.
    1414              :  **
    1415              :  ***********************************************************************/
    1416              : 
    1417              : /*----------------------------------------------------------
    1418              :  *  String to path / path to string conversion.
    1419              :  *      External format:
    1420              :  *              "((xcoord, ycoord),... )"
    1421              :  *              "[(xcoord, ycoord),... ]"
    1422              :  *              "(xcoord, ycoord),... "
    1423              :  *              "[xcoord, ycoord,... ]"
    1424              :  *      Also support older format:
    1425              :  *              "(closed, npts, xcoord, ycoord,... )"
    1426              :  *---------------------------------------------------------*/
    1427              : 
    1428              : Datum
    1429           36 : path_area(PG_FUNCTION_ARGS)
    1430              : {
    1431           36 :     PATH       *path = PG_GETARG_PATH_P(0);
    1432           36 :     float8      area = 0.0;
    1433              :     int         i,
    1434              :                 j;
    1435              : 
    1436           36 :     if (!path->closed)
    1437           16 :         PG_RETURN_NULL();
    1438              : 
    1439           56 :     for (i = 0; i < path->npts; i++)
    1440              :     {
    1441           36 :         j = (i + 1) % path->npts;
    1442           36 :         area = float8_pl(area, float8_mul(path->p[i].x, path->p[j].y));
    1443           36 :         area = float8_mi(area, float8_mul(path->p[i].y, path->p[j].x));
    1444              :     }
    1445              : 
    1446           20 :     PG_RETURN_FLOAT8(float8_div(fabs(area), 2.0));
    1447              : }
    1448              : 
    1449              : 
    1450              : Datum
    1451        20613 : path_in(PG_FUNCTION_ARGS)
    1452              : {
    1453        20613 :     char       *str = PG_GETARG_CSTRING(0);
    1454        20613 :     Node       *escontext = fcinfo->context;
    1455              :     PATH       *path;
    1456              :     bool        isopen;
    1457              :     char       *s;
    1458              :     int         npts;
    1459              :     int         size;
    1460              :     int         base_size;
    1461        20613 :     int         depth = 0;
    1462              : 
    1463        20613 :     if ((npts = pair_count(str, ',')) <= 0)
    1464           12 :         ereturn(escontext, (Datum) 0,
    1465              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1466              :                  errmsg("invalid input syntax for type %s: \"%s\"",
    1467              :                         "path", str)));
    1468              : 
    1469        20601 :     s = str;
    1470        20609 :     while (isspace((unsigned char) *s))
    1471            8 :         s++;
    1472              : 
    1473              :     /* skip single leading paren */
    1474        20601 :     if ((*s == LDELIM) && (strrchr(s, LDELIM) == s))
    1475              :     {
    1476           16 :         s++;
    1477           16 :         depth++;
    1478              :     }
    1479              : 
    1480        20601 :     base_size = sizeof(path->p[0]) * npts;
    1481        20601 :     size = offsetof(PATH, p) + base_size;
    1482              : 
    1483              :     /* Check for integer overflow */
    1484        20601 :     if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
    1485            0 :         ereturn(escontext, (Datum) 0,
    1486              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1487              :                  errmsg("too many points requested")));
    1488              : 
    1489        20601 :     path = (PATH *) palloc(size);
    1490              : 
    1491        20601 :     SET_VARSIZE(path, size);
    1492        20601 :     path->npts = npts;
    1493              : 
    1494        20601 :     if (!path_decode(s, true, npts, &(path->p[0]), &isopen, &s, "path", str,
    1495              :                      escontext))
    1496            8 :         PG_RETURN_NULL();
    1497              : 
    1498        20585 :     if (depth >= 1)
    1499              :     {
    1500           16 :         if (*s++ != RDELIM)
    1501            4 :             ereturn(escontext, (Datum) 0,
    1502              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1503              :                      errmsg("invalid input syntax for type %s: \"%s\"",
    1504              :                             "path", str)));
    1505           16 :         while (isspace((unsigned char) *s))
    1506            4 :             s++;
    1507              :     }
    1508        20581 :     if (*s != '\0')
    1509            4 :         ereturn(escontext, (Datum) 0,
    1510              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    1511              :                  errmsg("invalid input syntax for type %s: \"%s\"",
    1512              :                         "path", str)));
    1513              : 
    1514        20577 :     path->closed = (!isopen);
    1515              :     /* prevent instability in unused pad bytes */
    1516        20577 :     path->dummy = 0;
    1517              : 
    1518        20577 :     PG_RETURN_PATH_P(path);
    1519              : }
    1520              : 
    1521              : 
    1522              : Datum
    1523        31871 : path_out(PG_FUNCTION_ARGS)
    1524              : {
    1525        31871 :     PATH       *path = PG_GETARG_PATH_P(0);
    1526              : 
    1527        31871 :     PG_RETURN_CSTRING(path_encode(path->closed ? PATH_CLOSED : PATH_OPEN, path->npts, path->p));
    1528              : }
    1529              : 
    1530              : /*
    1531              :  *      path_recv           - converts external binary format to path
    1532              :  *
    1533              :  * External representation is closed flag (a boolean byte), int32 number
    1534              :  * of points, and the points.
    1535              :  */
    1536              : Datum
    1537            0 : path_recv(PG_FUNCTION_ARGS)
    1538              : {
    1539            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    1540              :     PATH       *path;
    1541              :     int         closed;
    1542              :     int32       npts;
    1543              :     int32       i;
    1544              :     int         size;
    1545              : 
    1546            0 :     closed = pq_getmsgbyte(buf);
    1547            0 :     npts = pq_getmsgint(buf, sizeof(int32));
    1548            0 :     if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(PATH, p)) / sizeof(Point)))
    1549            0 :         ereport(ERROR,
    1550              :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    1551              :                  errmsg("invalid number of points in external \"path\" value")));
    1552              : 
    1553            0 :     size = offsetof(PATH, p) + sizeof(path->p[0]) * npts;
    1554            0 :     path = (PATH *) palloc(size);
    1555              : 
    1556            0 :     SET_VARSIZE(path, size);
    1557            0 :     path->npts = npts;
    1558            0 :     path->closed = (closed ? 1 : 0);
    1559              :     /* prevent instability in unused pad bytes */
    1560            0 :     path->dummy = 0;
    1561              : 
    1562            0 :     for (i = 0; i < npts; i++)
    1563              :     {
    1564            0 :         path->p[i].x = pq_getmsgfloat8(buf);
    1565            0 :         path->p[i].y = pq_getmsgfloat8(buf);
    1566              :     }
    1567              : 
    1568            0 :     PG_RETURN_PATH_P(path);
    1569              : }
    1570              : 
    1571              : /*
    1572              :  *      path_send           - converts path to binary format
    1573              :  */
    1574              : Datum
    1575            0 : path_send(PG_FUNCTION_ARGS)
    1576              : {
    1577            0 :     PATH       *path = PG_GETARG_PATH_P(0);
    1578              :     StringInfoData buf;
    1579              :     int32       i;
    1580              : 
    1581            0 :     pq_begintypsend(&buf);
    1582            0 :     pq_sendbyte(&buf, path->closed ? 1 : 0);
    1583            0 :     pq_sendint32(&buf, path->npts);
    1584            0 :     for (i = 0; i < path->npts; i++)
    1585              :     {
    1586            0 :         pq_sendfloat8(&buf, path->p[i].x);
    1587            0 :         pq_sendfloat8(&buf, path->p[i].y);
    1588              :     }
    1589            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    1590              : }
    1591              : 
    1592              : 
    1593              : /*----------------------------------------------------------
    1594              :  *  Relational operators.
    1595              :  *      These are based on the path cardinality,
    1596              :  *      as stupid as that sounds.
    1597              :  *
    1598              :  *      Better relops and access methods coming soon.
    1599              :  *---------------------------------------------------------*/
    1600              : 
    1601              : Datum
    1602          324 : path_n_lt(PG_FUNCTION_ARGS)
    1603              : {
    1604          324 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1605          324 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1606              : 
    1607          324 :     PG_RETURN_BOOL(p1->npts < p2->npts);
    1608              : }
    1609              : 
    1610              : Datum
    1611          324 : path_n_gt(PG_FUNCTION_ARGS)
    1612              : {
    1613          324 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1614          324 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1615              : 
    1616          324 :     PG_RETURN_BOOL(p1->npts > p2->npts);
    1617              : }
    1618              : 
    1619              : Datum
    1620          325 : path_n_eq(PG_FUNCTION_ARGS)
    1621              : {
    1622          325 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1623          325 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1624              : 
    1625          325 :     PG_RETURN_BOOL(p1->npts == p2->npts);
    1626              : }
    1627              : 
    1628              : Datum
    1629          324 : path_n_le(PG_FUNCTION_ARGS)
    1630              : {
    1631          324 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1632          324 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1633              : 
    1634          324 :     PG_RETURN_BOOL(p1->npts <= p2->npts);
    1635              : }
    1636              : 
    1637              : Datum
    1638          324 : path_n_ge(PG_FUNCTION_ARGS)
    1639              : {
    1640          324 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1641          324 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1642              : 
    1643          324 :     PG_RETURN_BOOL(p1->npts >= p2->npts);
    1644              : }
    1645              : 
    1646              : /*----------------------------------------------------------
    1647              :  * Conversion operators.
    1648              :  *---------------------------------------------------------*/
    1649              : 
    1650              : Datum
    1651          108 : path_isclosed(PG_FUNCTION_ARGS)
    1652              : {
    1653          108 :     PATH       *path = PG_GETARG_PATH_P(0);
    1654              : 
    1655          108 :     PG_RETURN_BOOL(path->closed);
    1656              : }
    1657              : 
    1658              : Datum
    1659           76 : path_isopen(PG_FUNCTION_ARGS)
    1660              : {
    1661           76 :     PATH       *path = PG_GETARG_PATH_P(0);
    1662              : 
    1663           76 :     PG_RETURN_BOOL(!path->closed);
    1664              : }
    1665              : 
    1666              : Datum
    1667         3620 : path_npoints(PG_FUNCTION_ARGS)
    1668              : {
    1669         3620 :     PATH       *path = PG_GETARG_PATH_P(0);
    1670              : 
    1671         3620 :     PG_RETURN_INT32(path->npts);
    1672              : }
    1673              : 
    1674              : 
    1675              : Datum
    1676           52 : path_close(PG_FUNCTION_ARGS)
    1677              : {
    1678           52 :     PATH       *path = PG_GETARG_PATH_P_COPY(0);
    1679              : 
    1680           52 :     path->closed = true;
    1681              : 
    1682           52 :     PG_RETURN_PATH_P(path);
    1683              : }
    1684              : 
    1685              : Datum
    1686           36 : path_open(PG_FUNCTION_ARGS)
    1687              : {
    1688           36 :     PATH       *path = PG_GETARG_PATH_P_COPY(0);
    1689              : 
    1690           36 :     path->closed = false;
    1691              : 
    1692           36 :     PG_RETURN_PATH_P(path);
    1693              : }
    1694              : 
    1695              : 
    1696              : /*
    1697              :  * path_inter -
    1698              :  *      Does p1 intersect p2 at any point?
    1699              :  *      Use bounding boxes for a quick (O(n)) check, then do a
    1700              :  *      O(n^2) iterative edge check.
    1701              :  */
    1702              : Datum
    1703       920612 : path_inter(PG_FUNCTION_ARGS)
    1704              : {
    1705       920612 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1706       920612 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1707              :     BOX         b1,
    1708              :                 b2;
    1709              :     int         i,
    1710              :                 j;
    1711              :     LSEG        seg1,
    1712              :                 seg2;
    1713              : 
    1714              :     Assert(p1->npts > 0 && p2->npts > 0);
    1715              : 
    1716       920612 :     b1.high.x = b1.low.x = p1->p[0].x;
    1717       920612 :     b1.high.y = b1.low.y = p1->p[0].y;
    1718      3501400 :     for (i = 1; i < p1->npts; i++)
    1719              :     {
    1720      2580788 :         b1.high.x = float8_max(p1->p[i].x, b1.high.x);
    1721      2580788 :         b1.high.y = float8_max(p1->p[i].y, b1.high.y);
    1722      2580788 :         b1.low.x = float8_min(p1->p[i].x, b1.low.x);
    1723      2580788 :         b1.low.y = float8_min(p1->p[i].y, b1.low.y);
    1724              :     }
    1725       920612 :     b2.high.x = b2.low.x = p2->p[0].x;
    1726       920612 :     b2.high.y = b2.low.y = p2->p[0].y;
    1727      2634216 :     for (i = 1; i < p2->npts; i++)
    1728              :     {
    1729      1713604 :         b2.high.x = float8_max(p2->p[i].x, b2.high.x);
    1730      1713604 :         b2.high.y = float8_max(p2->p[i].y, b2.high.y);
    1731      1713604 :         b2.low.x = float8_min(p2->p[i].x, b2.low.x);
    1732      1713604 :         b2.low.y = float8_min(p2->p[i].y, b2.low.y);
    1733              :     }
    1734       920612 :     if (!box_ov(&b1, &b2))
    1735       898280 :         PG_RETURN_BOOL(false);
    1736              : 
    1737              :     /* pairwise check lseg intersections */
    1738       110008 :     for (i = 0; i < p1->npts; i++)
    1739              :     {
    1740              :         int         iprev;
    1741              : 
    1742        92592 :         if (i > 0)
    1743        70260 :             iprev = i - 1;
    1744              :         else
    1745              :         {
    1746        22332 :             if (!p1->closed)
    1747         5324 :                 continue;
    1748        17008 :             iprev = p1->npts - 1;    /* include the closure segment */
    1749              :         }
    1750              : 
    1751       290444 :         for (j = 0; j < p2->npts; j++)
    1752              :         {
    1753              :             int         jprev;
    1754              : 
    1755       208092 :             if (j > 0)
    1756       120824 :                 jprev = j - 1;
    1757              :             else
    1758              :             {
    1759        87268 :                 if (!p2->closed)
    1760        82492 :                     continue;
    1761         4776 :                 jprev = p2->npts - 1;    /* include the closure segment */
    1762              :             }
    1763              : 
    1764       125600 :             statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
    1765       125600 :             statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
    1766       125600 :             if (lseg_interpt_lseg(NULL, &seg1, &seg2))
    1767         4916 :                 PG_RETURN_BOOL(true);
    1768              :         }
    1769              :     }
    1770              : 
    1771              :     /* if we dropped through, no two segs intersected */
    1772        17416 :     PG_RETURN_BOOL(false);
    1773              : }
    1774              : 
    1775              : /*
    1776              :  * path_distance()
    1777              :  * This essentially does a cartesian product of the lsegs in the
    1778              :  *  two paths, and finds the min distance between any two lsegs
    1779              :  */
    1780              : Datum
    1781          324 : path_distance(PG_FUNCTION_ARGS)
    1782              : {
    1783          324 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    1784          324 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    1785          324 :     float8      min = 0.0;      /* initialize to keep compiler quiet */
    1786          324 :     bool        have_min = false;
    1787              :     float8      tmp;
    1788              :     int         i,
    1789              :                 j;
    1790              :     LSEG        seg1,
    1791              :                 seg2;
    1792              : 
    1793         1008 :     for (i = 0; i < p1->npts; i++)
    1794              :     {
    1795              :         int         iprev;
    1796              : 
    1797          684 :         if (i > 0)
    1798          360 :             iprev = i - 1;
    1799              :         else
    1800              :         {
    1801          324 :             if (!p1->closed)
    1802          144 :                 continue;
    1803          180 :             iprev = p1->npts - 1;    /* include the closure segment */
    1804              :         }
    1805              : 
    1806         1680 :         for (j = 0; j < p2->npts; j++)
    1807              :         {
    1808              :             int         jprev;
    1809              : 
    1810         1140 :             if (j > 0)
    1811          600 :                 jprev = j - 1;
    1812              :             else
    1813              :             {
    1814          540 :                 if (!p2->closed)
    1815          240 :                     continue;
    1816          300 :                 jprev = p2->npts - 1;    /* include the closure segment */
    1817              :             }
    1818              : 
    1819          900 :             statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
    1820          900 :             statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
    1821              : 
    1822          900 :             tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
    1823          900 :             if (!have_min || float8_lt(tmp, min))
    1824              :             {
    1825          388 :                 min = tmp;
    1826          388 :                 have_min = true;
    1827              :             }
    1828              :         }
    1829              :     }
    1830              : 
    1831          324 :     if (!have_min)
    1832            0 :         PG_RETURN_NULL();
    1833              : 
    1834          324 :     PG_RETURN_FLOAT8(min);
    1835              : }
    1836              : 
    1837              : 
    1838              : /*----------------------------------------------------------
    1839              :  *  "Arithmetic" operations.
    1840              :  *---------------------------------------------------------*/
    1841              : 
    1842              : Datum
    1843           36 : path_length(PG_FUNCTION_ARGS)
    1844              : {
    1845           36 :     PATH       *path = PG_GETARG_PATH_P(0);
    1846           36 :     float8      result = 0.0;
    1847              :     int         i;
    1848              : 
    1849          112 :     for (i = 0; i < path->npts; i++)
    1850              :     {
    1851              :         int         iprev;
    1852              : 
    1853           76 :         if (i > 0)
    1854           40 :             iprev = i - 1;
    1855              :         else
    1856              :         {
    1857           36 :             if (!path->closed)
    1858           16 :                 continue;
    1859           20 :             iprev = path->npts - 1; /* include the closure segment */
    1860              :         }
    1861              : 
    1862           60 :         result = float8_pl(result, point_dt(&path->p[iprev], &path->p[i], NULL));
    1863              :     }
    1864              : 
    1865           36 :     PG_RETURN_FLOAT8(result);
    1866              : }
    1867              : 
    1868              : /***********************************************************************
    1869              :  **
    1870              :  **     Routines for 2D points.
    1871              :  **
    1872              :  ***********************************************************************/
    1873              : 
    1874              : /*----------------------------------------------------------
    1875              :  *  String to point, point to string conversion.
    1876              :  *      External format:
    1877              :  *              "(x,y)"
    1878              :  *              "x,y"
    1879              :  *---------------------------------------------------------*/
    1880              : 
    1881              : Datum
    1882        17371 : point_in(PG_FUNCTION_ARGS)
    1883              : {
    1884        17371 :     char       *str = PG_GETARG_CSTRING(0);
    1885        17371 :     Point      *point = palloc_object(Point);
    1886              : 
    1887              :     /* Ignore failure from pair_decode, since our return value won't matter */
    1888        17371 :     pair_decode(str, &point->x, &point->y, NULL, "point", str, fcinfo->context);
    1889        17351 :     PG_RETURN_POINT_P(point);
    1890              : }
    1891              : 
    1892              : Datum
    1893       133638 : point_out(PG_FUNCTION_ARGS)
    1894              : {
    1895       133638 :     Point      *pt = PG_GETARG_POINT_P(0);
    1896              : 
    1897       133638 :     PG_RETURN_CSTRING(path_encode(PATH_NONE, 1, pt));
    1898              : }
    1899              : 
    1900              : /*
    1901              :  *      point_recv          - converts external binary format to point
    1902              :  */
    1903              : Datum
    1904           12 : point_recv(PG_FUNCTION_ARGS)
    1905              : {
    1906           12 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    1907              :     Point      *point;
    1908              : 
    1909           12 :     point = palloc_object(Point);
    1910           12 :     point->x = pq_getmsgfloat8(buf);
    1911           12 :     point->y = pq_getmsgfloat8(buf);
    1912           12 :     PG_RETURN_POINT_P(point);
    1913              : }
    1914              : 
    1915              : /*
    1916              :  *      point_send          - converts point to binary format
    1917              :  */
    1918              : Datum
    1919           12 : point_send(PG_FUNCTION_ARGS)
    1920              : {
    1921           12 :     Point      *pt = PG_GETARG_POINT_P(0);
    1922              :     StringInfoData buf;
    1923              : 
    1924           12 :     pq_begintypsend(&buf);
    1925           12 :     pq_sendfloat8(&buf, pt->x);
    1926           12 :     pq_sendfloat8(&buf, pt->y);
    1927           12 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    1928              : }
    1929              : 
    1930              : 
    1931              : /*
    1932              :  * Initialize a point
    1933              :  */
    1934              : static inline void
    1935      3466983 : point_construct(Point *result, float8 x, float8 y)
    1936              : {
    1937      3466983 :     result->x = x;
    1938      3466983 :     result->y = y;
    1939      3466983 : }
    1940              : 
    1941              : 
    1942              : /*----------------------------------------------------------
    1943              :  *  Relational operators for Points.
    1944              :  *      Since we do have a sense of coordinates being
    1945              :  *      "equal" to a given accuracy (point_vert, point_horiz),
    1946              :  *      the other ops must preserve that sense.  This means
    1947              :  *      that results may, strictly speaking, be a lie (unless
    1948              :  *      EPSILON = 0.0).
    1949              :  *---------------------------------------------------------*/
    1950              : 
    1951              : Datum
    1952       471970 : point_left(PG_FUNCTION_ARGS)
    1953              : {
    1954       471970 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    1955       471970 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    1956              : 
    1957       471970 :     PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
    1958              : }
    1959              : 
    1960              : Datum
    1961     11224719 : point_right(PG_FUNCTION_ARGS)
    1962              : {
    1963     11224719 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    1964     11224719 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    1965              : 
    1966     11224719 :     PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
    1967              : }
    1968              : 
    1969              : Datum
    1970     11383422 : point_above(PG_FUNCTION_ARGS)
    1971              : {
    1972     11383422 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    1973     11383422 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    1974              : 
    1975     11383422 :     PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
    1976              : }
    1977              : 
    1978              : Datum
    1979       835181 : point_below(PG_FUNCTION_ARGS)
    1980              : {
    1981       835181 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    1982       835181 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    1983              : 
    1984       835181 :     PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
    1985              : }
    1986              : 
    1987              : Datum
    1988       332274 : point_vert(PG_FUNCTION_ARGS)
    1989              : {
    1990       332274 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    1991       332274 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    1992              : 
    1993       332274 :     PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
    1994              : }
    1995              : 
    1996              : Datum
    1997       355538 : point_horiz(PG_FUNCTION_ARGS)
    1998              : {
    1999       355538 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    2000       355538 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    2001              : 
    2002       355538 :     PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
    2003              : }
    2004              : 
    2005              : Datum
    2006        53469 : point_eq(PG_FUNCTION_ARGS)
    2007              : {
    2008        53469 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    2009        53469 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    2010              : 
    2011        53469 :     PG_RETURN_BOOL(point_eq_point(pt1, pt2));
    2012              : }
    2013              : 
    2014              : Datum
    2015          468 : point_ne(PG_FUNCTION_ARGS)
    2016              : {
    2017          468 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    2018          468 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    2019              : 
    2020          468 :     PG_RETURN_BOOL(!point_eq_point(pt1, pt2));
    2021              : }
    2022              : 
    2023              : 
    2024              : /*
    2025              :  * Check whether the two points are the same
    2026              :  */
    2027              : static inline bool
    2028       202261 : point_eq_point(Point *pt1, Point *pt2)
    2029              : {
    2030              :     /* If any NaNs are involved, insist on exact equality */
    2031       202261 :     if (unlikely(isnan(pt1->x) || isnan(pt1->y) ||
    2032              :                  isnan(pt2->x) || isnan(pt2->y)))
    2033          284 :         return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y));
    2034              : 
    2035       201977 :     return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
    2036              : }
    2037              : 
    2038              : 
    2039              : /*----------------------------------------------------------
    2040              :  *  "Arithmetic" operators on points.
    2041              :  *---------------------------------------------------------*/
    2042              : 
    2043              : Datum
    2044       493117 : point_distance(PG_FUNCTION_ARGS)
    2045              : {
    2046       493117 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    2047       493117 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    2048              : 
    2049       493117 :     PG_RETURN_FLOAT8(point_dt(pt1, pt2, NULL));
    2050              : }
    2051              : 
    2052              : static inline float8
    2053     10364497 : point_dt(Point *pt1, Point *pt2, Node *escontext)
    2054              : {
    2055              :     float8      x;
    2056              :     float8      y;
    2057              : 
    2058     10364497 :     x = float8_mi_safe(pt1->x, pt2->x, escontext);
    2059     10364497 :     if (unlikely(SOFT_ERROR_OCCURRED(escontext)))
    2060            0 :         return 0.0;
    2061              : 
    2062     10364497 :     y = float8_mi_safe(pt1->y, pt2->y, escontext);
    2063     10364497 :     if (unlikely(SOFT_ERROR_OCCURRED(escontext)))
    2064            0 :         return 0.0;
    2065              : 
    2066     10364497 :     return hypot(x, y);
    2067              : }
    2068              : 
    2069              : Datum
    2070          400 : point_slope(PG_FUNCTION_ARGS)
    2071              : {
    2072          400 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    2073          400 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    2074              : 
    2075          400 :     PG_RETURN_FLOAT8(point_sl(pt1, pt2));
    2076              : }
    2077              : 
    2078              : 
    2079              : /*
    2080              :  * Return slope of two points
    2081              :  *
    2082              :  * Note that this function returns Inf when the points are the same.
    2083              :  */
    2084              : static inline float8
    2085      2566137 : point_sl(Point *pt1, Point *pt2)
    2086              : {
    2087      2566137 :     if (FPeq(pt1->x, pt2->x))
    2088       285669 :         return get_float8_infinity();
    2089      2280468 :     if (FPeq(pt1->y, pt2->y))
    2090       282996 :         return 0.0;
    2091      1997472 :     return float8_div(float8_mi(pt1->y, pt2->y), float8_mi(pt1->x, pt2->x));
    2092              : }
    2093              : 
    2094              : 
    2095              : /*
    2096              :  * Return inverse slope of two points
    2097              :  *
    2098              :  * Note that this function returns 0.0 when the points are the same.
    2099              :  */
    2100              : static inline float8
    2101       604320 : point_invsl(Point *pt1, Point *pt2)
    2102              : {
    2103       604320 :     if (FPeq(pt1->x, pt2->x))
    2104       239072 :         return 0.0;
    2105       365248 :     if (FPeq(pt1->y, pt2->y))
    2106       234136 :         return get_float8_infinity();
    2107       131112 :     return float8_div(float8_mi(pt1->x, pt2->x), float8_mi(pt2->y, pt1->y));
    2108              : }
    2109              : 
    2110              : 
    2111              : /***********************************************************************
    2112              :  **
    2113              :  **     Routines for 2D line segments.
    2114              :  **
    2115              :  ***********************************************************************/
    2116              : 
    2117              : /*----------------------------------------------------------
    2118              :  *  String to lseg, lseg to string conversion.
    2119              :  *      External forms: "[(x1, y1), (x2, y2)]"
    2120              :  *                      "(x1, y1), (x2, y2)"
    2121              :  *                      "x1, y1, x2, y2"
    2122              :  *      closed form ok  "((x1, y1), (x2, y2))"
    2123              :  *      (old form)      "(x1, y1, x2, y2)"
    2124              :  *---------------------------------------------------------*/
    2125              : 
    2126              : Datum
    2127           69 : lseg_in(PG_FUNCTION_ARGS)
    2128              : {
    2129           69 :     char       *str = PG_GETARG_CSTRING(0);
    2130           69 :     Node       *escontext = fcinfo->context;
    2131           69 :     LSEG       *lseg = palloc_object(LSEG);
    2132              :     bool        isopen;
    2133              : 
    2134           69 :     if (!path_decode(str, true, 2, &lseg->p[0], &isopen, NULL, "lseg", str,
    2135              :                      escontext))
    2136            8 :         PG_RETURN_NULL();
    2137              : 
    2138           45 :     PG_RETURN_LSEG_P(lseg);
    2139              : }
    2140              : 
    2141              : 
    2142              : Datum
    2143         4871 : lseg_out(PG_FUNCTION_ARGS)
    2144              : {
    2145         4871 :     LSEG       *ls = PG_GETARG_LSEG_P(0);
    2146              : 
    2147         4871 :     PG_RETURN_CSTRING(path_encode(PATH_OPEN, 2, &ls->p[0]));
    2148              : }
    2149              : 
    2150              : /*
    2151              :  *      lseg_recv           - converts external binary format to lseg
    2152              :  */
    2153              : Datum
    2154            0 : lseg_recv(PG_FUNCTION_ARGS)
    2155              : {
    2156            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    2157              :     LSEG       *lseg;
    2158              : 
    2159            0 :     lseg = palloc_object(LSEG);
    2160              : 
    2161            0 :     lseg->p[0].x = pq_getmsgfloat8(buf);
    2162            0 :     lseg->p[0].y = pq_getmsgfloat8(buf);
    2163            0 :     lseg->p[1].x = pq_getmsgfloat8(buf);
    2164            0 :     lseg->p[1].y = pq_getmsgfloat8(buf);
    2165              : 
    2166            0 :     PG_RETURN_LSEG_P(lseg);
    2167              : }
    2168              : 
    2169              : /*
    2170              :  *      lseg_send           - converts lseg to binary format
    2171              :  */
    2172              : Datum
    2173            0 : lseg_send(PG_FUNCTION_ARGS)
    2174              : {
    2175            0 :     LSEG       *ls = PG_GETARG_LSEG_P(0);
    2176              :     StringInfoData buf;
    2177              : 
    2178            0 :     pq_begintypsend(&buf);
    2179            0 :     pq_sendfloat8(&buf, ls->p[0].x);
    2180            0 :     pq_sendfloat8(&buf, ls->p[0].y);
    2181            0 :     pq_sendfloat8(&buf, ls->p[1].x);
    2182            0 :     pq_sendfloat8(&buf, ls->p[1].y);
    2183            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    2184              : }
    2185              : 
    2186              : 
    2187              : /*
    2188              :  * lseg_construct -
    2189              :  *      form a LSEG from two Points.
    2190              :  */
    2191              : Datum
    2192            5 : lseg_construct(PG_FUNCTION_ARGS)
    2193              : {
    2194            5 :     Point      *pt1 = PG_GETARG_POINT_P(0);
    2195            5 :     Point      *pt2 = PG_GETARG_POINT_P(1);
    2196            5 :     LSEG       *result = palloc_object(LSEG);
    2197              : 
    2198            5 :     statlseg_construct(result, pt1, pt2);
    2199              : 
    2200            5 :     PG_RETURN_LSEG_P(result);
    2201              : }
    2202              : 
    2203              : /* like lseg_construct, but assume space already allocated */
    2204              : static inline void
    2205       673949 : statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
    2206              : {
    2207       673949 :     lseg->p[0].x = pt1->x;
    2208       673949 :     lseg->p[0].y = pt1->y;
    2209       673949 :     lseg->p[1].x = pt2->x;
    2210       673949 :     lseg->p[1].y = pt2->y;
    2211       673949 : }
    2212              : 
    2213              : 
    2214              : /*
    2215              :  * Return slope of the line segment
    2216              :  */
    2217              : static inline float8
    2218      2565380 : lseg_sl(LSEG *lseg)
    2219              : {
    2220      2565380 :     return point_sl(&lseg->p[0], &lseg->p[1]);
    2221              : }
    2222              : 
    2223              : 
    2224              : /*
    2225              :  * Return inverse slope of the line segment
    2226              :  */
    2227              : static inline float8
    2228          256 : lseg_invsl(LSEG *lseg)
    2229              : {
    2230          256 :     return point_invsl(&lseg->p[0], &lseg->p[1]);
    2231              : }
    2232              : 
    2233              : 
    2234              : Datum
    2235           32 : lseg_length(PG_FUNCTION_ARGS)
    2236              : {
    2237           32 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2238              : 
    2239           32 :     PG_RETURN_FLOAT8(point_dt(&lseg->p[0], &lseg->p[1], NULL));
    2240              : }
    2241              : 
    2242              : /*----------------------------------------------------------
    2243              :  *  Relative position routines.
    2244              :  *---------------------------------------------------------*/
    2245              : 
    2246              : /*
    2247              :  *  find intersection of the two lines, and see if it falls on
    2248              :  *  both segments.
    2249              :  */
    2250              : Datum
    2251        16912 : lseg_intersect(PG_FUNCTION_ARGS)
    2252              : {
    2253        16912 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2254        16912 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2255              : 
    2256        16912 :     PG_RETURN_BOOL(lseg_interpt_lseg(NULL, l1, l2));
    2257              : }
    2258              : 
    2259              : 
    2260              : Datum
    2261          256 : lseg_parallel(PG_FUNCTION_ARGS)
    2262              : {
    2263          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2264          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2265              : 
    2266          256 :     PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_sl(l2)));
    2267              : }
    2268              : 
    2269              : /*
    2270              :  * Determine if two line segments are perpendicular.
    2271              :  */
    2272              : Datum
    2273          256 : lseg_perp(PG_FUNCTION_ARGS)
    2274              : {
    2275          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2276          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2277              : 
    2278          256 :     PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_invsl(l2)));
    2279              : }
    2280              : 
    2281              : Datum
    2282           32 : lseg_vertical(PG_FUNCTION_ARGS)
    2283              : {
    2284           32 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2285              : 
    2286           32 :     PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
    2287              : }
    2288              : 
    2289              : Datum
    2290           32 : lseg_horizontal(PG_FUNCTION_ARGS)
    2291              : {
    2292           32 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2293              : 
    2294           32 :     PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
    2295              : }
    2296              : 
    2297              : 
    2298              : Datum
    2299          257 : lseg_eq(PG_FUNCTION_ARGS)
    2300              : {
    2301          257 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2302          257 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2303              : 
    2304          257 :     PG_RETURN_BOOL(point_eq_point(&l1->p[0], &l2->p[0]) &&
    2305              :                    point_eq_point(&l1->p[1], &l2->p[1]));
    2306              : }
    2307              : 
    2308              : Datum
    2309          256 : lseg_ne(PG_FUNCTION_ARGS)
    2310              : {
    2311          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2312          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2313              : 
    2314          256 :     PG_RETURN_BOOL(!point_eq_point(&l1->p[0], &l2->p[0]) ||
    2315              :                    !point_eq_point(&l1->p[1], &l2->p[1]));
    2316              : }
    2317              : 
    2318              : Datum
    2319          256 : lseg_lt(PG_FUNCTION_ARGS)
    2320              : {
    2321          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2322          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2323              : 
    2324          256 :     PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1], NULL),
    2325              :                         point_dt(&l2->p[0], &l2->p[1], NULL)));
    2326              : }
    2327              : 
    2328              : Datum
    2329          256 : lseg_le(PG_FUNCTION_ARGS)
    2330              : {
    2331          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2332          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2333              : 
    2334          256 :     PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1], NULL),
    2335              :                         point_dt(&l2->p[0], &l2->p[1], NULL)));
    2336              : }
    2337              : 
    2338              : Datum
    2339          256 : lseg_gt(PG_FUNCTION_ARGS)
    2340              : {
    2341          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2342          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2343              : 
    2344          256 :     PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1], NULL),
    2345              :                         point_dt(&l2->p[0], &l2->p[1], NULL)));
    2346              : }
    2347              : 
    2348              : Datum
    2349          256 : lseg_ge(PG_FUNCTION_ARGS)
    2350              : {
    2351          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2352          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2353              : 
    2354          256 :     PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1], NULL),
    2355              :                         point_dt(&l2->p[0], &l2->p[1], NULL)));
    2356              : }
    2357              : 
    2358              : 
    2359              : /*----------------------------------------------------------
    2360              :  *  Line arithmetic routines.
    2361              :  *---------------------------------------------------------*/
    2362              : 
    2363              : /*
    2364              :  * lseg_distance -
    2365              :  *      If two segments don't intersect, then the closest
    2366              :  *      point will be from one of the endpoints to the other
    2367              :  *      segment.
    2368              :  */
    2369              : Datum
    2370          256 : lseg_distance(PG_FUNCTION_ARGS)
    2371              : {
    2372          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2373          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2374              : 
    2375          256 :     PG_RETURN_FLOAT8(lseg_closept_lseg(NULL, l1, l2));
    2376              : }
    2377              : 
    2378              : 
    2379              : Datum
    2380           64 : lseg_center(PG_FUNCTION_ARGS)
    2381              : {
    2382           64 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2383              :     Point      *result;
    2384              :     float8      x;
    2385              :     float8      y;
    2386              : 
    2387           64 :     result = palloc_object(Point);
    2388              : 
    2389           64 :     x = float8_pl_safe(lseg->p[0].x, lseg->p[1].x, fcinfo->context);
    2390           64 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    2391            0 :         goto fail;
    2392              : 
    2393           64 :     result->x = float8_div_safe(x, 2.0, fcinfo->context);
    2394           64 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    2395            0 :         goto fail;
    2396              : 
    2397           64 :     y = float8_pl_safe(lseg->p[0].y, lseg->p[1].y, fcinfo->context);
    2398           64 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    2399            0 :         goto fail;
    2400              : 
    2401           64 :     result->y = float8_div_safe(y, 2.0, fcinfo->context);
    2402           64 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    2403            0 :         goto fail;
    2404              : 
    2405           64 :     PG_RETURN_POINT_P(result);
    2406              : 
    2407            0 : fail:
    2408            0 :     PG_RETURN_NULL();
    2409              : }
    2410              : 
    2411              : 
    2412              : /*
    2413              :  * Return whether the two segments intersect. If *result is not NULL,
    2414              :  * it is set to the intersection point.
    2415              :  *
    2416              :  * This function is almost perfectly symmetric, even though it doesn't look
    2417              :  * like it.  See lseg_interpt_line() for the other half of it.
    2418              :  */
    2419              : static bool
    2420       978900 : lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
    2421              : {
    2422              :     Point       interpt;
    2423              :     LINE        tmp;
    2424              : 
    2425       978900 :     line_construct(&tmp, &l2->p[0], lseg_sl(l2));
    2426       978900 :     if (!lseg_interpt_line(&interpt, l1, &tmp))
    2427       923904 :         return false;
    2428              : 
    2429              :     /*
    2430              :      * If the line intersection point isn't within l2, there is no valid
    2431              :      * segment intersection point at all.
    2432              :      */
    2433        54996 :     if (!lseg_contain_point(l2, &interpt))
    2434        41188 :         return false;
    2435              : 
    2436        13808 :     if (result != NULL)
    2437         4476 :         *result = interpt;
    2438              : 
    2439        13808 :     return true;
    2440              : }
    2441              : 
    2442              : Datum
    2443         3832 : lseg_interpt(PG_FUNCTION_ARGS)
    2444              : {
    2445         3832 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2446         3832 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2447              :     Point      *result;
    2448              : 
    2449         3832 :     result = palloc_object(Point);
    2450              : 
    2451         3832 :     if (!lseg_interpt_lseg(result, l1, l2))
    2452          256 :         PG_RETURN_NULL();
    2453         3576 :     PG_RETURN_POINT_P(result);
    2454              : }
    2455              : 
    2456              : /***********************************************************************
    2457              :  **
    2458              :  **     Routines for position comparisons of differently-typed
    2459              :  **             2D objects.
    2460              :  **
    2461              :  ***********************************************************************/
    2462              : 
    2463              : /*---------------------------------------------------------------------
    2464              :  *      dist_
    2465              :  *              Minimum distance from one object to another.
    2466              :  *-------------------------------------------------------------------*/
    2467              : 
    2468              : /*
    2469              :  * Distance from a point to a line
    2470              :  */
    2471              : Datum
    2472          400 : dist_pl(PG_FUNCTION_ARGS)
    2473              : {
    2474          400 :     Point      *pt = PG_GETARG_POINT_P(0);
    2475          400 :     LINE       *line = PG_GETARG_LINE_P(1);
    2476              : 
    2477          400 :     PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt));
    2478              : }
    2479              : 
    2480              : /*
    2481              :  * Distance from a line to a point
    2482              :  */
    2483              : Datum
    2484          400 : dist_lp(PG_FUNCTION_ARGS)
    2485              : {
    2486          400 :     LINE       *line = PG_GETARG_LINE_P(0);
    2487          400 :     Point      *pt = PG_GETARG_POINT_P(1);
    2488              : 
    2489          400 :     PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt));
    2490              : }
    2491              : 
    2492              : /*
    2493              :  * Distance from a point to a lseg
    2494              :  */
    2495              : Datum
    2496          320 : dist_ps(PG_FUNCTION_ARGS)
    2497              : {
    2498          320 :     Point      *pt = PG_GETARG_POINT_P(0);
    2499          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(1);
    2500              : 
    2501          320 :     PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt));
    2502              : }
    2503              : 
    2504              : /*
    2505              :  * Distance from a lseg to a point
    2506              :  */
    2507              : Datum
    2508          320 : dist_sp(PG_FUNCTION_ARGS)
    2509              : {
    2510          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2511          320 :     Point      *pt = PG_GETARG_POINT_P(1);
    2512              : 
    2513          320 :     PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt));
    2514              : }
    2515              : 
    2516              : static float8
    2517          720 : dist_ppath_internal(Point *pt, PATH *path)
    2518              : {
    2519          720 :     float8      result = 0.0;   /* keep compiler quiet */
    2520          720 :     bool        have_min = false;
    2521              :     float8      tmp;
    2522              :     int         i;
    2523              :     LSEG        lseg;
    2524              : 
    2525              :     Assert(path->npts > 0);
    2526              : 
    2527              :     /*
    2528              :      * The distance from a point to a path is the smallest distance from the
    2529              :      * point to any of its constituent segments.
    2530              :      */
    2531         2240 :     for (i = 0; i < path->npts; i++)
    2532              :     {
    2533              :         int         iprev;
    2534              : 
    2535         1520 :         if (i > 0)
    2536          800 :             iprev = i - 1;
    2537              :         else
    2538              :         {
    2539          720 :             if (!path->closed)
    2540          320 :                 continue;
    2541          400 :             iprev = path->npts - 1; /* Include the closure segment */
    2542              :         }
    2543              : 
    2544         1200 :         statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
    2545         1200 :         tmp = lseg_closept_point(NULL, &lseg, pt);
    2546         1200 :         if (!have_min || float8_lt(tmp, result))
    2547              :         {
    2548          752 :             result = tmp;
    2549          752 :             have_min = true;
    2550              :         }
    2551              :     }
    2552              : 
    2553          720 :     return result;
    2554              : }
    2555              : 
    2556              : /*
    2557              :  * Distance from a point to a path
    2558              :  */
    2559              : Datum
    2560          360 : dist_ppath(PG_FUNCTION_ARGS)
    2561              : {
    2562          360 :     Point      *pt = PG_GETARG_POINT_P(0);
    2563          360 :     PATH       *path = PG_GETARG_PATH_P(1);
    2564              : 
    2565          360 :     PG_RETURN_FLOAT8(dist_ppath_internal(pt, path));
    2566              : }
    2567              : 
    2568              : /*
    2569              :  * Distance from a path to a point
    2570              :  */
    2571              : Datum
    2572          360 : dist_pathp(PG_FUNCTION_ARGS)
    2573              : {
    2574          360 :     PATH       *path = PG_GETARG_PATH_P(0);
    2575          360 :     Point      *pt = PG_GETARG_POINT_P(1);
    2576              : 
    2577          360 :     PG_RETURN_FLOAT8(dist_ppath_internal(pt, path));
    2578              : }
    2579              : 
    2580              : /*
    2581              :  * Distance from a point to a box
    2582              :  */
    2583              : Datum
    2584          284 : dist_pb(PG_FUNCTION_ARGS)
    2585              : {
    2586          284 :     Point      *pt = PG_GETARG_POINT_P(0);
    2587          284 :     BOX        *box = PG_GETARG_BOX_P(1);
    2588              : 
    2589          284 :     PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt));
    2590              : }
    2591              : 
    2592              : /*
    2593              :  * Distance from a box to a point
    2594              :  */
    2595              : Datum
    2596       103508 : dist_bp(PG_FUNCTION_ARGS)
    2597              : {
    2598       103508 :     BOX        *box = PG_GETARG_BOX_P(0);
    2599       103508 :     Point      *pt = PG_GETARG_POINT_P(1);
    2600              : 
    2601       103508 :     PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt));
    2602              : }
    2603              : 
    2604              : /*
    2605              :  * Distance from a lseg to a line
    2606              :  */
    2607              : Datum
    2608          320 : dist_sl(PG_FUNCTION_ARGS)
    2609              : {
    2610          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2611          320 :     LINE       *line = PG_GETARG_LINE_P(1);
    2612              : 
    2613          320 :     PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line));
    2614              : }
    2615              : 
    2616              : /*
    2617              :  * Distance from a line to a lseg
    2618              :  */
    2619              : Datum
    2620          320 : dist_ls(PG_FUNCTION_ARGS)
    2621              : {
    2622          320 :     LINE       *line = PG_GETARG_LINE_P(0);
    2623          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(1);
    2624              : 
    2625          320 :     PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line));
    2626              : }
    2627              : 
    2628              : /*
    2629              :  * Distance from a lseg to a box
    2630              :  */
    2631              : Datum
    2632          160 : dist_sb(PG_FUNCTION_ARGS)
    2633              : {
    2634          160 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    2635          160 :     BOX        *box = PG_GETARG_BOX_P(1);
    2636              : 
    2637          160 :     PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg));
    2638              : }
    2639              : 
    2640              : /*
    2641              :  * Distance from a box to a lseg
    2642              :  */
    2643              : Datum
    2644          160 : dist_bs(PG_FUNCTION_ARGS)
    2645              : {
    2646          160 :     BOX        *box = PG_GETARG_BOX_P(0);
    2647          160 :     LSEG       *lseg = PG_GETARG_LSEG_P(1);
    2648              : 
    2649          160 :     PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg));
    2650              : }
    2651              : 
    2652              : static float8
    2653          224 : dist_cpoly_internal(CIRCLE *circle, POLYGON *poly)
    2654              : {
    2655              :     float8      result;
    2656              : 
    2657              :     /* calculate distance to center, and subtract radius */
    2658          224 :     result = float8_mi(dist_ppoly_internal(&circle->center, poly),
    2659              :                        circle->radius);
    2660          224 :     if (result < 0.0)
    2661          120 :         result = 0.0;
    2662              : 
    2663          224 :     return result;
    2664              : }
    2665              : 
    2666              : /*
    2667              :  * Distance from a circle to a polygon
    2668              :  */
    2669              : Datum
    2670          224 : dist_cpoly(PG_FUNCTION_ARGS)
    2671              : {
    2672          224 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    2673          224 :     POLYGON    *poly = PG_GETARG_POLYGON_P(1);
    2674              : 
    2675          224 :     PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly));
    2676              : }
    2677              : 
    2678              : /*
    2679              :  * Distance from a polygon to a circle
    2680              :  */
    2681              : Datum
    2682            0 : dist_polyc(PG_FUNCTION_ARGS)
    2683              : {
    2684            0 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    2685            0 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(1);
    2686              : 
    2687            0 :     PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly));
    2688              : }
    2689              : 
    2690              : /*
    2691              :  * Distance from a point to a polygon
    2692              :  */
    2693              : Datum
    2694          280 : dist_ppoly(PG_FUNCTION_ARGS)
    2695              : {
    2696          280 :     Point      *point = PG_GETARG_POINT_P(0);
    2697          280 :     POLYGON    *poly = PG_GETARG_POLYGON_P(1);
    2698              : 
    2699          280 :     PG_RETURN_FLOAT8(dist_ppoly_internal(point, poly));
    2700              : }
    2701              : 
    2702              : Datum
    2703        22872 : dist_polyp(PG_FUNCTION_ARGS)
    2704              : {
    2705        22872 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    2706        22872 :     Point      *point = PG_GETARG_POINT_P(1);
    2707              : 
    2708        22872 :     PG_RETURN_FLOAT8(dist_ppoly_internal(point, poly));
    2709              : }
    2710              : 
    2711              : static float8
    2712        23376 : dist_ppoly_internal(Point *pt, POLYGON *poly)
    2713              : {
    2714              :     float8      result;
    2715              :     float8      d;
    2716              :     int         i;
    2717              :     LSEG        seg;
    2718              : 
    2719        23376 :     if (point_inside(pt, poly->npts, poly->p) != 0)
    2720          120 :         return 0.0;
    2721              : 
    2722              :     /* initialize distance with segment between first and last points */
    2723        23256 :     seg.p[0].x = poly->p[0].x;
    2724        23256 :     seg.p[0].y = poly->p[0].y;
    2725        23256 :     seg.p[1].x = poly->p[poly->npts - 1].x;
    2726        23256 :     seg.p[1].y = poly->p[poly->npts - 1].y;
    2727        23256 :     result = lseg_closept_point(NULL, &seg, pt);
    2728              : 
    2729              :     /* check distances for other segments */
    2730       172008 :     for (i = 0; i < poly->npts - 1; i++)
    2731              :     {
    2732       148752 :         seg.p[0].x = poly->p[i].x;
    2733       148752 :         seg.p[0].y = poly->p[i].y;
    2734       148752 :         seg.p[1].x = poly->p[i + 1].x;
    2735       148752 :         seg.p[1].y = poly->p[i + 1].y;
    2736       148752 :         d = lseg_closept_point(NULL, &seg, pt);
    2737       148752 :         if (float8_lt(d, result))
    2738         4392 :             result = d;
    2739              :     }
    2740              : 
    2741        23256 :     return result;
    2742              : }
    2743              : 
    2744              : 
    2745              : /*---------------------------------------------------------------------
    2746              :  *      interpt_
    2747              :  *              Intersection point of objects.
    2748              :  *              We choose to ignore the "point" of intersection between
    2749              :  *                lines and boxes, since there are typically two.
    2750              :  *-------------------------------------------------------------------*/
    2751              : 
    2752              : /*
    2753              :  * Return whether the line segment intersect with the line. If *result is not
    2754              :  * NULL, it is set to the intersection point.
    2755              :  */
    2756              : static bool
    2757      1584860 : lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
    2758              : {
    2759              :     Point       interpt;
    2760              :     LINE        tmp;
    2761              : 
    2762              :     /*
    2763              :      * First, we promote the line segment to a line, because we know how to
    2764              :      * find the intersection point of two lines.  If they don't have an
    2765              :      * intersection point, we are done.
    2766              :      */
    2767      1584860 :     line_construct(&tmp, &lseg->p[0], lseg_sl(lseg));
    2768      1584860 :     if (!line_interpt_line(&interpt, &tmp, line))
    2769         8108 :         return false;
    2770              : 
    2771              :     /*
    2772              :      * Then, we check whether the intersection point is actually on the line
    2773              :      * segment.
    2774              :      */
    2775      1576752 :     if (!lseg_contain_point(lseg, &interpt))
    2776      1513768 :         return false;
    2777        62984 :     if (result != NULL)
    2778              :     {
    2779              :         /*
    2780              :          * If there is an intersection, then check explicitly for matching
    2781              :          * endpoints since there may be rounding effects with annoying LSB
    2782              :          * residue.
    2783              :          */
    2784        62728 :         if (point_eq_point(&lseg->p[0], &interpt))
    2785         4408 :             *result = lseg->p[0];
    2786        58320 :         else if (point_eq_point(&lseg->p[1], &interpt))
    2787         8836 :             *result = lseg->p[1];
    2788              :         else
    2789        49484 :             *result = interpt;
    2790              :     }
    2791              : 
    2792        62984 :     return true;
    2793              : }
    2794              : 
    2795              : /*---------------------------------------------------------------------
    2796              :  *      close_
    2797              :  *              Point of closest proximity between objects.
    2798              :  *-------------------------------------------------------------------*/
    2799              : 
    2800              : /*
    2801              :  * If *result is not NULL, it is set to the intersection point of a
    2802              :  * perpendicular of the line through the point.  Returns the distance
    2803              :  * of those two points.
    2804              :  */
    2805              : static float8
    2806      1195440 : line_closept_point(Point *result, LINE *line, Point *point)
    2807              : {
    2808              :     Point       closept;
    2809              :     LINE        tmp;
    2810              : 
    2811              :     /*
    2812              :      * We drop a perpendicular to find the intersection point.  Ordinarily we
    2813              :      * should always find it, but that can fail in the presence of NaN
    2814              :      * coordinates, and perhaps even from simple roundoff issues.
    2815              :      */
    2816      1195440 :     line_construct(&tmp, point, line_invsl(line));
    2817      1195440 :     if (!line_interpt_line(&closept, &tmp, line))
    2818              :     {
    2819            0 :         if (result != NULL)
    2820            0 :             *result = *point;
    2821              : 
    2822            0 :         return get_float8_nan();
    2823              :     }
    2824              : 
    2825      1195440 :     if (result != NULL)
    2826          400 :         *result = closept;
    2827              : 
    2828      1195440 :     return point_dt(&closept, point, NULL);
    2829              : }
    2830              : 
    2831              : Datum
    2832          400 : close_pl(PG_FUNCTION_ARGS)
    2833              : {
    2834          400 :     Point      *pt = PG_GETARG_POINT_P(0);
    2835          400 :     LINE       *line = PG_GETARG_LINE_P(1);
    2836              :     Point      *result;
    2837              : 
    2838          400 :     result = palloc_object(Point);
    2839              : 
    2840          400 :     if (isnan(line_closept_point(result, line, pt)))
    2841          144 :         PG_RETURN_NULL();
    2842              : 
    2843          256 :     PG_RETURN_POINT_P(result);
    2844              : }
    2845              : 
    2846              : 
    2847              : /*
    2848              :  * Closest point on line segment to specified point.
    2849              :  *
    2850              :  * If *result is not NULL, set it to the closest point on the line segment
    2851              :  * to the point.  Returns the distance of the two points.
    2852              :  */
    2853              : static float8
    2854       604064 : lseg_closept_point(Point *result, LSEG *lseg, Point *pt)
    2855              : {
    2856              :     Point       closept;
    2857              :     LINE        tmp;
    2858              : 
    2859              :     /*
    2860              :      * To find the closest point, we draw a perpendicular line from the point
    2861              :      * to the line segment.
    2862              :      */
    2863       604064 :     line_construct(&tmp, pt, point_invsl(&lseg->p[0], &lseg->p[1]));
    2864       604064 :     lseg_closept_line(&closept, lseg, &tmp);
    2865              : 
    2866       604064 :     if (result != NULL)
    2867       317444 :         *result = closept;
    2868              : 
    2869       604064 :     return point_dt(&closept, pt, NULL);
    2870              : }
    2871              : 
    2872              : Datum
    2873          320 : close_ps(PG_FUNCTION_ARGS)
    2874              : {
    2875          320 :     Point      *pt = PG_GETARG_POINT_P(0);
    2876          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(1);
    2877              :     Point      *result;
    2878              : 
    2879          320 :     result = palloc_object(Point);
    2880              : 
    2881          320 :     if (isnan(lseg_closept_point(result, lseg, pt)))
    2882           64 :         PG_RETURN_NULL();
    2883              : 
    2884          256 :     PG_RETURN_POINT_P(result);
    2885              : }
    2886              : 
    2887              : 
    2888              : /*
    2889              :  * Closest point on line segment to line segment
    2890              :  */
    2891              : static float8
    2892         3512 : lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
    2893              : {
    2894              :     Point       point;
    2895              :     float8      dist,
    2896              :                 d;
    2897              : 
    2898              :     /* First, we handle the case when the line segments are intersecting. */
    2899         3512 :     if (lseg_interpt_lseg(result, on_lseg, to_lseg))
    2900           16 :         return 0.0;
    2901              : 
    2902              :     /*
    2903              :      * Then, we find the closest points from the endpoints of the second line
    2904              :      * segment, and keep the closest one.
    2905              :      */
    2906         3496 :     dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]);
    2907         3496 :     d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]);
    2908         3496 :     if (float8_lt(d, dist))
    2909              :     {
    2910         1164 :         dist = d;
    2911         1164 :         if (result != NULL)
    2912          544 :             *result = point;
    2913              :     }
    2914              : 
    2915              :     /* The closest point can still be one of the endpoints, so we test them. */
    2916         3496 :     d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]);
    2917         3496 :     if (float8_lt(d, dist))
    2918              :     {
    2919          768 :         dist = d;
    2920          768 :         if (result != NULL)
    2921          496 :             *result = on_lseg->p[0];
    2922              :     }
    2923         3496 :     d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]);
    2924         3496 :     if (float8_lt(d, dist))
    2925              :     {
    2926          296 :         dist = d;
    2927          296 :         if (result != NULL)
    2928          168 :             *result = on_lseg->p[1];
    2929              :     }
    2930              : 
    2931         3496 :     return dist;
    2932              : }
    2933              : 
    2934              : Datum
    2935          256 : close_lseg(PG_FUNCTION_ARGS)
    2936              : {
    2937          256 :     LSEG       *l1 = PG_GETARG_LSEG_P(0);
    2938          256 :     LSEG       *l2 = PG_GETARG_LSEG_P(1);
    2939              :     Point      *result;
    2940              : 
    2941          256 :     if (lseg_sl(l1) == lseg_sl(l2))
    2942           52 :         PG_RETURN_NULL();
    2943              : 
    2944          204 :     result = palloc_object(Point);
    2945              : 
    2946          204 :     if (isnan(lseg_closept_lseg(result, l2, l1)))
    2947           60 :         PG_RETURN_NULL();
    2948              : 
    2949          144 :     PG_RETURN_POINT_P(result);
    2950              : }
    2951              : 
    2952              : 
    2953              : /*
    2954              :  * Closest point on or in box to specified point.
    2955              :  *
    2956              :  * If *result is not NULL, set it to the closest point on the box to the
    2957              :  * given point, and return the distance of the two points.
    2958              :  */
    2959              : static float8
    2960       103992 : box_closept_point(Point *result, BOX *box, Point *pt)
    2961              : {
    2962              :     float8      dist,
    2963              :                 d;
    2964              :     Point       point,
    2965              :                 closept;
    2966              :     LSEG        lseg;
    2967              : 
    2968       103992 :     if (box_contain_point(box, pt))
    2969              :     {
    2970           28 :         if (result != NULL)
    2971            4 :             *result = *pt;
    2972              : 
    2973           28 :         return 0.0;
    2974              :     }
    2975              : 
    2976              :     /* pairwise check lseg distances */
    2977       103964 :     point.x = box->low.x;
    2978       103964 :     point.y = box->high.y;
    2979       103964 :     statlseg_construct(&lseg, &box->low, &point);
    2980       103964 :     dist = lseg_closept_point(result, &lseg, pt);
    2981              : 
    2982       103964 :     statlseg_construct(&lseg, &box->high, &point);
    2983       103964 :     d = lseg_closept_point(&closept, &lseg, pt);
    2984       103964 :     if (float8_lt(d, dist))
    2985              :     {
    2986         4956 :         dist = d;
    2987         4956 :         if (result != NULL)
    2988           36 :             *result = closept;
    2989              :     }
    2990              : 
    2991       103964 :     point.x = box->high.x;
    2992       103964 :     point.y = box->low.y;
    2993       103964 :     statlseg_construct(&lseg, &box->low, &point);
    2994       103964 :     d = lseg_closept_point(&closept, &lseg, pt);
    2995       103964 :     if (float8_lt(d, dist))
    2996              :     {
    2997         5292 :         dist = d;
    2998         5292 :         if (result != NULL)
    2999            4 :             *result = closept;
    3000              :     }
    3001              : 
    3002       103964 :     statlseg_construct(&lseg, &box->high, &point);
    3003       103964 :     d = lseg_closept_point(&closept, &lseg, pt);
    3004       103964 :     if (float8_lt(d, dist))
    3005              :     {
    3006           24 :         dist = d;
    3007           24 :         if (result != NULL)
    3008            8 :             *result = closept;
    3009              :     }
    3010              : 
    3011       103964 :     return dist;
    3012              : }
    3013              : 
    3014              : Datum
    3015          200 : close_pb(PG_FUNCTION_ARGS)
    3016              : {
    3017          200 :     Point      *pt = PG_GETARG_POINT_P(0);
    3018          200 :     BOX        *box = PG_GETARG_BOX_P(1);
    3019              :     Point      *result;
    3020              : 
    3021          200 :     result = palloc_object(Point);
    3022              : 
    3023          200 :     if (isnan(box_closept_point(result, box, pt)))
    3024           20 :         PG_RETURN_NULL();
    3025              : 
    3026          180 :     PG_RETURN_POINT_P(result);
    3027              : }
    3028              : 
    3029              : /*
    3030              :  * Closest point on line segment to line.
    3031              :  *
    3032              :  * Return the distance between the line and the closest point of the line
    3033              :  * segment to the line.  If *result is not NULL, set it to that point.
    3034              :  *
    3035              :  * NOTE: When the lines are parallel, endpoints of one of the line segment
    3036              :  * are FPeq(), in presence of NaN or Infinite coordinates, or perhaps =
    3037              :  * even because of simple roundoff issues, there may not be a single closest
    3038              :  * point.  We are likely to set the result to the second endpoint in these
    3039              :  * cases.
    3040              :  */
    3041              : static float8
    3042       604988 : lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
    3043              : {
    3044              :     float8      dist1,
    3045              :                 dist2;
    3046              : 
    3047       604988 :     if (lseg_interpt_line(result, lseg, line))
    3048         7868 :         return 0.0;
    3049              : 
    3050       597120 :     dist1 = line_closept_point(NULL, line, &lseg->p[0]);
    3051       597120 :     dist2 = line_closept_point(NULL, line, &lseg->p[1]);
    3052              : 
    3053       597120 :     if (dist1 < dist2)
    3054              :     {
    3055       317140 :         if (result != NULL)
    3056       317004 :             *result = lseg->p[0];
    3057              : 
    3058       317140 :         return dist1;
    3059              :     }
    3060              :     else
    3061              :     {
    3062       279980 :         if (result != NULL)
    3063       279612 :             *result = lseg->p[1];
    3064              : 
    3065       279980 :         return dist2;
    3066              :     }
    3067              : }
    3068              : 
    3069              : Datum
    3070          320 : close_ls(PG_FUNCTION_ARGS)
    3071              : {
    3072          320 :     LINE       *line = PG_GETARG_LINE_P(0);
    3073          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(1);
    3074              :     Point      *result;
    3075              : 
    3076          320 :     if (lseg_sl(lseg) == line_sl(line))
    3077           36 :         PG_RETURN_NULL();
    3078              : 
    3079          284 :     result = palloc_object(Point);
    3080              : 
    3081          284 :     if (isnan(lseg_closept_line(result, lseg, line)))
    3082           96 :         PG_RETURN_NULL();
    3083              : 
    3084          188 :     PG_RETURN_POINT_P(result);
    3085              : }
    3086              : 
    3087              : 
    3088              : /*
    3089              :  * Closest point on or in box to line segment.
    3090              :  *
    3091              :  * Returns the distance between the closest point on or in the box to
    3092              :  * the line segment.  If *result is not NULL, it is set to that point.
    3093              :  */
    3094              : static float8
    3095          480 : box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
    3096              : {
    3097              :     float8      dist,
    3098              :                 d;
    3099              :     Point       point,
    3100              :                 closept;
    3101              :     LSEG        bseg;
    3102              : 
    3103          480 :     if (box_interpt_lseg(result, box, lseg))
    3104           96 :         return 0.0;
    3105              : 
    3106              :     /* pairwise check lseg distances */
    3107          384 :     point.x = box->low.x;
    3108          384 :     point.y = box->high.y;
    3109          384 :     statlseg_construct(&bseg, &box->low, &point);
    3110          384 :     dist = lseg_closept_lseg(result, &bseg, lseg);
    3111              : 
    3112          384 :     statlseg_construct(&bseg, &box->high, &point);
    3113          384 :     d = lseg_closept_lseg(&closept, &bseg, lseg);
    3114          384 :     if (float8_lt(d, dist))
    3115              :     {
    3116           96 :         dist = d;
    3117           96 :         if (result != NULL)
    3118           32 :             *result = closept;
    3119              :     }
    3120              : 
    3121          384 :     point.x = box->high.x;
    3122          384 :     point.y = box->low.y;
    3123          384 :     statlseg_construct(&bseg, &box->low, &point);
    3124          384 :     d = lseg_closept_lseg(&closept, &bseg, lseg);
    3125          384 :     if (float8_lt(d, dist))
    3126              :     {
    3127           12 :         dist = d;
    3128           12 :         if (result != NULL)
    3129            4 :             *result = closept;
    3130              :     }
    3131              : 
    3132          384 :     statlseg_construct(&bseg, &box->high, &point);
    3133          384 :     d = lseg_closept_lseg(&closept, &bseg, lseg);
    3134          384 :     if (float8_lt(d, dist))
    3135              :     {
    3136           12 :         dist = d;
    3137           12 :         if (result != NULL)
    3138            4 :             *result = closept;
    3139              :     }
    3140              : 
    3141          384 :     return dist;
    3142              : }
    3143              : 
    3144              : Datum
    3145          160 : close_sb(PG_FUNCTION_ARGS)
    3146              : {
    3147          160 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    3148          160 :     BOX        *box = PG_GETARG_BOX_P(1);
    3149              :     Point      *result;
    3150              : 
    3151          160 :     result = palloc_object(Point);
    3152              : 
    3153          160 :     if (isnan(box_closept_lseg(result, box, lseg)))
    3154           20 :         PG_RETURN_NULL();
    3155              : 
    3156          140 :     PG_RETURN_POINT_P(result);
    3157              : }
    3158              : 
    3159              : 
    3160              : /*---------------------------------------------------------------------
    3161              :  *      on_
    3162              :  *              Whether one object lies completely within another.
    3163              :  *-------------------------------------------------------------------*/
    3164              : 
    3165              : /*
    3166              :  *      Does the point satisfy the equation?
    3167              :  */
    3168              : static bool
    3169          736 : line_contain_point(LINE *line, Point *point)
    3170              : {
    3171          736 :     return FPzero(float8_pl(float8_pl(float8_mul(line->A, point->x),
    3172              :                                       float8_mul(line->B, point->y)),
    3173              :                             line->C));
    3174              : }
    3175              : 
    3176              : Datum
    3177          400 : on_pl(PG_FUNCTION_ARGS)
    3178              : {
    3179          400 :     Point      *pt = PG_GETARG_POINT_P(0);
    3180          400 :     LINE       *line = PG_GETARG_LINE_P(1);
    3181              : 
    3182          400 :     PG_RETURN_BOOL(line_contain_point(line, pt));
    3183              : }
    3184              : 
    3185              : 
    3186              : /*
    3187              :  *      Determine colinearity by detecting a triangle inequality.
    3188              :  * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
    3189              :  */
    3190              : static bool
    3191      2676572 : lseg_contain_point(LSEG *lseg, Point *pt)
    3192              : {
    3193      5353144 :     return FPeq(point_dt(pt, &lseg->p[0], NULL) +
    3194      2676572 :                 point_dt(pt, &lseg->p[1], NULL),
    3195              :                 point_dt(&lseg->p[0], &lseg->p[1], NULL));
    3196              : }
    3197              : 
    3198              : Datum
    3199          320 : on_ps(PG_FUNCTION_ARGS)
    3200              : {
    3201          320 :     Point      *pt = PG_GETARG_POINT_P(0);
    3202          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(1);
    3203              : 
    3204          320 :     PG_RETURN_BOOL(lseg_contain_point(lseg, pt));
    3205              : }
    3206              : 
    3207              : 
    3208              : /*
    3209              :  * Check whether the point is in the box or on its border
    3210              :  */
    3211              : static bool
    3212       291856 : box_contain_point(BOX *box, Point *point)
    3213              : {
    3214       169188 :     return box->high.x >= point->x && box->low.x <= point->x &&
    3215       461044 :         box->high.y >= point->y && box->low.y <= point->y;
    3216              : }
    3217              : 
    3218              : Datum
    3219        92168 : on_pb(PG_FUNCTION_ARGS)
    3220              : {
    3221        92168 :     Point      *pt = PG_GETARG_POINT_P(0);
    3222        92168 :     BOX        *box = PG_GETARG_BOX_P(1);
    3223              : 
    3224        92168 :     PG_RETURN_BOOL(box_contain_point(box, pt));
    3225              : }
    3226              : 
    3227              : Datum
    3228        95124 : box_contain_pt(PG_FUNCTION_ARGS)
    3229              : {
    3230        95124 :     BOX        *box = PG_GETARG_BOX_P(0);
    3231        95124 :     Point      *pt = PG_GETARG_POINT_P(1);
    3232              : 
    3233        95124 :     PG_RETURN_BOOL(box_contain_point(box, pt));
    3234              : }
    3235              : 
    3236              : /*
    3237              :  * on_ppath -
    3238              :  *      Whether a point lies within (on) a polyline.
    3239              :  *      If open, we have to (groan) check each segment.
    3240              :  * (uses same algorithm as for point intersecting segment - tgl 1997-07-09)
    3241              :  *      If closed, we use the old O(n) ray method for point-in-polygon.
    3242              :  *              The ray is horizontal, from pt out to the right.
    3243              :  *              Each segment that crosses the ray counts as an
    3244              :  *              intersection; note that an endpoint or edge may touch
    3245              :  *              but not cross.
    3246              :  *              (we can do p-in-p in lg(n), but it takes preprocessing)
    3247              :  */
    3248              : Datum
    3249          400 : on_ppath(PG_FUNCTION_ARGS)
    3250              : {
    3251          400 :     Point      *pt = PG_GETARG_POINT_P(0);
    3252          400 :     PATH       *path = PG_GETARG_PATH_P(1);
    3253              :     int         i,
    3254              :                 n;
    3255              :     float8      a,
    3256              :                 b;
    3257              : 
    3258              :     /*-- OPEN --*/
    3259          400 :     if (!path->closed)
    3260              :     {
    3261          200 :         n = path->npts - 1;
    3262          200 :         a = point_dt(pt, &path->p[0], NULL);
    3263          472 :         for (i = 0; i < n; i++)
    3264              :         {
    3265          292 :             b = point_dt(pt, &path->p[i + 1], NULL);
    3266          292 :             if (FPeq(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1], NULL)))
    3267           20 :                 PG_RETURN_BOOL(true);
    3268          272 :             a = b;
    3269              :         }
    3270          180 :         PG_RETURN_BOOL(false);
    3271              :     }
    3272              : 
    3273              :     /*-- CLOSED --*/
    3274          200 :     PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
    3275              : }
    3276              : 
    3277              : 
    3278              : /*
    3279              :  * Check whether the line segment is on the line or close enough
    3280              :  *
    3281              :  * It is, if both of its points are on the line or close enough.
    3282              :  */
    3283              : Datum
    3284          320 : on_sl(PG_FUNCTION_ARGS)
    3285              : {
    3286          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    3287          320 :     LINE       *line = PG_GETARG_LINE_P(1);
    3288              : 
    3289          320 :     PG_RETURN_BOOL(line_contain_point(line, &lseg->p[0]) &&
    3290              :                    line_contain_point(line, &lseg->p[1]));
    3291              : }
    3292              : 
    3293              : 
    3294              : /*
    3295              :  * Check whether the line segment is in the box or on its border
    3296              :  *
    3297              :  * It is, if both of its points are in the box or on its border.
    3298              :  */
    3299              : static bool
    3300          160 : box_contain_lseg(BOX *box, LSEG *lseg)
    3301              : {
    3302          172 :     return box_contain_point(box, &lseg->p[0]) &&
    3303           12 :         box_contain_point(box, &lseg->p[1]);
    3304              : }
    3305              : 
    3306              : Datum
    3307          160 : on_sb(PG_FUNCTION_ARGS)
    3308              : {
    3309          160 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    3310          160 :     BOX        *box = PG_GETARG_BOX_P(1);
    3311              : 
    3312          160 :     PG_RETURN_BOOL(box_contain_lseg(box, lseg));
    3313              : }
    3314              : 
    3315              : /*---------------------------------------------------------------------
    3316              :  *      inter_
    3317              :  *              Whether one object intersects another.
    3318              :  *-------------------------------------------------------------------*/
    3319              : 
    3320              : Datum
    3321          320 : inter_sl(PG_FUNCTION_ARGS)
    3322              : {
    3323          320 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    3324          320 :     LINE       *line = PG_GETARG_LINE_P(1);
    3325              : 
    3326          320 :     PG_RETURN_BOOL(lseg_interpt_line(NULL, lseg, line));
    3327              : }
    3328              : 
    3329              : 
    3330              : /*
    3331              :  * Do line segment and box intersect?
    3332              :  *
    3333              :  * Segment completely inside box counts as intersection.
    3334              :  * If you want only segments crossing box boundaries,
    3335              :  *  try converting box to path first.
    3336              :  *
    3337              :  * This function also sets the *result to the closest point on the line
    3338              :  * segment to the center of the box when they overlap and the result is
    3339              :  * not NULL.  It is somewhat arbitrary, but maybe the best we can do as
    3340              :  * there are typically two points they intersect.
    3341              :  *
    3342              :  * Optimize for non-intersection by checking for box intersection first.
    3343              :  * - thomas 1998-01-30
    3344              :  */
    3345              : static bool
    3346          640 : box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
    3347              : {
    3348              :     BOX         lbox;
    3349              :     LSEG        bseg;
    3350              :     Point       point;
    3351              : 
    3352          640 :     lbox.low.x = float8_min(lseg->p[0].x, lseg->p[1].x);
    3353          640 :     lbox.low.y = float8_min(lseg->p[0].y, lseg->p[1].y);
    3354          640 :     lbox.high.x = float8_max(lseg->p[0].x, lseg->p[1].x);
    3355          640 :     lbox.high.y = float8_max(lseg->p[0].y, lseg->p[1].y);
    3356              : 
    3357              :     /* nothing close to overlap? then not going to intersect */
    3358          640 :     if (!box_ov(&lbox, box))
    3359          416 :         return false;
    3360              : 
    3361          224 :     if (result != NULL)
    3362              :     {
    3363           56 :         box_cn(&point, box, NULL);
    3364           56 :         lseg_closept_point(result, lseg, &point);
    3365              :     }
    3366              : 
    3367              :     /* an endpoint of segment is inside box? then clearly intersects */
    3368          400 :     if (box_contain_point(box, &lseg->p[0]) ||
    3369          176 :         box_contain_point(box, &lseg->p[1]))
    3370           64 :         return true;
    3371              : 
    3372              :     /* pairwise check lseg intersections */
    3373          160 :     point.x = box->low.x;
    3374          160 :     point.y = box->high.y;
    3375          160 :     statlseg_construct(&bseg, &box->low, &point);
    3376          160 :     if (lseg_interpt_lseg(NULL, &bseg, lseg))
    3377           64 :         return true;
    3378              : 
    3379           96 :     statlseg_construct(&bseg, &box->high, &point);
    3380           96 :     if (lseg_interpt_lseg(NULL, &bseg, lseg))
    3381            0 :         return true;
    3382              : 
    3383           96 :     point.x = box->high.x;
    3384           96 :     point.y = box->low.y;
    3385           96 :     statlseg_construct(&bseg, &box->low, &point);
    3386           96 :     if (lseg_interpt_lseg(NULL, &bseg, lseg))
    3387            0 :         return true;
    3388              : 
    3389           96 :     statlseg_construct(&bseg, &box->high, &point);
    3390           96 :     if (lseg_interpt_lseg(NULL, &bseg, lseg))
    3391            0 :         return true;
    3392              : 
    3393              :     /* if we dropped through, no two segs intersected */
    3394           96 :     return false;
    3395              : }
    3396              : 
    3397              : Datum
    3398          160 : inter_sb(PG_FUNCTION_ARGS)
    3399              : {
    3400          160 :     LSEG       *lseg = PG_GETARG_LSEG_P(0);
    3401          160 :     BOX        *box = PG_GETARG_BOX_P(1);
    3402              : 
    3403          160 :     PG_RETURN_BOOL(box_interpt_lseg(NULL, box, lseg));
    3404              : }
    3405              : 
    3406              : 
    3407              : /*
    3408              :  * inter_lb()
    3409              :  * Do line and box intersect?
    3410              :  */
    3411              : Datum
    3412          200 : inter_lb(PG_FUNCTION_ARGS)
    3413              : {
    3414          200 :     LINE       *line = PG_GETARG_LINE_P(0);
    3415          200 :     BOX        *box = PG_GETARG_BOX_P(1);
    3416              :     LSEG        bseg;
    3417              :     Point       p1,
    3418              :                 p2;
    3419              : 
    3420              :     /* pairwise check lseg intersections */
    3421          200 :     p1.x = box->low.x;
    3422          200 :     p1.y = box->low.y;
    3423          200 :     p2.x = box->low.x;
    3424          200 :     p2.y = box->high.y;
    3425          200 :     statlseg_construct(&bseg, &p1, &p2);
    3426          200 :     if (lseg_interpt_line(NULL, &bseg, line))
    3427           44 :         PG_RETURN_BOOL(true);
    3428          156 :     p1.x = box->high.x;
    3429          156 :     p1.y = box->high.y;
    3430          156 :     statlseg_construct(&bseg, &p1, &p2);
    3431          156 :     if (lseg_interpt_line(NULL, &bseg, line))
    3432            8 :         PG_RETURN_BOOL(true);
    3433          148 :     p2.x = box->high.x;
    3434          148 :     p2.y = box->low.y;
    3435          148 :     statlseg_construct(&bseg, &p1, &p2);
    3436          148 :     if (lseg_interpt_line(NULL, &bseg, line))
    3437            0 :         PG_RETURN_BOOL(true);
    3438          148 :     p1.x = box->low.x;
    3439          148 :     p1.y = box->low.y;
    3440          148 :     statlseg_construct(&bseg, &p1, &p2);
    3441          148 :     if (lseg_interpt_line(NULL, &bseg, line))
    3442            0 :         PG_RETURN_BOOL(true);
    3443              : 
    3444              :     /* if we dropped through, no intersection */
    3445          148 :     PG_RETURN_BOOL(false);
    3446              : }
    3447              : 
    3448              : /*------------------------------------------------------------------
    3449              :  * The following routines define a data type and operator class for
    3450              :  * POLYGONS .... Part of which (the polygon's bounding box) is built on
    3451              :  * top of the BOX data type.
    3452              :  *
    3453              :  * make_bound_box - create the bounding box for the input polygon
    3454              :  *------------------------------------------------------------------*/
    3455              : 
    3456              : /*---------------------------------------------------------------------
    3457              :  * Make the smallest bounding box for the given polygon.
    3458              :  *---------------------------------------------------------------------*/
    3459              : static void
    3460        40338 : make_bound_box(POLYGON *poly)
    3461              : {
    3462              :     int         i;
    3463              :     float8      x1,
    3464              :                 y1,
    3465              :                 x2,
    3466              :                 y2;
    3467              : 
    3468              :     Assert(poly->npts > 0);
    3469              : 
    3470        40338 :     x1 = x2 = poly->p[0].x;
    3471        40338 :     y2 = y1 = poly->p[0].y;
    3472       481378 :     for (i = 1; i < poly->npts; i++)
    3473              :     {
    3474       441040 :         if (float8_lt(poly->p[i].x, x1))
    3475           44 :             x1 = poly->p[i].x;
    3476       441040 :         if (float8_gt(poly->p[i].x, x2))
    3477       240707 :             x2 = poly->p[i].x;
    3478       441040 :         if (float8_lt(poly->p[i].y, y1))
    3479       120267 :             y1 = poly->p[i].y;
    3480       441040 :         if (float8_gt(poly->p[i].y, y2))
    3481       120404 :             y2 = poly->p[i].y;
    3482              :     }
    3483              : 
    3484        40338 :     poly->boundbox.low.x = x1;
    3485        40338 :     poly->boundbox.high.x = x2;
    3486        40338 :     poly->boundbox.low.y = y1;
    3487        40338 :     poly->boundbox.high.y = y2;
    3488        40338 : }
    3489              : 
    3490              : /*------------------------------------------------------------------
    3491              :  * poly_in - read in the polygon from a string specification
    3492              :  *
    3493              :  *      External format:
    3494              :  *              "((x0,y0),...,(xn,yn))"
    3495              :  *              "x0,y0,...,xn,yn"
    3496              :  *              also supports the older style "(x1,...,xn,y1,...yn)"
    3497              :  *------------------------------------------------------------------*/
    3498              : Datum
    3499          269 : poly_in(PG_FUNCTION_ARGS)
    3500              : {
    3501          269 :     char       *str = PG_GETARG_CSTRING(0);
    3502          269 :     Node       *escontext = fcinfo->context;
    3503              :     POLYGON    *poly;
    3504              :     int         npts;
    3505              :     int         size;
    3506              :     int         base_size;
    3507              :     bool        isopen;
    3508              : 
    3509          269 :     if ((npts = pair_count(str, ',')) <= 0)
    3510           24 :         ereturn(escontext, (Datum) 0,
    3511              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    3512              :                  errmsg("invalid input syntax for type %s: \"%s\"",
    3513              :                         "polygon", str)));
    3514              : 
    3515          245 :     base_size = sizeof(poly->p[0]) * npts;
    3516          245 :     size = offsetof(POLYGON, p) + base_size;
    3517              : 
    3518              :     /* Check for integer overflow */
    3519          245 :     if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
    3520            0 :         ereturn(escontext, (Datum) 0,
    3521              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3522              :                  errmsg("too many points requested")));
    3523              : 
    3524          245 :     poly = (POLYGON *) palloc0(size);   /* zero any holes */
    3525              : 
    3526          245 :     SET_VARSIZE(poly, size);
    3527          245 :     poly->npts = npts;
    3528              : 
    3529          245 :     if (!path_decode(str, false, npts, &(poly->p[0]), &isopen, NULL, "polygon",
    3530              :                      str, escontext))
    3531            8 :         PG_RETURN_NULL();
    3532              : 
    3533          233 :     make_bound_box(poly);
    3534              : 
    3535          233 :     PG_RETURN_POLYGON_P(poly);
    3536              : }
    3537              : 
    3538              : /*---------------------------------------------------------------
    3539              :  * poly_out - convert internal POLYGON representation to the
    3540              :  *            character string format "((f8,f8),...,(f8,f8))"
    3541              :  *---------------------------------------------------------------*/
    3542              : Datum
    3543        47755 : poly_out(PG_FUNCTION_ARGS)
    3544              : {
    3545        47755 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    3546              : 
    3547        47755 :     PG_RETURN_CSTRING(path_encode(PATH_CLOSED, poly->npts, poly->p));
    3548              : }
    3549              : 
    3550              : /*
    3551              :  *      poly_recv           - converts external binary format to polygon
    3552              :  *
    3553              :  * External representation is int32 number of points, and the points.
    3554              :  * We recompute the bounding box on read, instead of trusting it to
    3555              :  * be valid.  (Checking it would take just as long, so may as well
    3556              :  * omit it from external representation.)
    3557              :  */
    3558              : Datum
    3559            0 : poly_recv(PG_FUNCTION_ARGS)
    3560              : {
    3561            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    3562              :     POLYGON    *poly;
    3563              :     int32       npts;
    3564              :     int32       i;
    3565              :     int         size;
    3566              : 
    3567            0 :     npts = pq_getmsgint(buf, sizeof(int32));
    3568            0 :     if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(POLYGON, p)) / sizeof(Point)))
    3569            0 :         ereport(ERROR,
    3570              :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    3571              :                  errmsg("invalid number of points in external \"polygon\" value")));
    3572              : 
    3573            0 :     size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * npts;
    3574            0 :     poly = (POLYGON *) palloc0(size);   /* zero any holes */
    3575              : 
    3576            0 :     SET_VARSIZE(poly, size);
    3577            0 :     poly->npts = npts;
    3578              : 
    3579            0 :     for (i = 0; i < npts; i++)
    3580              :     {
    3581            0 :         poly->p[i].x = pq_getmsgfloat8(buf);
    3582            0 :         poly->p[i].y = pq_getmsgfloat8(buf);
    3583              :     }
    3584              : 
    3585            0 :     make_bound_box(poly);
    3586              : 
    3587            0 :     PG_RETURN_POLYGON_P(poly);
    3588              : }
    3589              : 
    3590              : /*
    3591              :  *      poly_send           - converts polygon to binary format
    3592              :  */
    3593              : Datum
    3594            0 : poly_send(PG_FUNCTION_ARGS)
    3595              : {
    3596            0 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    3597              :     StringInfoData buf;
    3598              :     int32       i;
    3599              : 
    3600            0 :     pq_begintypsend(&buf);
    3601            0 :     pq_sendint32(&buf, poly->npts);
    3602            0 :     for (i = 0; i < poly->npts; i++)
    3603              :     {
    3604            0 :         pq_sendfloat8(&buf, poly->p[i].x);
    3605            0 :         pq_sendfloat8(&buf, poly->p[i].y);
    3606              :     }
    3607            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    3608              : }
    3609              : 
    3610              : 
    3611              : /*-------------------------------------------------------
    3612              :  * Is polygon A strictly left of polygon B? i.e. is
    3613              :  * the right most point of A left of the left most point
    3614              :  * of B?
    3615              :  *-------------------------------------------------------*/
    3616              : Datum
    3617          196 : poly_left(PG_FUNCTION_ARGS)
    3618              : {
    3619          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3620          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3621              :     bool        result;
    3622              : 
    3623          196 :     result = polya->boundbox.high.x < polyb->boundbox.low.x;
    3624              : 
    3625              :     /*
    3626              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3627              :      */
    3628          196 :     PG_FREE_IF_COPY(polya, 0);
    3629          196 :     PG_FREE_IF_COPY(polyb, 1);
    3630              : 
    3631          196 :     PG_RETURN_BOOL(result);
    3632              : }
    3633              : 
    3634              : /*-------------------------------------------------------
    3635              :  * Is polygon A overlapping or left of polygon B? i.e. is
    3636              :  * the right most point of A at or left of the right most point
    3637              :  * of B?
    3638              :  *-------------------------------------------------------*/
    3639              : Datum
    3640          196 : poly_overleft(PG_FUNCTION_ARGS)
    3641              : {
    3642          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3643          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3644              :     bool        result;
    3645              : 
    3646          196 :     result = polya->boundbox.high.x <= polyb->boundbox.high.x;
    3647              : 
    3648              :     /*
    3649              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3650              :      */
    3651          196 :     PG_FREE_IF_COPY(polya, 0);
    3652          196 :     PG_FREE_IF_COPY(polyb, 1);
    3653              : 
    3654          196 :     PG_RETURN_BOOL(result);
    3655              : }
    3656              : 
    3657              : /*-------------------------------------------------------
    3658              :  * Is polygon A strictly right of polygon B? i.e. is
    3659              :  * the left most point of A right of the right most point
    3660              :  * of B?
    3661              :  *-------------------------------------------------------*/
    3662              : Datum
    3663          196 : poly_right(PG_FUNCTION_ARGS)
    3664              : {
    3665          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3666          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3667              :     bool        result;
    3668              : 
    3669          196 :     result = polya->boundbox.low.x > polyb->boundbox.high.x;
    3670              : 
    3671              :     /*
    3672              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3673              :      */
    3674          196 :     PG_FREE_IF_COPY(polya, 0);
    3675          196 :     PG_FREE_IF_COPY(polyb, 1);
    3676              : 
    3677          196 :     PG_RETURN_BOOL(result);
    3678              : }
    3679              : 
    3680              : /*-------------------------------------------------------
    3681              :  * Is polygon A overlapping or right of polygon B? i.e. is
    3682              :  * the left most point of A at or right of the left most point
    3683              :  * of B?
    3684              :  *-------------------------------------------------------*/
    3685              : Datum
    3686          196 : poly_overright(PG_FUNCTION_ARGS)
    3687              : {
    3688          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3689          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3690              :     bool        result;
    3691              : 
    3692          196 :     result = polya->boundbox.low.x >= polyb->boundbox.low.x;
    3693              : 
    3694              :     /*
    3695              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3696              :      */
    3697          196 :     PG_FREE_IF_COPY(polya, 0);
    3698          196 :     PG_FREE_IF_COPY(polyb, 1);
    3699              : 
    3700          196 :     PG_RETURN_BOOL(result);
    3701              : }
    3702              : 
    3703              : /*-------------------------------------------------------
    3704              :  * Is polygon A strictly below polygon B? i.e. is
    3705              :  * the upper most point of A below the lower most point
    3706              :  * of B?
    3707              :  *-------------------------------------------------------*/
    3708              : Datum
    3709          196 : poly_below(PG_FUNCTION_ARGS)
    3710              : {
    3711          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3712          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3713              :     bool        result;
    3714              : 
    3715          196 :     result = polya->boundbox.high.y < polyb->boundbox.low.y;
    3716              : 
    3717              :     /*
    3718              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3719              :      */
    3720          196 :     PG_FREE_IF_COPY(polya, 0);
    3721          196 :     PG_FREE_IF_COPY(polyb, 1);
    3722              : 
    3723          196 :     PG_RETURN_BOOL(result);
    3724              : }
    3725              : 
    3726              : /*-------------------------------------------------------
    3727              :  * Is polygon A overlapping or below polygon B? i.e. is
    3728              :  * the upper most point of A at or below the upper most point
    3729              :  * of B?
    3730              :  *-------------------------------------------------------*/
    3731              : Datum
    3732          196 : poly_overbelow(PG_FUNCTION_ARGS)
    3733              : {
    3734          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3735          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3736              :     bool        result;
    3737              : 
    3738          196 :     result = polya->boundbox.high.y <= polyb->boundbox.high.y;
    3739              : 
    3740              :     /*
    3741              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3742              :      */
    3743          196 :     PG_FREE_IF_COPY(polya, 0);
    3744          196 :     PG_FREE_IF_COPY(polyb, 1);
    3745              : 
    3746          196 :     PG_RETURN_BOOL(result);
    3747              : }
    3748              : 
    3749              : /*-------------------------------------------------------
    3750              :  * Is polygon A strictly above polygon B? i.e. is
    3751              :  * the lower most point of A above the upper most point
    3752              :  * of B?
    3753              :  *-------------------------------------------------------*/
    3754              : Datum
    3755          196 : poly_above(PG_FUNCTION_ARGS)
    3756              : {
    3757          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3758          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3759              :     bool        result;
    3760              : 
    3761          196 :     result = polya->boundbox.low.y > polyb->boundbox.high.y;
    3762              : 
    3763              :     /*
    3764              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3765              :      */
    3766          196 :     PG_FREE_IF_COPY(polya, 0);
    3767          196 :     PG_FREE_IF_COPY(polyb, 1);
    3768              : 
    3769          196 :     PG_RETURN_BOOL(result);
    3770              : }
    3771              : 
    3772              : /*-------------------------------------------------------
    3773              :  * Is polygon A overlapping or above polygon B? i.e. is
    3774              :  * the lower most point of A at or above the lower most point
    3775              :  * of B?
    3776              :  *-------------------------------------------------------*/
    3777              : Datum
    3778          196 : poly_overabove(PG_FUNCTION_ARGS)
    3779              : {
    3780          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3781          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3782              :     bool        result;
    3783              : 
    3784          196 :     result = polya->boundbox.low.y >= polyb->boundbox.low.y;
    3785              : 
    3786              :     /*
    3787              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3788              :      */
    3789          196 :     PG_FREE_IF_COPY(polya, 0);
    3790          196 :     PG_FREE_IF_COPY(polyb, 1);
    3791              : 
    3792          196 :     PG_RETURN_BOOL(result);
    3793              : }
    3794              : 
    3795              : 
    3796              : /*-------------------------------------------------------
    3797              :  * Is polygon A the same as polygon B? i.e. are all the
    3798              :  * points the same?
    3799              :  * Check all points for matches in both forward and reverse
    3800              :  *  direction since polygons are non-directional and are
    3801              :  *  closed shapes.
    3802              :  *-------------------------------------------------------*/
    3803              : Datum
    3804         4197 : poly_same(PG_FUNCTION_ARGS)
    3805              : {
    3806         4197 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3807         4197 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3808              :     bool        result;
    3809              : 
    3810         4197 :     if (polya->npts != polyb->npts)
    3811          136 :         result = false;
    3812              :     else
    3813         4061 :         result = plist_same(polya->npts, polya->p, polyb->p);
    3814              : 
    3815              :     /*
    3816              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3817              :      */
    3818         4197 :     PG_FREE_IF_COPY(polya, 0);
    3819         4197 :     PG_FREE_IF_COPY(polyb, 1);
    3820              : 
    3821         4197 :     PG_RETURN_BOOL(result);
    3822              : }
    3823              : 
    3824              : /*-----------------------------------------------------------------
    3825              :  * Determine if polygon A overlaps polygon B
    3826              :  *-----------------------------------------------------------------*/
    3827              : static bool
    3828        19612 : poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
    3829              : {
    3830              :     bool        result;
    3831              : 
    3832              :     Assert(polya->npts > 0 && polyb->npts > 0);
    3833              : 
    3834              :     /* Quick check by bounding box */
    3835        19612 :     result = box_ov(&polya->boundbox, &polyb->boundbox);
    3836              : 
    3837              :     /*
    3838              :      * Brute-force algorithm - try to find intersected edges, if so then
    3839              :      * polygons are overlapped else check is one polygon inside other or not
    3840              :      * by testing single point of them.
    3841              :      */
    3842        19612 :     if (result)
    3843              :     {
    3844              :         int         ia,
    3845              :                     ib;
    3846              :         LSEG        sa,
    3847              :                     sb;
    3848              : 
    3849              :         /* Init first of polya's edge with last point */
    3850         7076 :         sa.p[0] = polya->p[polya->npts - 1];
    3851         7076 :         result = false;
    3852              : 
    3853        84364 :         for (ia = 0; ia < polya->npts && !result; ia++)
    3854              :         {
    3855              :             /* Second point of polya's edge is a current one */
    3856        77288 :             sa.p[1] = polya->p[ia];
    3857              : 
    3858              :             /* Init first of polyb's edge with last point */
    3859        77288 :             sb.p[0] = polyb->p[polyb->npts - 1];
    3860              : 
    3861       384676 :             for (ib = 0; ib < polyb->npts && !result; ib++)
    3862              :             {
    3863       307388 :                 sb.p[1] = polyb->p[ib];
    3864       307388 :                 result = lseg_interpt_lseg(NULL, &sa, &sb);
    3865       307388 :                 sb.p[0] = sb.p[1];
    3866              :             }
    3867              : 
    3868              :             /*
    3869              :              * move current endpoint to the first point of next edge
    3870              :              */
    3871        77288 :             sa.p[0] = sa.p[1];
    3872              :         }
    3873              : 
    3874         7076 :         if (!result)
    3875              :         {
    3876         9268 :             result = (point_inside(polya->p, polyb->npts, polyb->p) ||
    3877         2952 :                       point_inside(polyb->p, polya->npts, polya->p));
    3878              :         }
    3879              :     }
    3880              : 
    3881        19612 :     return result;
    3882              : }
    3883              : 
    3884              : Datum
    3885        19416 : poly_overlap(PG_FUNCTION_ARGS)
    3886              : {
    3887        19416 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    3888        19416 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    3889              :     bool        result;
    3890              : 
    3891        19416 :     result = poly_overlap_internal(polya, polyb);
    3892              : 
    3893              :     /*
    3894              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    3895              :      */
    3896        19416 :     PG_FREE_IF_COPY(polya, 0);
    3897        19416 :     PG_FREE_IF_COPY(polyb, 1);
    3898              : 
    3899        19416 :     PG_RETURN_BOOL(result);
    3900              : }
    3901              : 
    3902              : /*
    3903              :  * Tests special kind of segment for in/out of polygon.
    3904              :  * Special kind means:
    3905              :  *  - point a should be on segment s
    3906              :  *  - segment (a,b) should not be contained by s
    3907              :  * Returns true if:
    3908              :  *  - segment (a,b) is collinear to s and (a,b) is in polygon
    3909              :  *  - segment (a,b) s not collinear to s. Note: that doesn't
    3910              :  *    mean that segment is in polygon!
    3911              :  */
    3912              : 
    3913              : static bool
    3914          404 : touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
    3915              : {
    3916              :     /* point a is on s, b is not */
    3917              :     LSEG        t;
    3918              : 
    3919          404 :     t.p[0] = *a;
    3920          404 :     t.p[1] = *b;
    3921              : 
    3922          404 :     if (point_eq_point(a, s->p))
    3923              :     {
    3924           48 :         if (lseg_contain_point(&t, s->p + 1))
    3925            0 :             return lseg_inside_poly(b, s->p + 1, poly, start);
    3926              :     }
    3927          356 :     else if (point_eq_point(a, s->p + 1))
    3928              :     {
    3929          104 :         if (lseg_contain_point(&t, s->p))
    3930            0 :             return lseg_inside_poly(b, s->p, poly, start);
    3931              :     }
    3932          252 :     else if (lseg_contain_point(&t, s->p))
    3933              :     {
    3934            0 :         return lseg_inside_poly(b, s->p, poly, start);
    3935              :     }
    3936          252 :     else if (lseg_contain_point(&t, s->p + 1))
    3937              :     {
    3938            0 :         return lseg_inside_poly(b, s->p + 1, poly, start);
    3939              :     }
    3940              : 
    3941          404 :     return true;                /* may be not true, but that will check later */
    3942              : }
    3943              : 
    3944              : /*
    3945              :  * Returns true if segment (a,b) is in polygon, option
    3946              :  * start is used for optimization - function checks
    3947              :  * polygon's edges starting from start
    3948              :  */
    3949              : static bool
    3950       132184 : lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
    3951              : {
    3952              :     LSEG        s,
    3953              :                 t;
    3954              :     int         i;
    3955       132184 :     bool        res = true,
    3956       132184 :                 intersection = false;
    3957              : 
    3958              :     /* since this function recurses, it could be driven to stack overflow */
    3959       132184 :     check_stack_depth();
    3960              : 
    3961       132184 :     t.p[0] = *a;
    3962       132184 :     t.p[1] = *b;
    3963       132184 :     s.p[0] = poly->p[(start == 0) ? (poly->npts - 1) : (start - 1)];
    3964              : 
    3965       653796 :     for (i = start; i < poly->npts && res; i++)
    3966              :     {
    3967              :         Point       interpt;
    3968              : 
    3969       521924 :         CHECK_FOR_INTERRUPTS();
    3970              : 
    3971       521924 :         s.p[1] = poly->p[i];
    3972              : 
    3973       521924 :         if (lseg_contain_point(&s, t.p))
    3974              :         {
    3975          524 :             if (lseg_contain_point(&s, t.p + 1))
    3976          312 :                 return true;    /* t is contained by s */
    3977              : 
    3978              :             /* Y-cross */
    3979          212 :             res = touched_lseg_inside_poly(t.p, t.p + 1, &s, poly, i + 1);
    3980              :         }
    3981       521400 :         else if (lseg_contain_point(&s, t.p + 1))
    3982              :         {
    3983              :             /* Y-cross */
    3984          192 :             res = touched_lseg_inside_poly(t.p + 1, t.p, &s, poly, i + 1);
    3985              :         }
    3986       521208 :         else if (lseg_interpt_lseg(&interpt, &t, &s))
    3987              :         {
    3988              :             /*
    3989              :              * segments are X-crossing, go to check each subsegment
    3990              :              */
    3991              : 
    3992          900 :             intersection = true;
    3993          900 :             res = lseg_inside_poly(t.p, &interpt, poly, i + 1);
    3994          900 :             if (res)
    3995          772 :                 res = lseg_inside_poly(t.p + 1, &interpt, poly, i + 1);
    3996              :         }
    3997              : 
    3998       521612 :         s.p[0] = s.p[1];
    3999              :     }
    4000              : 
    4001       131872 :     if (res && !intersection)
    4002              :     {
    4003              :         Point       p;
    4004              : 
    4005              :         /*
    4006              :          * if X-intersection wasn't found, then check central point of tested
    4007              :          * segment. In opposite case we already check all subsegments
    4008              :          */
    4009       130972 :         p.x = float8_div(float8_pl(t.p[0].x, t.p[1].x), 2.0);
    4010       130972 :         p.y = float8_div(float8_pl(t.p[0].y, t.p[1].y), 2.0);
    4011              : 
    4012       130972 :         res = point_inside(&p, poly->npts, poly->p);
    4013              :     }
    4014              : 
    4015       131872 :     return res;
    4016              : }
    4017              : 
    4018              : /*
    4019              :  * Check whether the first polygon contains the second
    4020              :  */
    4021              : static bool
    4022        56620 : poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
    4023              : {
    4024              :     int         i;
    4025              :     LSEG        s;
    4026              : 
    4027              :     Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
    4028              : 
    4029              :     /*
    4030              :      * Quick check to see if contained's bounding box is contained in
    4031              :      * contains' bb.
    4032              :      */
    4033        56620 :     if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
    4034        38228 :         return false;
    4035              : 
    4036        18392 :     s.p[0] = contained_poly->p[contained_poly->npts - 1];
    4037              : 
    4038       140592 :     for (i = 0; i < contained_poly->npts; i++)
    4039              :     {
    4040       130512 :         s.p[1] = contained_poly->p[i];
    4041       130512 :         if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
    4042         8312 :             return false;
    4043       122200 :         s.p[0] = s.p[1];
    4044              :     }
    4045              : 
    4046        10080 :     return true;
    4047              : }
    4048              : 
    4049              : Datum
    4050          256 : poly_contain(PG_FUNCTION_ARGS)
    4051              : {
    4052          256 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    4053          256 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    4054              :     bool        result;
    4055              : 
    4056          256 :     result = poly_contain_poly(polya, polyb);
    4057              : 
    4058              :     /*
    4059              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    4060              :      */
    4061          256 :     PG_FREE_IF_COPY(polya, 0);
    4062          256 :     PG_FREE_IF_COPY(polyb, 1);
    4063              : 
    4064          256 :     PG_RETURN_BOOL(result);
    4065              : }
    4066              : 
    4067              : 
    4068              : /*-----------------------------------------------------------------
    4069              :  * Determine if polygon A is contained by polygon B
    4070              :  *-----------------------------------------------------------------*/
    4071              : Datum
    4072        56364 : poly_contained(PG_FUNCTION_ARGS)
    4073              : {
    4074        56364 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    4075        56364 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    4076              :     bool        result;
    4077              : 
    4078              :     /* Just switch the arguments and pass it off to poly_contain */
    4079        56364 :     result = poly_contain_poly(polyb, polya);
    4080              : 
    4081              :     /*
    4082              :      * Avoid leaking memory for toasted inputs ... needed for rtree indexes
    4083              :      */
    4084        56364 :     PG_FREE_IF_COPY(polya, 0);
    4085        56364 :     PG_FREE_IF_COPY(polyb, 1);
    4086              : 
    4087        56364 :     PG_RETURN_BOOL(result);
    4088              : }
    4089              : 
    4090              : 
    4091              : Datum
    4092          296 : poly_contain_pt(PG_FUNCTION_ARGS)
    4093              : {
    4094          296 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    4095          296 :     Point      *p = PG_GETARG_POINT_P(1);
    4096              : 
    4097          296 :     PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
    4098              : }
    4099              : 
    4100              : Datum
    4101          321 : pt_contained_poly(PG_FUNCTION_ARGS)
    4102              : {
    4103          321 :     Point      *p = PG_GETARG_POINT_P(0);
    4104          321 :     POLYGON    *poly = PG_GETARG_POLYGON_P(1);
    4105              : 
    4106          321 :     PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
    4107              : }
    4108              : 
    4109              : 
    4110              : Datum
    4111          196 : poly_distance(PG_FUNCTION_ARGS)
    4112              : {
    4113          196 :     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
    4114          196 :     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
    4115          196 :     float8      min = 0.0;      /* initialize to keep compiler quiet */
    4116          196 :     bool        have_min = false;
    4117              :     float8      tmp;
    4118              :     int         i,
    4119              :                 j;
    4120              :     LSEG        seg1,
    4121              :                 seg2;
    4122              : 
    4123              :     /*
    4124              :      * Distance is zero if polygons overlap.  We must check this because the
    4125              :      * path distance will not give the right answer if one poly is entirely
    4126              :      * within the other.
    4127              :      */
    4128          196 :     if (poly_overlap_internal(polya, polyb))
    4129          100 :         PG_RETURN_FLOAT8(0.0);
    4130              : 
    4131              :     /*
    4132              :      * When they don't overlap, the distance calculation is identical to that
    4133              :      * for closed paths (i.e., we needn't care about the fact that polygons
    4134              :      * include their contained areas).  See path_distance().
    4135              :      */
    4136          352 :     for (i = 0; i < polya->npts; i++)
    4137              :     {
    4138              :         int         iprev;
    4139              : 
    4140          256 :         if (i > 0)
    4141          160 :             iprev = i - 1;
    4142              :         else
    4143           96 :             iprev = polya->npts - 1;
    4144              : 
    4145          872 :         for (j = 0; j < polyb->npts; j++)
    4146              :         {
    4147              :             int         jprev;
    4148              : 
    4149          616 :             if (j > 0)
    4150          360 :                 jprev = j - 1;
    4151              :             else
    4152          256 :                 jprev = polyb->npts - 1;
    4153              : 
    4154          616 :             statlseg_construct(&seg1, &polya->p[iprev], &polya->p[i]);
    4155          616 :             statlseg_construct(&seg2, &polyb->p[jprev], &polyb->p[j]);
    4156              : 
    4157          616 :             tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
    4158          616 :             if (!have_min || float8_lt(tmp, min))
    4159              :             {
    4160          128 :                 min = tmp;
    4161          128 :                 have_min = true;
    4162              :             }
    4163              :         }
    4164              :     }
    4165              : 
    4166           96 :     if (!have_min)
    4167            0 :         PG_RETURN_NULL();
    4168              : 
    4169           96 :     PG_RETURN_FLOAT8(min);
    4170              : }
    4171              : 
    4172              : 
    4173              : /***********************************************************************
    4174              :  **
    4175              :  **     Routines for 2D points.
    4176              :  **
    4177              :  ***********************************************************************/
    4178              : 
    4179              : Datum
    4180       688675 : construct_point(PG_FUNCTION_ARGS)
    4181              : {
    4182       688675 :     float8      x = PG_GETARG_FLOAT8(0);
    4183       688675 :     float8      y = PG_GETARG_FLOAT8(1);
    4184              :     Point      *result;
    4185              : 
    4186       688675 :     result = palloc_object(Point);
    4187              : 
    4188       688675 :     point_construct(result, x, y);
    4189              : 
    4190       688675 :     PG_RETURN_POINT_P(result);
    4191              : }
    4192              : 
    4193              : 
    4194              : static inline void
    4195         2048 : point_add_point(Point *result, Point *pt1, Point *pt2, Node *escontext)
    4196              : {
    4197              :     float8      x;
    4198              :     float8      y;
    4199              : 
    4200         2048 :     x = float8_pl_safe(pt1->x, pt2->x, escontext);
    4201         2048 :     if (SOFT_ERROR_OCCURRED(escontext))
    4202            0 :         return;
    4203              : 
    4204         2048 :     y = float8_pl_safe(pt1->y, pt2->y, escontext);
    4205         2048 :     if (SOFT_ERROR_OCCURRED(escontext))
    4206            0 :         return;
    4207              : 
    4208         2048 :     point_construct(result, x, y);
    4209              : }
    4210              : 
    4211              : Datum
    4212          400 : point_add(PG_FUNCTION_ARGS)
    4213              : {
    4214          400 :     Point      *p1 = PG_GETARG_POINT_P(0);
    4215          400 :     Point      *p2 = PG_GETARG_POINT_P(1);
    4216              :     Point      *result;
    4217              : 
    4218          400 :     result = palloc_object(Point);
    4219              : 
    4220          400 :     point_add_point(result, p1, p2, NULL);
    4221              : 
    4222          400 :     PG_RETURN_POINT_P(result);
    4223              : }
    4224              : 
    4225              : 
    4226              : static inline void
    4227         1880 : point_sub_point(Point *result, Point *pt1, Point *pt2)
    4228              : {
    4229         1880 :     point_construct(result,
    4230              :                     float8_mi(pt1->x, pt2->x),
    4231              :                     float8_mi(pt1->y, pt2->y));
    4232         1880 : }
    4233              : 
    4234              : Datum
    4235          400 : point_sub(PG_FUNCTION_ARGS)
    4236              : {
    4237          400 :     Point      *p1 = PG_GETARG_POINT_P(0);
    4238          400 :     Point      *p2 = PG_GETARG_POINT_P(1);
    4239              :     Point      *result;
    4240              : 
    4241          400 :     result = palloc_object(Point);
    4242              : 
    4243          400 :     point_sub_point(result, p1, p2);
    4244              : 
    4245          400 :     PG_RETURN_POINT_P(result);
    4246              : }
    4247              : 
    4248              : 
    4249              : static inline void
    4250         1480 : point_mul_point(Point *result, Point *pt1, Point *pt2)
    4251              : {
    4252         1480 :     point_construct(result,
    4253              :                     float8_mi(float8_mul(pt1->x, pt2->x),
    4254              :                               float8_mul(pt1->y, pt2->y)),
    4255              :                     float8_pl(float8_mul(pt1->x, pt2->y),
    4256              :                               float8_mul(pt1->y, pt2->x)));
    4257         1476 : }
    4258              : 
    4259              : Datum
    4260          200 : point_mul(PG_FUNCTION_ARGS)
    4261              : {
    4262          200 :     Point      *p1 = PG_GETARG_POINT_P(0);
    4263          200 :     Point      *p2 = PG_GETARG_POINT_P(1);
    4264              :     Point      *result;
    4265              : 
    4266          200 :     result = palloc_object(Point);
    4267              : 
    4268          200 :     point_mul_point(result, p1, p2);
    4269              : 
    4270          196 :     PG_RETURN_POINT_P(result);
    4271              : }
    4272              : 
    4273              : 
    4274              : static inline void
    4275          396 : point_div_point(Point *result, Point *pt1, Point *pt2)
    4276              : {
    4277              :     float8      div;
    4278              : 
    4279          396 :     div = float8_pl(float8_mul(pt2->x, pt2->x), float8_mul(pt2->y, pt2->y));
    4280              : 
    4281          388 :     point_construct(result,
    4282              :                     float8_div(float8_pl(float8_mul(pt1->x, pt2->x),
    4283              :                                          float8_mul(pt1->y, pt2->y)), div),
    4284              :                     float8_div(float8_mi(float8_mul(pt1->y, pt2->x),
    4285              :                                          float8_mul(pt1->x, pt2->y)), div));
    4286          376 : }
    4287              : 
    4288              : Datum
    4289           88 : point_div(PG_FUNCTION_ARGS)
    4290              : {
    4291           88 :     Point      *p1 = PG_GETARG_POINT_P(0);
    4292           88 :     Point      *p2 = PG_GETARG_POINT_P(1);
    4293              :     Point      *result;
    4294              : 
    4295           88 :     result = palloc_object(Point);
    4296              : 
    4297           88 :     point_div_point(result, p1, p2);
    4298              : 
    4299           80 :     PG_RETURN_POINT_P(result);
    4300              : }
    4301              : 
    4302              : 
    4303              : /***********************************************************************
    4304              :  **
    4305              :  **     Routines for 2D boxes.
    4306              :  **
    4307              :  ***********************************************************************/
    4308              : 
    4309              : Datum
    4310       161124 : points_box(PG_FUNCTION_ARGS)
    4311              : {
    4312       161124 :     Point      *p1 = PG_GETARG_POINT_P(0);
    4313       161124 :     Point      *p2 = PG_GETARG_POINT_P(1);
    4314              :     BOX        *result;
    4315              : 
    4316       161124 :     result = palloc_object(BOX);
    4317              : 
    4318       161124 :     box_construct(result, p1, p2);
    4319              : 
    4320       161124 :     PG_RETURN_BOX_P(result);
    4321              : }
    4322              : 
    4323              : Datum
    4324          200 : box_add(PG_FUNCTION_ARGS)
    4325              : {
    4326          200 :     BOX        *box = PG_GETARG_BOX_P(0);
    4327          200 :     Point      *p = PG_GETARG_POINT_P(1);
    4328              :     BOX        *result;
    4329              : 
    4330          200 :     result = palloc_object(BOX);
    4331              : 
    4332          200 :     point_add_point(&result->high, &box->high, p, NULL);
    4333          200 :     point_add_point(&result->low, &box->low, p, NULL);
    4334              : 
    4335          200 :     PG_RETURN_BOX_P(result);
    4336              : }
    4337              : 
    4338              : Datum
    4339          200 : box_sub(PG_FUNCTION_ARGS)
    4340              : {
    4341          200 :     BOX        *box = PG_GETARG_BOX_P(0);
    4342          200 :     Point      *p = PG_GETARG_POINT_P(1);
    4343              :     BOX        *result;
    4344              : 
    4345          200 :     result = palloc_object(BOX);
    4346              : 
    4347          200 :     point_sub_point(&result->high, &box->high, p);
    4348          200 :     point_sub_point(&result->low, &box->low, p);
    4349              : 
    4350          200 :     PG_RETURN_BOX_P(result);
    4351              : }
    4352              : 
    4353              : Datum
    4354          100 : box_mul(PG_FUNCTION_ARGS)
    4355              : {
    4356          100 :     BOX        *box = PG_GETARG_BOX_P(0);
    4357          100 :     Point      *p = PG_GETARG_POINT_P(1);
    4358              :     BOX        *result;
    4359              :     Point       high,
    4360              :                 low;
    4361              : 
    4362          100 :     result = palloc_object(BOX);
    4363              : 
    4364          100 :     point_mul_point(&high, &box->high, p);
    4365          100 :     point_mul_point(&low, &box->low, p);
    4366              : 
    4367          100 :     box_construct(result, &high, &low);
    4368              : 
    4369          100 :     PG_RETURN_BOX_P(result);
    4370              : }
    4371              : 
    4372              : Datum
    4373           40 : box_div(PG_FUNCTION_ARGS)
    4374              : {
    4375           40 :     BOX        *box = PG_GETARG_BOX_P(0);
    4376           40 :     Point      *p = PG_GETARG_POINT_P(1);
    4377              :     BOX        *result;
    4378              :     Point       high,
    4379              :                 low;
    4380              : 
    4381           40 :     result = palloc_object(BOX);
    4382              : 
    4383           40 :     point_div_point(&high, &box->high, p);
    4384           40 :     point_div_point(&low, &box->low, p);
    4385              : 
    4386           40 :     box_construct(result, &high, &low);
    4387              : 
    4388           40 :     PG_RETURN_BOX_P(result);
    4389              : }
    4390              : 
    4391              : /*
    4392              :  * Convert point to empty box
    4393              :  */
    4394              : Datum
    4395          244 : point_box(PG_FUNCTION_ARGS)
    4396              : {
    4397          244 :     Point      *pt = PG_GETARG_POINT_P(0);
    4398              :     BOX        *box;
    4399              : 
    4400          244 :     box = palloc_object(BOX);
    4401              : 
    4402          244 :     box->high.x = pt->x;
    4403          244 :     box->low.x = pt->x;
    4404          244 :     box->high.y = pt->y;
    4405          244 :     box->low.y = pt->y;
    4406              : 
    4407          244 :     PG_RETURN_BOX_P(box);
    4408              : }
    4409              : 
    4410              : /*
    4411              :  * Smallest bounding box that includes both of the given boxes
    4412              :  */
    4413              : Datum
    4414          100 : boxes_bound_box(PG_FUNCTION_ARGS)
    4415              : {
    4416          100 :     BOX        *box1 = PG_GETARG_BOX_P(0),
    4417          100 :                *box2 = PG_GETARG_BOX_P(1),
    4418              :                *container;
    4419              : 
    4420          100 :     container = palloc_object(BOX);
    4421              : 
    4422          100 :     container->high.x = float8_max(box1->high.x, box2->high.x);
    4423          100 :     container->low.x = float8_min(box1->low.x, box2->low.x);
    4424          100 :     container->high.y = float8_max(box1->high.y, box2->high.y);
    4425          100 :     container->low.y = float8_min(box1->low.y, box2->low.y);
    4426              : 
    4427          100 :     PG_RETURN_BOX_P(container);
    4428              : }
    4429              : 
    4430              : 
    4431              : /***********************************************************************
    4432              :  **
    4433              :  **     Routines for 2D paths.
    4434              :  **
    4435              :  ***********************************************************************/
    4436              : 
    4437              : /*
    4438              :  * path_add()
    4439              :  * Concatenate two paths (only if they are both open).
    4440              :  */
    4441              : Datum
    4442          324 : path_add(PG_FUNCTION_ARGS)
    4443              : {
    4444          324 :     PATH       *p1 = PG_GETARG_PATH_P(0);
    4445          324 :     PATH       *p2 = PG_GETARG_PATH_P(1);
    4446              :     PATH       *result;
    4447              :     int         size,
    4448              :                 base_size;
    4449              :     int         i;
    4450              : 
    4451          324 :     if (p1->closed || p2->closed)
    4452          260 :         PG_RETURN_NULL();
    4453              : 
    4454           64 :     base_size = sizeof(p1->p[0]) * (p1->npts + p2->npts);
    4455           64 :     size = offsetof(PATH, p) + base_size;
    4456              : 
    4457              :     /* Check for integer overflow */
    4458           64 :     if (base_size / sizeof(p1->p[0]) != (p1->npts + p2->npts) ||
    4459              :         size <= base_size)
    4460            0 :         ereport(ERROR,
    4461              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    4462              :                  errmsg("too many points requested")));
    4463              : 
    4464           64 :     result = (PATH *) palloc(size);
    4465              : 
    4466           64 :     SET_VARSIZE(result, size);
    4467           64 :     result->npts = (p1->npts + p2->npts);
    4468           64 :     result->closed = p1->closed;
    4469              :     /* prevent instability in unused pad bytes */
    4470           64 :     result->dummy = 0;
    4471              : 
    4472          224 :     for (i = 0; i < p1->npts; i++)
    4473              :     {
    4474          160 :         result->p[i].x = p1->p[i].x;
    4475          160 :         result->p[i].y = p1->p[i].y;
    4476              :     }
    4477          224 :     for (i = 0; i < p2->npts; i++)
    4478              :     {
    4479          160 :         result->p[i + p1->npts].x = p2->p[i].x;
    4480          160 :         result->p[i + p1->npts].y = p2->p[i].y;
    4481              :     }
    4482              : 
    4483           64 :     PG_RETURN_PATH_P(result);
    4484              : }
    4485              : 
    4486              : /*
    4487              :  * path_add_pt()
    4488              :  * Translation operators.
    4489              :  */
    4490              : Datum
    4491          360 : path_add_pt(PG_FUNCTION_ARGS)
    4492              : {
    4493          360 :     PATH       *path = PG_GETARG_PATH_P_COPY(0);
    4494          360 :     Point      *point = PG_GETARG_POINT_P(1);
    4495              :     int         i;
    4496              : 
    4497         1120 :     for (i = 0; i < path->npts; i++)
    4498          760 :         point_add_point(&path->p[i], &path->p[i], point, NULL);
    4499              : 
    4500          360 :     PG_RETURN_PATH_P(path);
    4501              : }
    4502              : 
    4503              : Datum
    4504          360 : path_sub_pt(PG_FUNCTION_ARGS)
    4505              : {
    4506          360 :     PATH       *path = PG_GETARG_PATH_P_COPY(0);
    4507          360 :     Point      *point = PG_GETARG_POINT_P(1);
    4508              :     int         i;
    4509              : 
    4510         1120 :     for (i = 0; i < path->npts; i++)
    4511          760 :         point_sub_point(&path->p[i], &path->p[i], point);
    4512              : 
    4513          360 :     PG_RETURN_PATH_P(path);
    4514              : }
    4515              : 
    4516              : /*
    4517              :  * path_mul_pt()
    4518              :  * Rotation and scaling operators.
    4519              :  */
    4520              : Datum
    4521          360 : path_mul_pt(PG_FUNCTION_ARGS)
    4522              : {
    4523          360 :     PATH       *path = PG_GETARG_PATH_P_COPY(0);
    4524          360 :     Point      *point = PG_GETARG_POINT_P(1);
    4525              :     int         i;
    4526              : 
    4527         1120 :     for (i = 0; i < path->npts; i++)
    4528          760 :         point_mul_point(&path->p[i], &path->p[i], point);
    4529              : 
    4530          360 :     PG_RETURN_PATH_P(path);
    4531              : }
    4532              : 
    4533              : Datum
    4534           76 : path_div_pt(PG_FUNCTION_ARGS)
    4535              : {
    4536           76 :     PATH       *path = PG_GETARG_PATH_P_COPY(0);
    4537           76 :     Point      *point = PG_GETARG_POINT_P(1);
    4538              :     int         i;
    4539              : 
    4540          228 :     for (i = 0; i < path->npts; i++)
    4541          156 :         point_div_point(&path->p[i], &path->p[i], point);
    4542              : 
    4543           72 :     PG_RETURN_PATH_P(path);
    4544              : }
    4545              : 
    4546              : 
    4547              : Datum
    4548           60 : path_poly(PG_FUNCTION_ARGS)
    4549              : {
    4550           60 :     PATH       *path = PG_GETARG_PATH_P(0);
    4551              :     POLYGON    *poly;
    4552              :     int         size;
    4553              :     int         i;
    4554              : 
    4555              :     /* This is not very consistent --- other similar cases return NULL ... */
    4556           60 :     if (!path->closed)
    4557            4 :         ereturn(fcinfo->context, (Datum) 0,
    4558              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    4559              :                  errmsg("open path cannot be converted to polygon")));
    4560              : 
    4561              :     /*
    4562              :      * Never overflows: the old size fit in MaxAllocSize, and the new size is
    4563              :      * just a small constant larger.
    4564              :      */
    4565           56 :     size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * path->npts;
    4566           56 :     poly = (POLYGON *) palloc(size);
    4567              : 
    4568           56 :     SET_VARSIZE(poly, size);
    4569           56 :     poly->npts = path->npts;
    4570              : 
    4571          168 :     for (i = 0; i < path->npts; i++)
    4572              :     {
    4573          112 :         poly->p[i].x = path->p[i].x;
    4574          112 :         poly->p[i].y = path->p[i].y;
    4575              :     }
    4576              : 
    4577           56 :     make_bound_box(poly);
    4578              : 
    4579           56 :     PG_RETURN_POLYGON_P(poly);
    4580              : }
    4581              : 
    4582              : 
    4583              : /***********************************************************************
    4584              :  **
    4585              :  **     Routines for 2D polygons.
    4586              :  **
    4587              :  ***********************************************************************/
    4588              : 
    4589              : Datum
    4590           84 : poly_npoints(PG_FUNCTION_ARGS)
    4591              : {
    4592           84 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    4593              : 
    4594           84 :     PG_RETURN_INT32(poly->npts);
    4595              : }
    4596              : 
    4597              : 
    4598              : Datum
    4599           28 : poly_center(PG_FUNCTION_ARGS)
    4600              : {
    4601           28 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    4602              :     Point      *result;
    4603              :     CIRCLE      circle;
    4604              : 
    4605           28 :     result = palloc_object(Point);
    4606              : 
    4607           28 :     poly_to_circle(&circle, poly, fcinfo->context);
    4608           28 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    4609            0 :         PG_RETURN_NULL();
    4610              : 
    4611           28 :     *result = circle.center;
    4612              : 
    4613           28 :     PG_RETURN_POINT_P(result);
    4614              : }
    4615              : 
    4616              : 
    4617              : Datum
    4618           28 : poly_box(PG_FUNCTION_ARGS)
    4619              : {
    4620           28 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    4621              :     BOX        *box;
    4622              : 
    4623           28 :     box = palloc_object(BOX);
    4624           28 :     *box = poly->boundbox;
    4625              : 
    4626           28 :     PG_RETURN_BOX_P(box);
    4627              : }
    4628              : 
    4629              : 
    4630              : /*
    4631              :  * box_poly()
    4632              :  * Convert a box to a polygon.
    4633              :  */
    4634              : Datum
    4635        12420 : box_poly(PG_FUNCTION_ARGS)
    4636              : {
    4637        12420 :     BOX        *box = PG_GETARG_BOX_P(0);
    4638              :     POLYGON    *poly;
    4639              :     int         size;
    4640              : 
    4641              :     /* map four corners of the box to a polygon */
    4642        12420 :     size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * 4;
    4643        12420 :     poly = (POLYGON *) palloc(size);
    4644              : 
    4645        12420 :     SET_VARSIZE(poly, size);
    4646        12420 :     poly->npts = 4;
    4647              : 
    4648        12420 :     poly->p[0].x = box->low.x;
    4649        12420 :     poly->p[0].y = box->low.y;
    4650        12420 :     poly->p[1].x = box->low.x;
    4651        12420 :     poly->p[1].y = box->high.y;
    4652        12420 :     poly->p[2].x = box->high.x;
    4653        12420 :     poly->p[2].y = box->high.y;
    4654        12420 :     poly->p[3].x = box->high.x;
    4655        12420 :     poly->p[3].y = box->low.y;
    4656              : 
    4657        12420 :     box_construct(&poly->boundbox, &box->high, &box->low);
    4658              : 
    4659        12420 :     PG_RETURN_POLYGON_P(poly);
    4660              : }
    4661              : 
    4662              : 
    4663              : Datum
    4664           28 : poly_path(PG_FUNCTION_ARGS)
    4665              : {
    4666           28 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    4667              :     PATH       *path;
    4668              :     int         size;
    4669              :     int         i;
    4670              : 
    4671              :     /*
    4672              :      * Never overflows: the old size fit in MaxAllocSize, and the new size is
    4673              :      * smaller by a small constant.
    4674              :      */
    4675           28 :     size = offsetof(PATH, p) + sizeof(path->p[0]) * poly->npts;
    4676           28 :     path = (PATH *) palloc(size);
    4677              : 
    4678           28 :     SET_VARSIZE(path, size);
    4679           28 :     path->npts = poly->npts;
    4680           28 :     path->closed = true;
    4681              :     /* prevent instability in unused pad bytes */
    4682           28 :     path->dummy = 0;
    4683              : 
    4684          112 :     for (i = 0; i < poly->npts; i++)
    4685              :     {
    4686           84 :         path->p[i].x = poly->p[i].x;
    4687           84 :         path->p[i].y = poly->p[i].y;
    4688              :     }
    4689              : 
    4690           28 :     PG_RETURN_PATH_P(path);
    4691              : }
    4692              : 
    4693              : 
    4694              : /***********************************************************************
    4695              :  **
    4696              :  **     Routines for circles.
    4697              :  **
    4698              :  ***********************************************************************/
    4699              : 
    4700              : /*----------------------------------------------------------
    4701              :  * Formatting and conversion routines.
    4702              :  *---------------------------------------------------------*/
    4703              : 
    4704              : /*
    4705              :  * circle_in        -       convert a string to internal form.
    4706              :  *
    4707              :  *      External format: (center and radius of circle)
    4708              :  *              "<(f8,f8),f8>"
    4709              :  *              also supports quick entry style "f8,f8,f8"
    4710              :  */
    4711              : Datum
    4712          270 : circle_in(PG_FUNCTION_ARGS)
    4713              : {
    4714          270 :     char       *str = PG_GETARG_CSTRING(0);
    4715          270 :     Node       *escontext = fcinfo->context;
    4716          270 :     CIRCLE     *circle = palloc_object(CIRCLE);
    4717              :     char       *s,
    4718              :                *cp;
    4719          270 :     int         depth = 0;
    4720              : 
    4721          270 :     s = str;
    4722          286 :     while (isspace((unsigned char) *s))
    4723           16 :         s++;
    4724          270 :     if (*s == LDELIM_C)
    4725          220 :         depth++, s++;
    4726           50 :     else if (*s == LDELIM)
    4727              :     {
    4728              :         /* If there are two left parens, consume the first one */
    4729           34 :         cp = (s + 1);
    4730           42 :         while (isspace((unsigned char) *cp))
    4731            8 :             cp++;
    4732           34 :         if (*cp == LDELIM)
    4733           14 :             depth++, s = cp;
    4734              :     }
    4735              : 
    4736              :     /* pair_decode will consume parens around the pair, if any */
    4737          270 :     if (!pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle", str,
    4738              :                      escontext))
    4739            8 :         PG_RETURN_NULL();
    4740              : 
    4741          254 :     if (*s == DELIM)
    4742          254 :         s++;
    4743              : 
    4744          254 :     if (!single_decode(s, &circle->radius, &s, "circle", str, escontext))
    4745            0 :         PG_RETURN_NULL();
    4746              : 
    4747              :     /* We have to accept NaN. */
    4748          254 :     if (circle->radius < 0.0)
    4749           12 :         ereturn(escontext, (Datum) 0,
    4750              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    4751              :                  errmsg("invalid input syntax for type %s: \"%s\"",
    4752              :                         "circle", str)));
    4753              : 
    4754          468 :     while (depth > 0)
    4755              :     {
    4756          230 :         if ((*s == RDELIM) || ((*s == RDELIM_C) && (depth == 1)))
    4757              :         {
    4758          226 :             depth--;
    4759          226 :             s++;
    4760          238 :             while (isspace((unsigned char) *s))
    4761           12 :                 s++;
    4762              :         }
    4763              :         else
    4764            4 :             ereturn(escontext, (Datum) 0,
    4765              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    4766              :                      errmsg("invalid input syntax for type %s: \"%s\"",
    4767              :                             "circle", str)));
    4768              :     }
    4769              : 
    4770          238 :     if (*s != '\0')
    4771            4 :         ereturn(escontext, (Datum) 0,
    4772              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
    4773              :                  errmsg("invalid input syntax for type %s: \"%s\"",
    4774              :                         "circle", str)));
    4775              : 
    4776          234 :     PG_RETURN_CIRCLE_P(circle);
    4777              : }
    4778              : 
    4779              : /*
    4780              :  * circle_out       -       convert a circle to external form.
    4781              :  */
    4782              : Datum
    4783         6104 : circle_out(PG_FUNCTION_ARGS)
    4784              : {
    4785         6104 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    4786              :     StringInfoData str;
    4787              : 
    4788         6104 :     initStringInfo(&str);
    4789              : 
    4790         6104 :     appendStringInfoChar(&str, LDELIM_C);
    4791         6104 :     appendStringInfoChar(&str, LDELIM);
    4792         6104 :     pair_encode(circle->center.x, circle->center.y, &str);
    4793         6104 :     appendStringInfoChar(&str, RDELIM);
    4794         6104 :     appendStringInfoChar(&str, DELIM);
    4795         6104 :     single_encode(circle->radius, &str);
    4796         6104 :     appendStringInfoChar(&str, RDELIM_C);
    4797              : 
    4798         6104 :     PG_RETURN_CSTRING(str.data);
    4799              : }
    4800              : 
    4801              : /*
    4802              :  *      circle_recv         - converts external binary format to circle
    4803              :  */
    4804              : Datum
    4805            0 : circle_recv(PG_FUNCTION_ARGS)
    4806              : {
    4807            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    4808              :     CIRCLE     *circle;
    4809              : 
    4810            0 :     circle = palloc_object(CIRCLE);
    4811              : 
    4812            0 :     circle->center.x = pq_getmsgfloat8(buf);
    4813            0 :     circle->center.y = pq_getmsgfloat8(buf);
    4814            0 :     circle->radius = pq_getmsgfloat8(buf);
    4815              : 
    4816              :     /* We have to accept NaN. */
    4817            0 :     if (circle->radius < 0.0)
    4818            0 :         ereport(ERROR,
    4819              :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
    4820              :                  errmsg("invalid radius in external \"circle\" value")));
    4821              : 
    4822            0 :     PG_RETURN_CIRCLE_P(circle);
    4823              : }
    4824              : 
    4825              : /*
    4826              :  *      circle_send         - converts circle to binary format
    4827              :  */
    4828              : Datum
    4829            0 : circle_send(PG_FUNCTION_ARGS)
    4830              : {
    4831            0 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    4832              :     StringInfoData buf;
    4833              : 
    4834            0 :     pq_begintypsend(&buf);
    4835            0 :     pq_sendfloat8(&buf, circle->center.x);
    4836            0 :     pq_sendfloat8(&buf, circle->center.y);
    4837            0 :     pq_sendfloat8(&buf, circle->radius);
    4838            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    4839              : }
    4840              : 
    4841              : 
    4842              : /*----------------------------------------------------------
    4843              :  *  Relational operators for CIRCLEs.
    4844              :  *      <, >, <=, >=, and == are based on circle area.
    4845              :  *---------------------------------------------------------*/
    4846              : 
    4847              : /*
    4848              :  * circles identical?
    4849              :  *
    4850              :  * We consider NaNs values to be equal to each other to let those circles
    4851              :  * to be found.
    4852              :  */
    4853              : Datum
    4854          257 : circle_same(PG_FUNCTION_ARGS)
    4855              : {
    4856          257 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4857          257 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4858              : 
    4859          257 :     PG_RETURN_BOOL(((isnan(circle1->radius) && isnan(circle2->radius)) ||
    4860              :                     FPeq(circle1->radius, circle2->radius)) &&
    4861              :                    point_eq_point(&circle1->center, &circle2->center));
    4862              : }
    4863              : 
    4864              : /*
    4865              :  * circle_overlap   -       does circle1 overlap circle2?
    4866              :  */
    4867              : Datum
    4868        12812 : circle_overlap(PG_FUNCTION_ARGS)
    4869              : {
    4870        12812 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4871        12812 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4872              : 
    4873        12812 :     PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center, NULL),
    4874              :                         float8_pl(circle1->radius, circle2->radius)));
    4875              : }
    4876              : 
    4877              : /*
    4878              :  * circle_overleft -        is the right edge of circle1 at or left of
    4879              :  *                              the right edge of circle2?
    4880              :  */
    4881              : Datum
    4882          256 : circle_overleft(PG_FUNCTION_ARGS)
    4883              : {
    4884          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4885          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4886              : 
    4887          256 :     PG_RETURN_BOOL(FPle(float8_pl(circle1->center.x, circle1->radius),
    4888              :                         float8_pl(circle2->center.x, circle2->radius)));
    4889              : }
    4890              : 
    4891              : /*
    4892              :  * circle_left      -       is circle1 strictly left of circle2?
    4893              :  */
    4894              : Datum
    4895          256 : circle_left(PG_FUNCTION_ARGS)
    4896              : {
    4897          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4898          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4899              : 
    4900          256 :     PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.x, circle1->radius),
    4901              :                         float8_mi(circle2->center.x, circle2->radius)));
    4902              : }
    4903              : 
    4904              : /*
    4905              :  * circle_right -       is circle1 strictly right of circle2?
    4906              :  */
    4907              : Datum
    4908          256 : circle_right(PG_FUNCTION_ARGS)
    4909              : {
    4910          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4911          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4912              : 
    4913          256 :     PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.x, circle1->radius),
    4914              :                         float8_pl(circle2->center.x, circle2->radius)));
    4915              : }
    4916              : 
    4917              : /*
    4918              :  * circle_overright -   is the left edge of circle1 at or right of
    4919              :  *                              the left edge of circle2?
    4920              :  */
    4921              : Datum
    4922          256 : circle_overright(PG_FUNCTION_ARGS)
    4923              : {
    4924          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4925          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4926              : 
    4927          256 :     PG_RETURN_BOOL(FPge(float8_mi(circle1->center.x, circle1->radius),
    4928              :                         float8_mi(circle2->center.x, circle2->radius)));
    4929              : }
    4930              : 
    4931              : /*
    4932              :  * circle_contained     -       is circle1 contained by circle2?
    4933              :  */
    4934              : Datum
    4935          256 : circle_contained(PG_FUNCTION_ARGS)
    4936              : {
    4937          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4938          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4939              : 
    4940          256 :     PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center, NULL),
    4941              :                         float8_mi(circle2->radius, circle1->radius)));
    4942              : }
    4943              : 
    4944              : /*
    4945              :  * circle_contain   -       does circle1 contain circle2?
    4946              :  */
    4947              : Datum
    4948          264 : circle_contain(PG_FUNCTION_ARGS)
    4949              : {
    4950          264 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4951          264 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4952              : 
    4953          264 :     PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center, NULL),
    4954              :                         float8_mi(circle1->radius, circle2->radius)));
    4955              : }
    4956              : 
    4957              : 
    4958              : /*
    4959              :  * circle_below     -       is circle1 strictly below circle2?
    4960              :  */
    4961              : Datum
    4962          256 : circle_below(PG_FUNCTION_ARGS)
    4963              : {
    4964          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4965          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4966              : 
    4967          256 :     PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.y, circle1->radius),
    4968              :                         float8_mi(circle2->center.y, circle2->radius)));
    4969              : }
    4970              : 
    4971              : /*
    4972              :  * circle_above -       is circle1 strictly above circle2?
    4973              :  */
    4974              : Datum
    4975          256 : circle_above(PG_FUNCTION_ARGS)
    4976              : {
    4977          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4978          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4979              : 
    4980          256 :     PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.y, circle1->radius),
    4981              :                         float8_pl(circle2->center.y, circle2->radius)));
    4982              : }
    4983              : 
    4984              : /*
    4985              :  * circle_overbelow -       is the upper edge of circle1 at or below
    4986              :  *                              the upper edge of circle2?
    4987              :  */
    4988              : Datum
    4989          256 : circle_overbelow(PG_FUNCTION_ARGS)
    4990              : {
    4991          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    4992          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    4993              : 
    4994          256 :     PG_RETURN_BOOL(FPle(float8_pl(circle1->center.y, circle1->radius),
    4995              :                         float8_pl(circle2->center.y, circle2->radius)));
    4996              : }
    4997              : 
    4998              : /*
    4999              :  * circle_overabove -   is the lower edge of circle1 at or above
    5000              :  *                              the lower edge of circle2?
    5001              :  */
    5002              : Datum
    5003          256 : circle_overabove(PG_FUNCTION_ARGS)
    5004              : {
    5005          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5006          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5007              : 
    5008          256 :     PG_RETURN_BOOL(FPge(float8_mi(circle1->center.y, circle1->radius),
    5009              :                         float8_mi(circle2->center.y, circle2->radius)));
    5010              : }
    5011              : 
    5012              : 
    5013              : /*
    5014              :  * circle_relop -       is area(circle1) relop area(circle2), within
    5015              :  *                              our accuracy constraint?
    5016              :  */
    5017              : Datum
    5018          256 : circle_eq(PG_FUNCTION_ARGS)
    5019              : {
    5020          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5021          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5022              : 
    5023          256 :     PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
    5024              : }
    5025              : 
    5026              : Datum
    5027          256 : circle_ne(PG_FUNCTION_ARGS)
    5028              : {
    5029          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5030          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5031              : 
    5032          256 :     PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
    5033              : }
    5034              : 
    5035              : Datum
    5036         1052 : circle_lt(PG_FUNCTION_ARGS)
    5037              : {
    5038         1052 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5039         1052 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5040              : 
    5041         1052 :     PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
    5042              : }
    5043              : 
    5044              : Datum
    5045          256 : circle_gt(PG_FUNCTION_ARGS)
    5046              : {
    5047          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5048          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5049              : 
    5050          256 :     PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
    5051              : }
    5052              : 
    5053              : Datum
    5054          256 : circle_le(PG_FUNCTION_ARGS)
    5055              : {
    5056          256 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5057          256 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5058              : 
    5059          256 :     PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
    5060              : }
    5061              : 
    5062              : Datum
    5063          324 : circle_ge(PG_FUNCTION_ARGS)
    5064              : {
    5065          324 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5066          324 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5067              : 
    5068          324 :     PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
    5069              : }
    5070              : 
    5071              : 
    5072              : /*----------------------------------------------------------
    5073              :  *  "Arithmetic" operators on circles.
    5074              :  *---------------------------------------------------------*/
    5075              : 
    5076              : /*
    5077              :  * circle_add_pt()
    5078              :  * Translation operator.
    5079              :  */
    5080              : Datum
    5081          320 : circle_add_pt(PG_FUNCTION_ARGS)
    5082              : {
    5083          320 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5084          320 :     Point      *point = PG_GETARG_POINT_P(1);
    5085              :     CIRCLE     *result;
    5086              : 
    5087          320 :     result = palloc_object(CIRCLE);
    5088              : 
    5089          320 :     point_add_point(&result->center, &circle->center, point, NULL);
    5090          320 :     result->radius = circle->radius;
    5091              : 
    5092          320 :     PG_RETURN_CIRCLE_P(result);
    5093              : }
    5094              : 
    5095              : Datum
    5096          320 : circle_sub_pt(PG_FUNCTION_ARGS)
    5097              : {
    5098          320 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5099          320 :     Point      *point = PG_GETARG_POINT_P(1);
    5100              :     CIRCLE     *result;
    5101              : 
    5102          320 :     result = palloc_object(CIRCLE);
    5103              : 
    5104          320 :     point_sub_point(&result->center, &circle->center, point);
    5105          320 :     result->radius = circle->radius;
    5106              : 
    5107          320 :     PG_RETURN_CIRCLE_P(result);
    5108              : }
    5109              : 
    5110              : 
    5111              : /*
    5112              :  * circle_mul_pt()
    5113              :  * Rotation and scaling operators.
    5114              :  */
    5115              : Datum
    5116          320 : circle_mul_pt(PG_FUNCTION_ARGS)
    5117              : {
    5118          320 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5119          320 :     Point      *point = PG_GETARG_POINT_P(1);
    5120              :     CIRCLE     *result;
    5121              : 
    5122          320 :     result = palloc_object(CIRCLE);
    5123              : 
    5124          320 :     point_mul_point(&result->center, &circle->center, point);
    5125          320 :     result->radius = float8_mul(circle->radius, hypot(point->x, point->y));
    5126              : 
    5127          320 :     PG_RETURN_CIRCLE_P(result);
    5128              : }
    5129              : 
    5130              : Datum
    5131           72 : circle_div_pt(PG_FUNCTION_ARGS)
    5132              : {
    5133           72 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5134           72 :     Point      *point = PG_GETARG_POINT_P(1);
    5135              :     CIRCLE     *result;
    5136              : 
    5137           72 :     result = palloc_object(CIRCLE);
    5138              : 
    5139           72 :     point_div_point(&result->center, &circle->center, point);
    5140           64 :     result->radius = float8_div(circle->radius, hypot(point->x, point->y));
    5141              : 
    5142           64 :     PG_RETURN_CIRCLE_P(result);
    5143              : }
    5144              : 
    5145              : 
    5146              : /*
    5147              :  * circle_area      -       returns the area of the circle.
    5148              :  */
    5149              : Datum
    5150          340 : circle_area(PG_FUNCTION_ARGS)
    5151              : {
    5152          340 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5153              : 
    5154          340 :     PG_RETURN_FLOAT8(circle_ar(circle));
    5155              : }
    5156              : 
    5157              : 
    5158              : /*
    5159              :  * circle_diameter -        returns the diameter of the circle.
    5160              :  */
    5161              : Datum
    5162           64 : circle_diameter(PG_FUNCTION_ARGS)
    5163              : {
    5164           64 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5165              : 
    5166           64 :     PG_RETURN_FLOAT8(float8_mul(circle->radius, 2.0));
    5167              : }
    5168              : 
    5169              : 
    5170              : /*
    5171              :  * circle_radius    -       returns the radius of the circle.
    5172              :  */
    5173              : Datum
    5174        12504 : circle_radius(PG_FUNCTION_ARGS)
    5175              : {
    5176        12504 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5177              : 
    5178        12504 :     PG_RETURN_FLOAT8(circle->radius);
    5179              : }
    5180              : 
    5181              : 
    5182              : /*
    5183              :  * circle_distance -        returns the distance between
    5184              :  *                                two circles.
    5185              :  */
    5186              : Datum
    5187          112 : circle_distance(PG_FUNCTION_ARGS)
    5188              : {
    5189          112 :     CIRCLE     *circle1 = PG_GETARG_CIRCLE_P(0);
    5190          112 :     CIRCLE     *circle2 = PG_GETARG_CIRCLE_P(1);
    5191              :     float8      result;
    5192              : 
    5193          112 :     result = float8_mi(point_dt(&circle1->center, &circle2->center, NULL),
    5194              :                        float8_pl(circle1->radius, circle2->radius));
    5195          112 :     if (result < 0.0)
    5196           48 :         result = 0.0;
    5197              : 
    5198          112 :     PG_RETURN_FLOAT8(result);
    5199              : }
    5200              : 
    5201              : 
    5202              : Datum
    5203           16 : circle_contain_pt(PG_FUNCTION_ARGS)
    5204              : {
    5205           16 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5206           16 :     Point      *point = PG_GETARG_POINT_P(1);
    5207              :     float8      d;
    5208              : 
    5209           16 :     d = point_dt(&circle->center, point, NULL);
    5210           16 :     PG_RETURN_BOOL(d <= circle->radius);
    5211              : }
    5212              : 
    5213              : 
    5214              : Datum
    5215           40 : pt_contained_circle(PG_FUNCTION_ARGS)
    5216              : {
    5217           40 :     Point      *point = PG_GETARG_POINT_P(0);
    5218           40 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(1);
    5219              :     float8      d;
    5220              : 
    5221           40 :     d = point_dt(&circle->center, point, NULL);
    5222           40 :     PG_RETURN_BOOL(d <= circle->radius);
    5223              : }
    5224              : 
    5225              : 
    5226              : /*
    5227              :  * dist_pc -        returns the distance between
    5228              :  *                        a point and a circle.
    5229              :  */
    5230              : Datum
    5231          564 : dist_pc(PG_FUNCTION_ARGS)
    5232              : {
    5233          564 :     Point      *point = PG_GETARG_POINT_P(0);
    5234          564 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(1);
    5235              :     float8      result;
    5236              : 
    5237          564 :     result = float8_mi(point_dt(point, &circle->center, NULL),
    5238              :                        circle->radius);
    5239          564 :     if (result < 0.0)
    5240           76 :         result = 0.0;
    5241              : 
    5242          564 :     PG_RETURN_FLOAT8(result);
    5243              : }
    5244              : 
    5245              : /*
    5246              :  * Distance from a circle to a point
    5247              :  */
    5248              : Datum
    5249        12484 : dist_cpoint(PG_FUNCTION_ARGS)
    5250              : {
    5251        12484 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5252        12484 :     Point      *point = PG_GETARG_POINT_P(1);
    5253              :     float8      result;
    5254              : 
    5255        12484 :     result = float8_mi(point_dt(point, &circle->center, NULL), circle->radius);
    5256        12484 :     if (result < 0.0)
    5257            0 :         result = 0.0;
    5258              : 
    5259        12484 :     PG_RETURN_FLOAT8(result);
    5260              : }
    5261              : 
    5262              : /*
    5263              :  * circle_center    -       returns the center point of the circle.
    5264              :  */
    5265              : Datum
    5266        12604 : circle_center(PG_FUNCTION_ARGS)
    5267              : {
    5268        12604 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5269              :     Point      *result;
    5270              : 
    5271        12604 :     result = palloc_object(Point);
    5272        12604 :     result->x = circle->center.x;
    5273        12604 :     result->y = circle->center.y;
    5274              : 
    5275        12604 :     PG_RETURN_POINT_P(result);
    5276              : }
    5277              : 
    5278              : 
    5279              : /*
    5280              :  * circle_ar        -       returns the area of the circle.
    5281              :  */
    5282              : static float8
    5283         5140 : circle_ar(CIRCLE *circle)
    5284              : {
    5285         5140 :     return float8_mul(float8_mul(circle->radius, circle->radius), M_PI);
    5286              : }
    5287              : 
    5288              : 
    5289              : /*----------------------------------------------------------
    5290              :  *  Conversion operators.
    5291              :  *---------------------------------------------------------*/
    5292              : 
    5293              : Datum
    5294       120110 : cr_circle(PG_FUNCTION_ARGS)
    5295              : {
    5296       120110 :     Point      *center = PG_GETARG_POINT_P(0);
    5297       120110 :     float8      radius = PG_GETARG_FLOAT8(1);
    5298              :     CIRCLE     *result;
    5299              : 
    5300       120110 :     result = palloc_object(CIRCLE);
    5301              : 
    5302       120110 :     result->center.x = center->x;
    5303       120110 :     result->center.y = center->y;
    5304       120110 :     result->radius = radius;
    5305              : 
    5306       120110 :     PG_RETURN_CIRCLE_P(result);
    5307              : }
    5308              : 
    5309              : Datum
    5310           32 : circle_box(PG_FUNCTION_ARGS)
    5311              : {
    5312           32 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5313              :     BOX        *box;
    5314              :     float8      delta;
    5315              : 
    5316           32 :     box = palloc_object(BOX);
    5317              : 
    5318           32 :     delta = float8_div_safe(circle->radius, sqrt(2.0), fcinfo->context);
    5319           32 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5320            0 :         goto fail;
    5321              : 
    5322           32 :     box->high.x = float8_pl_safe(circle->center.x, delta, fcinfo->context);
    5323           32 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5324            0 :         goto fail;
    5325              : 
    5326           32 :     box->low.x = float8_mi_safe(circle->center.x, delta, fcinfo->context);
    5327           32 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5328            0 :         goto fail;
    5329              : 
    5330           32 :     box->high.y = float8_pl_safe(circle->center.y, delta, fcinfo->context);
    5331           32 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5332            0 :         goto fail;
    5333              : 
    5334           32 :     box->low.y = float8_mi_safe(circle->center.y, delta, fcinfo->context);
    5335           32 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5336            0 :         goto fail;
    5337              : 
    5338           32 :     PG_RETURN_BOX_P(box);
    5339              : 
    5340            0 : fail:
    5341            0 :     PG_RETURN_NULL();
    5342              : }
    5343              : 
    5344              : /*
    5345              :  * box_circle()
    5346              :  * Convert a box to a circle.
    5347              :  */
    5348              : Datum
    5349        12420 : box_circle(PG_FUNCTION_ARGS)
    5350              : {
    5351        12420 :     BOX        *box = PG_GETARG_BOX_P(0);
    5352              :     CIRCLE     *circle;
    5353              :     float8      x;
    5354              :     float8      y;
    5355              : 
    5356        12420 :     circle = palloc_object(CIRCLE);
    5357              : 
    5358        12420 :     x = float8_pl_safe(box->high.x, box->low.x, fcinfo->context);
    5359        12420 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5360            0 :         goto fail;
    5361              : 
    5362        12420 :     circle->center.x = float8_div_safe(x, 2.0, fcinfo->context);
    5363        12420 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5364            0 :         goto fail;
    5365              : 
    5366        12420 :     y = float8_pl_safe(box->high.y, box->low.y, fcinfo->context);
    5367        12420 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5368            0 :         goto fail;
    5369              : 
    5370        12420 :     circle->center.y = float8_div_safe(y, 2.0, fcinfo->context);
    5371        12420 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5372            0 :         goto fail;
    5373              : 
    5374        12420 :     circle->radius = point_dt(&circle->center, &box->high,
    5375              :                               fcinfo->context);
    5376              : 
    5377        12420 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5378            0 :         goto fail;
    5379              : 
    5380        12420 :     PG_RETURN_CIRCLE_P(circle);
    5381              : 
    5382            0 : fail:
    5383            0 :     PG_RETURN_NULL();
    5384              : }
    5385              : 
    5386              : 
    5387              : static POLYGON *
    5388        40057 : circle_poly_internal(int32 npts, const CIRCLE *circle, FunctionCallInfo fcinfo)
    5389              : {
    5390              :     POLYGON    *poly;
    5391              :     int         base_size,
    5392              :                 size;
    5393              :     int         i;
    5394              :     float8      angle;
    5395              :     float8      anglestep;
    5396              : 
    5397        40057 :     if (FPzero(circle->radius))
    5398            4 :         ereturn(fcinfo->context, NULL,
    5399              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5400              :                  errmsg("cannot convert circle with radius zero to polygon")));
    5401              : 
    5402        40053 :     if (npts < 2)
    5403            4 :         ereturn(fcinfo->context, NULL,
    5404              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    5405              :                  errmsg("must request at least 2 points")));
    5406              : 
    5407        40049 :     base_size = sizeof(poly->p[0]) * npts;
    5408        40049 :     size = offsetof(POLYGON, p) + base_size;
    5409              : 
    5410              :     /* Check for integer overflow */
    5411        40049 :     if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
    5412            0 :         ereturn(fcinfo->context, NULL,
    5413              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    5414              :                  errmsg("too many points requested")));
    5415              : 
    5416        40049 :     poly = (POLYGON *) palloc0(size);   /* zero any holes */
    5417        40049 :     SET_VARSIZE(poly, size);
    5418        40049 :     poly->npts = npts;
    5419              : 
    5420        40049 :     anglestep = float8_div(2.0 * M_PI, npts);
    5421              : 
    5422       520541 :     for (i = 0; i < npts; i++)
    5423              :     {
    5424              :         float8      temp;
    5425              : 
    5426       480492 :         angle = float8_mul_safe(anglestep, i, fcinfo->context);
    5427       480492 :         if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5428            0 :             return NULL;
    5429              : 
    5430       480492 :         temp = float8_mul_safe(circle->radius, cos(angle), fcinfo->context);
    5431       480492 :         if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5432            0 :             return NULL;
    5433              : 
    5434       480492 :         poly->p[i].x = float8_mi_safe(circle->center.x, temp, fcinfo->context);
    5435       480492 :         if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5436            0 :             return NULL;
    5437              : 
    5438       480492 :         temp = float8_mul_safe(circle->radius, sin(angle), fcinfo->context);
    5439       480492 :         if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5440            0 :             return NULL;
    5441              : 
    5442       480492 :         poly->p[i].y = float8_pl_safe(circle->center.y, temp, fcinfo->context);
    5443       480492 :         if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5444            0 :             return NULL;
    5445              :     }
    5446              : 
    5447        40049 :     make_bound_box(poly);
    5448              : 
    5449        40049 :     return poly;
    5450              : }
    5451              : 
    5452              : Datum
    5453           32 : circle_poly(PG_FUNCTION_ARGS)
    5454              : {
    5455           32 :     int32       npts = PG_GETARG_INT32(0);
    5456           32 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(1);
    5457              : 
    5458           32 :     PG_RETURN_POLYGON_P(circle_poly_internal(npts, circle, fcinfo));
    5459              : }
    5460              : 
    5461              : /* convert circle to 12-vertex polygon */
    5462              : Datum
    5463        40025 : circle_to_poly(PG_FUNCTION_ARGS)
    5464              : {
    5465        40025 :     int32       npts = 12;
    5466        40025 :     CIRCLE     *circle = PG_GETARG_CIRCLE_P(0);
    5467              : 
    5468        40025 :     PG_RETURN_POLYGON_P(circle_poly_internal(npts, circle, fcinfo));
    5469              : }
    5470              : 
    5471              : /*
    5472              :  * Convert polygon to circle
    5473              :  *
    5474              :  * The result must be preallocated.
    5475              :  *
    5476              :  * XXX This algorithm should use weighted means of line segments
    5477              :  *  rather than straight average values of points - tgl 97/01/21.
    5478              :  */
    5479              : static void
    5480           48 : poly_to_circle(CIRCLE *result, POLYGON *poly, Node *escontext)
    5481              : {
    5482              :     int         i;
    5483              :     float8      x;
    5484              : 
    5485              :     Assert(poly->npts > 0);
    5486              : 
    5487           48 :     result->center.x = 0;
    5488           48 :     result->center.y = 0;
    5489           48 :     result->radius = 0;
    5490              : 
    5491          216 :     for (i = 0; i < poly->npts; i++)
    5492              :     {
    5493          168 :         point_add_point(&result->center, &result->center, &poly->p[i], escontext);
    5494          168 :         if (SOFT_ERROR_OCCURRED(escontext))
    5495            0 :             return;
    5496              :     }
    5497              : 
    5498           48 :     result->center.x = float8_div_safe(result->center.x, poly->npts, escontext);
    5499           48 :     if (SOFT_ERROR_OCCURRED(escontext))
    5500            0 :         return;
    5501              : 
    5502           48 :     result->center.y = float8_div_safe(result->center.y, poly->npts, escontext);
    5503           48 :     if (SOFT_ERROR_OCCURRED(escontext))
    5504            0 :         return;
    5505              : 
    5506          216 :     for (i = 0; i < poly->npts; i++)
    5507              :     {
    5508          168 :         x = point_dt(&poly->p[i], &result->center, escontext);
    5509          168 :         if (SOFT_ERROR_OCCURRED(escontext))
    5510            0 :             return;
    5511              : 
    5512          168 :         result->radius = float8_pl_safe(result->radius, x, escontext);
    5513          168 :         if (SOFT_ERROR_OCCURRED(escontext))
    5514            0 :             return;
    5515              :     }
    5516              : 
    5517           48 :     result->radius = float8_div_safe(result->radius, poly->npts, escontext);
    5518           48 :     if (SOFT_ERROR_OCCURRED(escontext))
    5519            0 :         return;
    5520              : }
    5521              : 
    5522              : Datum
    5523           20 : poly_circle(PG_FUNCTION_ARGS)
    5524              : {
    5525           20 :     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
    5526              :     CIRCLE     *result;
    5527              : 
    5528           20 :     result = palloc_object(CIRCLE);
    5529              : 
    5530           20 :     poly_to_circle(result, poly, fcinfo->context);
    5531           20 :     if (SOFT_ERROR_OCCURRED(fcinfo->context))
    5532            0 :         PG_RETURN_NULL();
    5533              : 
    5534           20 :     PG_RETURN_CIRCLE_P(result);
    5535              : }
    5536              : 
    5537              : 
    5538              : /***********************************************************************
    5539              :  **
    5540              :  **     Private routines for multiple types.
    5541              :  **
    5542              :  ***********************************************************************/
    5543              : 
    5544              : /*
    5545              :  *  Test to see if the point is inside the polygon, returns 1/0, or 2 if
    5546              :  *  the point is on the polygon.
    5547              :  *  Code adapted but not copied from integer-based routines in WN: A
    5548              :  *  Server for the HTTP
    5549              :  *  version 1.15.1, file wn/image.c
    5550              :  *  http://hopf.math.northwestern.edu/index.html
    5551              :  *  Description of algorithm:  http://www.linuxjournal.com/article/2197
    5552              :  *                             http://www.linuxjournal.com/article/2029
    5553              :  */
    5554              : 
    5555              : #define POINT_ON_POLYGON INT_MAX
    5556              : 
    5557              : static int
    5558       164433 : point_inside(Point *p, int npts, Point *plist)
    5559              : {
    5560              :     float8      x0,
    5561              :                 y0;
    5562              :     float8      prev_x,
    5563              :                 prev_y;
    5564       164433 :     int         i = 0;
    5565              :     float8      x,
    5566              :                 y;
    5567              :     int         cross,
    5568       164433 :                 total_cross = 0;
    5569              : 
    5570              :     Assert(npts > 0);
    5571              : 
    5572              :     /* compute first polygon point relative to single point */
    5573       164433 :     x0 = float8_mi(plist[0].x, p->x);
    5574       164433 :     y0 = float8_mi(plist[0].y, p->y);
    5575              : 
    5576       164433 :     prev_x = x0;
    5577       164433 :     prev_y = y0;
    5578              :     /* loop over polygon points and aggregate total_cross */
    5579       758628 :     for (i = 1; i < npts; i++)
    5580              :     {
    5581              :         /* compute next polygon point relative to single point */
    5582       594283 :         x = float8_mi(plist[i].x, p->x);
    5583       594283 :         y = float8_mi(plist[i].y, p->y);
    5584              : 
    5585              :         /* compute previous to current point crossing */
    5586       594283 :         if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
    5587           88 :             return 2;
    5588       594195 :         total_cross += cross;
    5589              : 
    5590       594195 :         prev_x = x;
    5591       594195 :         prev_y = y;
    5592              :     }
    5593              : 
    5594              :     /* now do the first point */
    5595       164345 :     if ((cross = lseg_crossing(x0, y0, prev_x, prev_y)) == POINT_ON_POLYGON)
    5596           72 :         return 2;
    5597       164273 :     total_cross += cross;
    5598              : 
    5599       164273 :     if (total_cross != 0)
    5600       126129 :         return 1;
    5601        38144 :     return 0;
    5602              : }
    5603              : 
    5604              : 
    5605              : /*
    5606              :  * lseg_crossing()
    5607              :  * Returns +/-2 if line segment crosses the positive X-axis in a +/- direction.
    5608              :  * Returns +/-1 if one point is on the positive X-axis.
    5609              :  * Returns 0 if both points are on the positive X-axis, or there is no crossing.
    5610              :  * Returns POINT_ON_POLYGON if the segment contains (0,0).
    5611              :  * Wow, that is one confusing API, but it is used above, and when summed,
    5612              :  * can tell is if a point is in a polygon.
    5613              :  */
    5614              : 
    5615              : static int
    5616       758628 : lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y)
    5617              : {
    5618              :     float8      z;
    5619              :     int         y_sign;
    5620              : 
    5621       758628 :     if (FPzero(y))
    5622              :     {                           /* y == 0, on X axis */
    5623         1162 :         if (FPzero(x))          /* (x,y) is (0,0)? */
    5624          152 :             return POINT_ON_POLYGON;
    5625         1010 :         else if (FPgt(x, 0))
    5626              :         {                       /* x > 0 */
    5627          673 :             if (FPzero(prev_y)) /* y and prev_y are zero */
    5628              :                 /* prev_x > 0? */
    5629           40 :                 return FPgt(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
    5630          633 :             return FPlt(prev_y, 0.0) ? 1 : -1;
    5631              :         }
    5632              :         else
    5633              :         {                       /* x < 0, x not on positive X axis */
    5634          337 :             if (FPzero(prev_y))
    5635              :                 /* prev_x < 0? */
    5636           16 :                 return FPlt(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
    5637          321 :             return 0;
    5638              :         }
    5639              :     }
    5640              :     else
    5641              :     {                           /* y != 0 */
    5642              :         /* compute y crossing direction from previous point */
    5643       757466 :         y_sign = FPgt(y, 0.0) ? 1 : -1;
    5644              : 
    5645       757466 :         if (FPzero(prev_y))
    5646              :             /* previous point was on X axis, so new point is either off or on */
    5647         1018 :             return FPlt(prev_x, 0.0) ? 0 : y_sign;
    5648       756448 :         else if ((y_sign < 0 && FPlt(prev_y, 0.0)) ||
    5649       386704 :                  (y_sign > 0 && FPgt(prev_y, 0.0)))
    5650              :             /* both above or below X axis */
    5651       481664 :             return 0;           /* same sign */
    5652              :         else
    5653              :         {                       /* y and prev_y cross X-axis */
    5654       274784 :             if (FPge(x, 0.0) && FPgt(prev_x, 0.0))
    5655              :                 /* both non-negative so cross positive X-axis */
    5656        99924 :                 return 2 * y_sign;
    5657       174860 :             if (FPlt(x, 0.0) && FPle(prev_x, 0.0))
    5658              :                 /* both non-positive so do not cross positive X-axis */
    5659        88296 :                 return 0;
    5660              : 
    5661              :             /* x and y cross axes, see URL above point_inside() */
    5662        86564 :             z = float8_mi(float8_mul(float8_mi(x, prev_x), y),
    5663              :                           float8_mul(float8_mi(y, prev_y), x));
    5664        86564 :             if (FPzero(z))
    5665            8 :                 return POINT_ON_POLYGON;
    5666        86556 :             if ((y_sign < 0 && FPlt(z, 0.0)) ||
    5667        49664 :                 (y_sign > 0 && FPgt(z, 0.0)))
    5668        48456 :                 return 0;
    5669        38100 :             return 2 * y_sign;
    5670              :         }
    5671              :     }
    5672              : }
    5673              : 
    5674              : 
    5675              : static bool
    5676         4061 : plist_same(int npts, Point *p1, Point *p2)
    5677              : {
    5678              :     int         i,
    5679              :                 ii,
    5680              :                 j;
    5681              : 
    5682              :     /* find match for first point */
    5683         4173 :     for (i = 0; i < npts; i++)
    5684              :     {
    5685         4149 :         if (point_eq_point(&p2[i], &p1[0]))
    5686              :         {
    5687              : 
    5688              :             /* match found? then look forward through remaining points */
    5689        12122 :             for (ii = 1, j = i + 1; ii < npts; ii++, j++)
    5690              :             {
    5691         8093 :                 if (j >= npts)
    5692           12 :                     j = 0;
    5693         8093 :                 if (!point_eq_point(&p2[j], &p1[ii]))
    5694           24 :                     break;
    5695              :             }
    5696         4053 :             if (ii == npts)
    5697         4029 :                 return true;
    5698              : 
    5699              :             /* match not found forwards? then look backwards */
    5700           56 :             for (ii = 1, j = i - 1; ii < npts; ii++, j--)
    5701              :             {
    5702           48 :                 if (j < 0)
    5703            8 :                     j = (npts - 1);
    5704           48 :                 if (!point_eq_point(&p2[j], &p1[ii]))
    5705           16 :                     break;
    5706              :             }
    5707           24 :             if (ii == npts)
    5708            8 :                 return true;
    5709              :         }
    5710              :     }
    5711              : 
    5712           24 :     return false;
    5713              : }
        

Generated by: LCOV version 2.0-1