/*
 *	chull finds the convex hull of a set of points in the plane.
 *
 *	It is based on a C translation (by f2c) of
 *	ACM TOMS algorithm 523 by W. F. Eddy, vol 3 (1977), 398-403, 411-2.
 *
 *	converted to double precision, output order altered
 *	by B.D. Ripley, March 1999
 *
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <R_ext/Boolean.h>	/* TRUE,... */


static void split(int n, double *x,
		  int m, int *in,
		  int ii, int jj,
		  int s,
		  int *iabv, int *na, int *maxa,
		  int *ibel, int *nb, int *maxb)
{
/* split() takes the m points of array x whose
 subscripts are in array in and partitions them by the
 line joining the two points in array x whose subscripts are ii and jj.
 The subscripts of the points above the line are put into array
 iabv, and the subscripts of the points below are put into array ibel.

 na and nb are, respectively, the number of points
 above the line and the number below.
 maxa and maxb are the subscripts for array
 x of the point furthest above the line and the point
 furthest below, respectively. if either subset is null
 the corresponding subscript (maxa or maxb) is set to zero.

 formal parameters
 INPUT
	n    integer		total number of data points
	x    real array (2,n)	(x,y) co-ordinates of the data
	m    integer		number of points in input subset
	in   integer array (m)	subscripts for array x of the
				points in the input subset
	ii   integer		subscript for array x of one point
				on the partitioning line
	jj   integer		subscript for array x of another
				point on the partitioning line
	s    integer		switch to determine output.
				refer to comments below
 OUTPUT
	iabv integer array (m)	subscripts for array x of the
				points above the partitioning line
	na   integer		number of elements in iabv
	maxa integer		subscript for array x of point
				furthest above the line.
				set to zero if na is zero
	ibel integer array (m)	subscripts for array x of the
				points below the partitioning line
	nb   integer		number of elements in ibel
	maxb integer		subscript for array x of point
				furthest below the line.
				set to zero if nb is zero

 if s = 2 dont save ibel,nb,maxb.
 if s =-2 dont save iabv,na,maxa.
 otherwise save everything
 if s is positive the array being partitioned is above
 the initial partitioning line.
 if it is negative, then the set of points is below.
*/

    /* Local variables (=0 : -Wall) */
    double a=0, b=0, down, d1, up, xt, z;
    int i, is;
    bool vert, neg_dir = false;

    /* Parameter adjustments */
    --x;

    xt = x[ii];
    /* Check to see if the line is vertical */
    vert = (x[jj] == xt);
    d1 = x[jj + n] - x[ii + n];
    if (vert) {
	neg_dir = ((s > 0 && d1 < 0.) || (s < 0 && d1 > 0.));
    } else {
	a = d1 / (x[jj] - xt);
	b = x[ii + n] - a * xt;
    }
    up	 = 0.; *na = 0; *maxa = 0;
    down = 0.; *nb = 0; *maxb = 0;
    for (i = 0; i < m; ++i) {
	is = in[i];
	if (vert) {
	    if(neg_dir) z = xt - x[is];
	    else	z = x[is] - xt;
	} else {
	    z = x[is + n] - a * x[is] - b;
	}
	if (z > 0.) {			/* the point is ABOVE the line */
	    if (s == -2) continue;
	    iabv[*na] = is;
	    ++(*na);
	    if (z >= up) {
		up = z;
		*maxa = *na;
	    }
	}
	else if (s != 2 && z < 0.) {	/* the point is BELOW the line */
	    ibel[*nb] = is;
	    ++(*nb);
	    if (z <= down) {
		down = z;
		*maxb = *nb;
	    }
	}
    }
}

static void in_chull(int *n, double *x, int *m, int *in,
		  int *ia, int *ib, int *ih, int *nh, int *il)
{
/* this subroutine determines which of the m points of array
 x whose subscripts are in array in are vertices of the
 minimum area convex polygon containing the m points. the
 subscripts of the vertices are placed in array ih in the
 order they are found. nh is the number of elements in
 array ih and array il. array il is a linked list giving
 the order of the elements of array ih in a counter
 clockwise direction. this algorithm corresponds to a
 preorder traversal of a certain binary tree. each vertex
 of the binary tree represents a subset of the m points.
 at each step the subset of points corresponding to the
 current vertex of the tree is partitioned by a line
 joining two vertices of the convex polygon. the left son
 vertex in the binary tree represents the subset of points
 above the partitioning line and the right son vertex, the
 subset below the line. the leaves of the tree represent
 either null subsets or subsets inside a triangle whose
 vertices coincide with vertices of the convex polygon.

 formal parameters
 INPUT
	n  integer		total number of data points (= nrow(x))
	x  real array (2,n)	(x,y) co-ordinates of the data
	m  integer		number of points in the input subset
	in integer array (m)	subscripts for array x of the points
				in the input subset
 work area
	ia integer array (m)	subscripts for array x of left son subsets.
				see comments after dimension statements
	ib integer array (m)	subscripts for array x of right son subsets

 OUTPUT
	ih integer array (m)	subscripts for array x of the
				vertices of the convex hull
	nh integer		number of elements in arrays ih and il.
				== number of vertices of the convex polygon
 il is used internally here.
	il integer array (m)	a linked list giving in order in a
				counter-clockwise direction the
				elements of array ih
 the upper end of array ia is used to store temporarily
 the sizes of the subsets which correspond to right son
 vertices, while traversing down the left sons when on the
 left half of the tree, and to store the sizes of the left
 sons while traversing the right sons(down the right half)
 */
#define y(k) x[k + x_dim1]

    Rboolean mine, maxe;
    int i, j, ilinh, ma, mb, kn, mm, kx, mx, mp1, mbb, nia, nib,
	inh, min, mxa, mxb, mxbb;
    int x_dim1, x_offset;
    double d1;

    /* Parameter adjustments */
    x_dim1 = *n;
    x_offset = 1;
    x -= x_offset;
    --il;
    --ih;
    --ib;
    --ia;
    --in;

    if (*m == 1) {
	goto L_1pt;
    }
    il[1] = 2;
    il[2] = 1;
    kn = in[1];
    kx = in[2];
    if (*m == 2) {
	goto L_2pts;
    }
    mp1 = *m + 1;
    min = 1;
    mx = 1;
    kx = in[1];
    maxe = false;
    mine = false;
    /* find two vertices of the convex hull for the initial partition */
    for (i = 2; i <= *m; ++i) {
	j = in[i];
	if ((d1 = x[j] - x[kx]) < 0.) {
	} else if (d1 == 0) {
	    maxe = true;
	} else {
	    maxe = false;
	    mx = i;
	    kx = j;
	}
	if ((d1 = x[j] - x[kn]) < 0.) {
	    mine = false;
	    min = i;
	    kn = j;
	} else if (d1 == 0) {
	    mine = true;
	}
    }

    if (kx == kn) { /* if the max and min are equal,
		     * all m points lie on a vertical line */
	goto L_vertical;
    }

    if (maxe || mine) {/* if maxe (or mine) is true, there are several
			  maxima (or minima) with equal first coordinates */

	if (maxe) {/* have several points with the (same) largest x[] */
	    for (i = 1; i <= *m; ++i) {
		j = in[i];
		if (x[j] != x[kx]) continue;
		if (y(j) <= y(kx)) continue;
		mx = i;
		kx = j;
	    }
	}

	if (mine) {/* have several points with the (same) smallest x[] */
	    for (i = 1; i <= *m; ++i) {
		j = in[i];
		if (x[j] != x[kn]) continue;
		if (y(j) >= y(kn)) continue;
		min = i;
		kn = j;
	    }
	}

    }

/* L7:*/
    ih[1] = kx;
    ih[2] = kn;
    *nh = 3;
    inh = 1;
    nib = 1;
    ma = *m;
    in[mx] = in[*m];
    in[*m] = kx;
    mm = *m - 2;
    if (min == *m) {
	min = mx;
    }
    in[min] = in[*m - 1];
    in[*m - 1] = kn;
/* begin by partitioning the root of the tree */
    split(*n, &x[x_offset], mm, &in[1],
	  ih[1], ih[2],
	  0,
	  &ia[1], &mb, &mxa,
	  &ib[1], &ia[ma], &mxbb);

/*	first traverse the LEFT HALF of the tree */

/* start with the left son */
 L8:
    nib += ia[ma];
    --ma;
    do {
	if (mxa != 0) {
	    il[*nh] = il[inh];
	    il[inh] = *nh;
	    ih[*nh] = ia[mxa];
	    ia[mxa] = ia[mb];
	    --mb;
	    ++(*nh);
	    if (mb != 0) {
		ilinh = il[inh];
		split(*n, &x[x_offset], mb, &ia[1],
		      ih[inh], ih[ilinh],
		      1,
		      &ia[1], &mbb, &mxa,
		      &ib[nib], &ia[ma], &mxb);
		mb = mbb;
		goto L8;
	    }
/* then the right son */
	    inh = il[inh];
	}

	do {
	    inh = il[inh];
	    ++ma;
	    nib -= ia[ma];
	    if (ma >= *m) goto L12;
	} while(ia[ma] == 0);
	ilinh = il[inh];
/* on the left side of the tree, the right son of a right son */
/* must represent a subset of points which is inside a */
/* triangle with vertices which are also vertices of the */
/* convex polygon and hence the subset may be neglected. */
	split(*n, &x[x_offset], ia[ma], &ib[nib],
	      ih[inh], ih[ilinh],
	      2,
	      &ia[1], &mb, &mxa,
	      &ib[nib], &mbb, &mxb);
	ia[ma] = mbb;
    } while(true);

/*	 now traverse the RIGHT HALF of the tree */
 L12:
    mxb = mxbb;
    ma = *m;
    mb = ia[ma];
    nia = 1;
    ia[ma] = 0;
/* start with the right son */
 L13:
    nia += ia[ma];
    --ma;

    do {
	if (mxb != 0) {
	    il[*nh] = il[inh];
	    il[inh] = *nh;
	    ih[*nh] = ib[mxb];
	    ib[mxb] = ib[mb];
	    --mb;
	    ++(*nh);
	    if (mb != 0) {
		ilinh = il[inh];
		split(*n, &x[x_offset], mb, &ib[nib],
		      ih[inh], ih[ilinh],
		      -1,
		      &ia[nia], &ia[ma], &mxa,
		      &ib[nib], &mbb, &mxb);
		mb = mbb;
		goto L13;
	    }

/* then the left son */
	    inh = il[inh];
	}

	do {
	    inh = il[inh];
	    ++ma;
	    // Next two lines swapped in R 4.0.0 as nia is not used in Finis.
	    if (ma == mp1) goto Finis;
	    nia -= ia[ma];
	} while(ia[ma] == 0);
	ilinh = il[inh];
/* on the right side of the tree, the left son of a left son */
/* must represent a subset of points which is inside a */
/* triangle with vertices which are also vertices of the */
/* convex polygon and hence the subset may be neglected. */
	split(*n, &x[x_offset], ia[ma], &ia[nia],
	      ih[inh], ih[ilinh],
	      -2,
	      &ia[nia], &mbb, &mxa,
	      &ib[nib], &mb, &mxb);
    } while(true);

/* -------------------------------------------------------------- */

 L_vertical:/* all the points lie on a vertical line */

    kx = in[1];
    kn = in[1];
    for (i = 1; i <= *m; ++i) {
	j = in[i];
	if (y(j) > y(kx)) {
	    mx = i;
	    kx = j;
	}
	if (y(j) < y(kn)) {
	    min = i;
	    kn = j;
	}
    }
    if (kx == kn) goto L_1pt;

 L_2pts:/* only two points */
    ih[1] = kx;
    ih[2] = kn;
    if (x[kn] == x[kx] && y(kn) == y(kx))
	*nh = 2;
    else
	*nh = 3;
    goto Finis;

 L_1pt:/* only one point */
    *nh = 2;
    ih[1] = in[1];
    il[1] = 1;

 Finis:
    --(*nh);
    /* put the results in order, as given by IH */
    for (i = 1; i <= *nh; ++i) {
	ia[i] = ih[i];
    }
    j = il[1];
    for (i = 2; i <= *nh; ++i) {
	ih[i] = ia[j];
	j = il[j];
    }
    return;

#undef y
} /* chull */

#include <Rinternals.h>
SEXP chull(SEXP x)
{
    // x is a two-column matrix
    int n = nrows(x), nh;
    int *in = (int*)R_alloc(n, sizeof(int));
    for (int i = 0; i < n; i++) in[i] = i+1;
    int *ih = (int*)R_alloc(4*n, sizeof(int));
    x = PROTECT(coerceVector(x, REALSXP));
    in_chull(&n, REAL(x), &n, in, ih+n, ih+2*n, ih, &nh, ih+3*n);
    SEXP ans = allocVector(INTSXP, nh);
    int *ians = INTEGER(ans);
    for (int i = 0; i < nh; i++) ians[i] = ih[nh - 1 -i];
    UNPROTECT(1);
    return ans;
}
