//
// Convex hulls by Andrew's monotone chain algorithm.
//
// For a description of the algorithm, see
// https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169
//
//
// The implementation is designed for use with a garbage collector.
// In other words, some of the memory is allowed to leak.
//
// Sometimes, where it is slightly easier if one does so, we do try to
// free memory. Boehm GC sometimes cannot free allocated memory
// itself, so perhaps it is just as well if an ATS program explicitly
// frees some of its memory.
//

#include "share/atspre_staload.hats"

#define NIL list_nil ()
#define ::  list_cons

(* Make the floating point type big, so use of a boxed representation
   for points makes some sense. *)
typedef floatpt = ldouble

extern castfn int2floatpt : int -<> floatpt
overload i2fp with int2floatpt

(*------------------------------------------------------------------*)
//
// Enough plane geometry for our purpose.
//

(* Let us make the point type boxed. Here is one way to do that. The
   name of the type will be "plane_point_t". The constructor will be
   named "plane_point". *)
datatype plane_point_t =
| plane_point of (floatpt, floatpt)

fn {}
plane_point_x (p : plane_point_t) :<> floatpt =
  let
    val+ plane_point (x, _) = p
  in
    x
  end

fn {}
plane_point_y (p : plane_point_t) :<> floatpt =
  let
    val+ plane_point (_, y) = p
  in
    y
  end

fn {}
plane_point_eq (p : plane_point_t,
                q : plane_point_t) :<> bool =
  let
    val+ plane_point (xp, yp) = p
    val+ plane_point (xq, yq) = q
  in
    xp = xq && yp = yq
  end

(* Impose a total order on points, making it one that will work for
   Andrew's monotone chain algorithm. *)
fn {}
plane_point_order (p : plane_point_t,
                   q : plane_point_t) :<> bool =
  let
    val+ plane_point (xp, yp) = p
    val+ plane_point (xq, yq) = q
  in
    xp < xq || (xp = xq && yp < yq)
  end

(* Subtraction is really a vector or multivector operation. *)
fn {}
plane_point_sub (p : plane_point_t,
                 q : plane_point_t) :<> plane_point_t =
  let
    val+ plane_point (xp, yp) = p
    val+ plane_point (xq, yq) = q
  in
    plane_point (xp - xq, yp - yq)
  end

(* Cross product is really a multivector operation. *)
fn {}
plane_point_cross (p : plane_point_t,
                   q : plane_point_t) :<> floatpt =
  let
    val+ plane_point (xp, yp) = p
    val+ plane_point (xq, yq) = q
  in
    (xp * yq) - (yp * xq)
  end

overload .x with plane_point_x
overload .y with plane_point_y
overload = with plane_point_eq
overload order with plane_point_order
overload < with plane_point_order
overload - with plane_point_sub

(* Make "cross" a left-associative infix operator with the same
   precedence as "*". *)
infixl ( * ) cross
overload cross with plane_point_cross

(*------------------------------------------------------------------*)
//
// Sorting an array of points.
//

fn
plane_point_array_sort
          {n   : int}
          (arr : &(@[plane_point_t][n]) >> _,
           n   : size_t n) :<!wrt> void =
  let
    (* The comparison will be inlined by template expansion. *)
    implement
    array_quicksort$cmp<plane_point_t> (p, q) =
      if order (p, q) then    (* An overload for plane_point_order. *)
        ~1
      else if q < p then (* Another overload for plane_point_order. *)
        1
      else
        0
  in
    (* The following sort routine is in the ATS2 prelude. *)
    array_quicksort<plane_point_t> (arr, n)
  end

(*------------------------------------------------------------------*)
//
// Removing duplicates from a sorted array. Returns a new array.
//

extern fun {a : [email protected]}
array_delete_neighbor_dups$eq (p : a, q : a) :<> bool

fn {a : [email protected]}
array_delete_neighbor_dups
          {n   : int}
          (arr : &(@[a][n]),
           n   : size_t n)
     :<!wrt> [m       : nat | m <= n]
             [parr1   : addr]
             @(@[a][m] @ parr1,
               mfree_gc_v parr1 |
               ptr parr1,
               size_t m) =
  let
    macdef eq = array_delete_neighbor_dups$eq<a>

    fn
    nondups_list {n   : int}
                 (arr : &(@[a][n]),
                  n   : size_t n)
        :<> [m : nat | m <= n] list (a, m) =
      let
        fun
        loop {i : nat | i < n}
             {k : nat | k < n - i}
             .<i>.              (* <-- proof of termination. *)
             (arr : &(@[a][n]),
              i   : size_t i,
              lst : list (a, k))
            :<> [m : nat | m <= n] list (a, m) =
          (* Cons a list of non-duplicates, going backwards through
             the array so the list will be in forwards order. (The
             order does not really matter in ATS, though, because in
             the prelude there are both array_initize_list *and*
             array_initize_rlist. *)
          if i = i2sz 0 then
            arr[i] :: lst
          (* The "\" in the following line makes eq temporarily
             infix. *)
          else if arr[i - 1] \eq arr[i] then
            loop (arr, pred i, lst)
          else
            loop (arr, pred i, arr[i] :: lst)

        prval () = lemma_array_param arr
      in
        if n = i2sz 0 then
          NIL
        else
          loop (arr, pred n, NIL)
      end

    val lst = nondups_list (arr, n)
    prval () = lemma_list_param lst
    val m = i2sz (length lst)

    val @(pfarr1, pfgc1 | parr1) = array_ptr_alloc<a> (m)
    val () = array_initize_list<a> (!parr1, sz2i m, lst)
  in
    @(pfarr1, pfgc1 | parr1, m)
  end

(*------------------------------------------------------------------*)
//
// Removing duplicates from a sorted plane_point_t array. Returns a
// new array.
//

fn
plane_point_array_delete_neighbor_dups
          {n  : int}
          (arr : &(@[plane_point_t][n]),
           n   : size_t n)
     :<!wrt> [m       : nat | m <= n]
             [parr1   : addr]
             @(@[plane_point_t][m] @ parr1,
               mfree_gc_v parr1 |
               ptr parr1,
               size_t m) =
  let
    implement
    array_delete_neighbor_dups$eq<plane_point_t> (p, q) =
      p = q            (* Here = is an overload for plane_point_eq. *)
  in
    array_delete_neighbor_dups<plane_point_t> (arr, n)
  end

(*------------------------------------------------------------------*)
//
// The convex hull algorithm.
//

fn
cross_test {m, k : int | 1 <= k; k < m}
           (pt_i : plane_point_t,
            hull : &(@[plane_point_t][m]),
            k    : size_t k) :<> bool =
  let
    val hull_k = hull[k]
    and hull_k1 = hull[k - 1]
  in
    i2fp 0 < (hull_k - hull_k1) cross (pt_i - hull_k1)
  end

fn
construct_lower_hull {n  : int | 2 <= n}
                     (pt : &(@[plane_point_t][n]),
                      n  : size_t n)
    :<!wrt> [m     : int | 2 <= m; m <= n]
            [phull : addr]
            @(@[plane_point_t][n] @ phull,
              mfree_gc_v phull |
              ptr phull,
              size_t m) =
  let
    val @(pfhull, pfgc | phull) = array_ptr_alloc<plane_point_t> n

    (* It is easier to work with an array if it is fully
       initialized. (Yes, there are also ways to cheat and so merely
       pretend the array has been initialized.)  *)
    val arbitrary_point = plane_point (i2fp 0, i2fp 0)
    val () = array_initize_elt<plane_point_t> (!phull, n,
                                               arbitrary_point)

    (* The macro "hull" can be used with index notation "[]". *)
    macdef hull = !phull

    val () = hull[0] := pt[0]
    val () = hull[1] := pt[1]

    fun
    outer_loop {i    : int | 0 <= i; i <= n}
               {j    : int | 1 <= j; j < n}
               .<n - i>.        (* <-- proof of termination. *)
               (pt   : &(@[plane_point_t][n]),
                hull : &(@[plane_point_t][n]),
                i    : size_t i,
                j    : size_t j)
        :<!wrt> [m : int | 2 <= m; m <= n] size_t m =
      if i = n then
        succ j
      else
        let
          val pt_i = pt[i]

          fun
          inner_loop {k : int | 0 <= k; k < n - 1}
                     .<k>.      (* <-- proof of termination. *)
                     (hull : &(@[plane_point_t][n]),
                      k    : size_t k)
              :<!wrt> [j : int | 1 <= j; j < n] size_t j =
            if k = i2sz 0 then
              begin
                hull[succ k] := pt_i;
                succ k
              end
            else if cross_test (pt_i, hull, k) then
              begin
                hull[succ k] := pt_i;
                succ k
              end
            else
              inner_loop (hull, pred k)

          (* I do not know how to write a proof of the following, so
             let us test it at runtime. *)
          val () = $effmask_exn assertloc (j < n - 1)
        in
          outer_loop (pt, hull, succ i, inner_loop (hull, j))
        end

    val hull_size = outer_loop (pt, hull, i2sz 2, i2sz 1)
  in
    @(pfhull, pfgc | phull, hull_size)
  end

fn
construct_upper_hull {n  : int | 2 <= n}
                     (pt : &(@[plane_point_t][n]),
                      n  : size_t n)
    :<!wrt> [m     : int | 2 <= m; m <= n]
            [phull : addr]
            @(@[plane_point_t][n] @ phull,
              mfree_gc_v phull |
              ptr phull,
              size_t m) =
  let
    val @(pfhull, pfgc | phull) = array_ptr_alloc<plane_point_t> n

    (* It is easier to work with an array if it is fully
       initialized. (Yes, there are also ways to cheat and so merely
       pretend the array has been initialized.)  *)
    val arbitrary_point = plane_point (i2fp 0, i2fp 0)
    val () = array_initize_elt<plane_point_t> (!phull, n,
                                               arbitrary_point)

    (* The macro "hull" can be used with index notation "[]". *)
    macdef hull = !phull

    val () = hull[0] := pt[n - 1]
    val () = hull[1] := pt[n - 2]

    fun
    outer_loop {i1   : int | 0 <= i1; i1 <= n - 2}
               {j    : int | 1 <= j; j < n}
               .<i1>.           (* <-- proof of termination. *)
               (pt   : &(@[plane_point_t][n]),
                hull : &(@[plane_point_t][n]),
                i1   : size_t i1,
                j    : size_t j)
        :<!wrt> [m : int | 2 <= m; m <= n] size_t m =
      if i1 = i2sz 0 then
        succ j
      else
        let
          val i = pred i1
          val pt_i = pt[i]

          fun
          inner_loop {k : int | 0 <= k; k < n - 1}
                     .<k>.      (* <-- proof of termination. *)
                     (hull : &(@[plane_point_t][n]),
                      k    : size_t k)
              :<!wrt> [j : int | 1 <= j; j < n] size_t j =
            if k = i2sz 0 then
              begin
                hull[succ k] := pt_i;
                succ k
              end
            else if cross_test (pt_i, hull, k) then
              begin
                hull[succ k] := pt_i;
                succ k
              end
            else
              inner_loop (hull, pred k)

          (* I do not know how to write a proof of the following, so
             let us test it at runtime. *)
          val () = $effmask_exn assertloc (j < n - 1)
        in
          outer_loop (pt, hull, pred i1, inner_loop (hull, j))
        end

    val hull_size = outer_loop (pt, hull, n - i2sz 2, i2sz 1)
  in
    @(pfhull, pfgc | phull, hull_size)
  end

fn
construct_hull {n  : int | 2 <= n}
               (pt : &(@[plane_point_t][n]),
                n  : size_t n)
    :<!wrt> [hull_size : int | 2 <= hull_size]
            [phull     : addr]
            @(@[plane_point_t][hull_size] @ phull,
              mfree_gc_v phull |
              ptr phull,
              size_t hull_size) =

  (* The following implementation demonstrates slightly complicated
     "safe" initialization. By "safe" I mean so one never *reads* from
     an uninitialized entry. Elsewhere in the program I did this more
     simply, with prelude routines.

     I also demonstrate freeing of a linear object. If you remove the
     calls to array_ptr_free, you cannot compile the program. *)

  let
    (* Side note: Construction of the lower and upper hulls can be
       done in parallel. *)
    val [lower_hull_size : int]
        [p_lower_hull : addr]
        @(pf_lower_hull, pfgc_lower | p_lower_hull, lower_hull_size) =
      construct_lower_hull (pt, n)
    and [upper_hull_size : int]
        [p_upper_hull : addr]
        @(pf_upper_hull, pfgc_upper | p_upper_hull, upper_hull_size) =
      construct_upper_hull (pt, n)

    stadef hull_size = lower_hull_size + upper_hull_size - 2
    val hull_size : size_t hull_size =
      lower_hull_size + upper_hull_size - i2sz 2

    val [phull : addr] @(pfhull, pfgc_hull | phull) =
      array_ptr_alloc<plane_point_t> hull_size

    (* Split off the part of each partial hull's view that we actually
       will use. *)
    prval @(pf_lower, pf_lower_rest) =
      array_v_split {plane_point_t} {p_lower_hull} {n}
                    {lower_hull_size - 1}
                    pf_lower_hull
    prval @(pf_upper, pf_upper_rest) =
      array_v_split {plane_point_t} {p_upper_hull} {n}
                    {upper_hull_size - 1}
                    pf_upper_hull

    (* Split the new array's view in two. The question mark means
       that the array elements are uninitialized. *)
    prval @(pfleft, pfright) =
      array_v_split {plane_point_t?} {phull} {hull_size}
                    {lower_hull_size - 1}
                    pfhull

    (* Copy the lower hull, thus initializing the left side of the new
       array. *)
    val () = array_copy<plane_point_t> (!phull, !p_lower_hull,
                                        pred lower_hull_size)

    (* Copy the upper hull, thus initializing the left side of the new
       array. *)
    val phull_right =
      ptr_add<plane_point_t> (phull, pred lower_hull_size)
    val () = array_copy<plane_point_t> (!phull_right, !p_upper_hull,
                                        pred upper_hull_size)

    (* Join the views of the initialized halves. *)
    prval pfhull = array_v_unsplit (pfleft, pfright)

    (* Restore the views of the partial hulls. *)
    prval () = pf_lower_hull :=
      array_v_unsplit (pf_lower, pf_lower_rest)
    prval () = pf_upper_hull :=
      array_v_unsplit (pf_upper, pf_upper_rest)

    (* We do not need the lower and upper hulls anymore, and so can
       free them. (Of course, if there is a garbage collector, you
       could make freeing be a no-operation.) *)
    val () = array_ptr_free (pf_lower_hull, pfgc_lower | p_lower_hull)
    val () = array_ptr_free (pf_upper_hull, pfgc_upper | p_upper_hull)
  in
    @(pfhull, pfgc_hull | phull, hull_size)
  end

fn
plane_convex_hull {num_points : int}
                  (points_lst : list (plane_point_t, num_points))
    :<!wrt> [hull_size : int | 0 <= hull_size]
            [phull     : addr]
            @(@[plane_point_t][hull_size] @ phull,
              mfree_gc_v phull |
              ptr phull,
              size_t hull_size) =
  (* Takes an arbitrary list of points, which may be in any order and
     may contain duplicates. Returns an ordered array of points that
     make up the convex hull. If the initial list of points is empty,
     the returned array is empty. *)
  let
    prval () = lemma_list_param points_lst
    val num_points = i2sz (length points_lst)

    (* Copy the list to an array. *)
    val @(pf_points, pfgc_points | p_points) =
      array_ptr_alloc<plane_point_t> num_points
    val () =
      array_initize_list<plane_point_t> (!p_points, sz2i num_points,
                                         points_lst)

    (* Sort the array. *)
    val () = plane_point_array_sort (!p_points, num_points)

    (* Create a new sorted array that has the duplicates removed. *)
    val @(pf_pt, pfgc_pt | p_pt, n) =
      plane_point_array_delete_neighbor_dups (!p_points, num_points)

    (* The original array no longer is needed. *)
    val () = array_ptr_free (pf_points, pfgc_points | p_points)
  in
    if n <= 2 then
      @(pf_pt, pfgc_pt | p_pt, n)
    else
      let
        val @(pfhull, pfgc_hull | phull, hull_size) =
          construct_hull (!p_pt, n)
        val () = array_ptr_free (pf_pt, pfgc_pt | p_pt)
      in
        @(pfhull, pfgc_hull | phull, hull_size)
      end
  end

(*------------------------------------------------------------------*)

implement
main0 () =
  {
    val example_points =
      $list (plane_point (i2fp 16, i2fp 3),
             plane_point (i2fp 12, i2fp 17),
             plane_point (i2fp 0, i2fp 6),
             plane_point (i2fp ~4, i2fp ~6),
             plane_point (i2fp 16, i2fp 6),
             plane_point (i2fp 16, i2fp ~7),
             plane_point (i2fp 16, i2fp ~3),
             plane_point (i2fp 17, i2fp ~4),
             plane_point (i2fp 5, i2fp 19),
             plane_point (i2fp 19, i2fp ~8),
             plane_point (i2fp 3, i2fp 16),
             plane_point (i2fp 12, i2fp 13),
             plane_point (i2fp 3, i2fp ~4),
             plane_point (i2fp 17, i2fp 5),
             plane_point (i2fp ~3, i2fp 15),
             plane_point (i2fp ~3, i2fp ~9),
             plane_point (i2fp 0, i2fp 11),
             plane_point (i2fp ~9, i2fp ~3),
             plane_point (i2fp ~4, i2fp ~2),
             plane_point (i2fp 12, i2fp 10))

    val (pf_hull, pfgc_hull | p_hull, hull_size) =
      plane_convex_hull example_points

    macdef hull = !p_hull

    val () =
      let
        var i : [i : nat] size_t i
      in
        for (i := i2sz 0; i < hull_size; i := succ i)
          println! ("(", hull[i].x(), " ", hull[i].y(), ")")
      end

    val () = array_ptr_free (pf_hull, pfgc_hull | p_hull)
  }

(*------------------------------------------------------------------*)


#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct tPoint {
    int x, y;
} Point;

bool ccw(const Point *a, const Point *b, const Point *c) {
    return (b->x - a->x) * (c->y - a->y)
         > (b->y - a->y) * (c->x - a->x);
}

int comparePoints(const void *lhs, const void *rhs) {
    const Point* lp = lhs;
    const Point* rp = rhs;
    if (lp->x < rp->x)
        return -1;
    if (rp->x < lp->x)
        return 1;
    if (lp->y < rp->y)
        return -1;
    if (rp->y < lp->y)
        return 1;
    return 0;
}

void fatal(const char* message) {
    fprintf(stderr, "%s\n", message);
    exit(1);
}

void* xmalloc(size_t n) {
    void* ptr = malloc(n);
    if (ptr == NULL)
        fatal("Out of memory");
    return ptr;
}

void* xrealloc(void* p, size_t n) {
    void* ptr = realloc(p, n);
    if (ptr == NULL)
        fatal("Out of memory");
    return ptr;
}

void printPoints(const Point* points, int len) {
    printf("[");
    if (len > 0) {
        const Point* ptr = points;
        const Point* end = points + len;
        printf("(%d, %d)", ptr->x, ptr->y);
        ++ptr;
        for (; ptr < end; ++ptr)
            printf(", (%d, %d)", ptr->x, ptr->y);
    }
    printf("]");
}

Point* convexHull(Point p[], int len, int* hsize) {
    if (len == 0) {
        *hsize = 0;
        return NULL;
    }

    int i, size = 0, capacity = 4;
    Point* hull = xmalloc(capacity * sizeof(Point));

    qsort(p, len, sizeof(Point), comparePoints);

    /* lower hull */
    for (i = 0; i < len; ++i) {
        while (size >= 2 && !ccw(&hull[size - 2], &hull[size - 1], &p[i]))
            --size;
        if (size == capacity) {
            capacity *= 2;
            hull = xrealloc(hull, capacity * sizeof(Point));
        }
        assert(size >= 0 && size < capacity);
        hull[size++] = p[i];
    }

    /* upper hull */
    int t = size + 1;
    for (i = len - 1; i >= 0; i--) {
        while (size >= t && !ccw(&hull[size - 2], &hull[size - 1], &p[i]))
            --size;
        if (size == capacity) {
            capacity *= 2;
            hull = xrealloc(hull, capacity * sizeof(Point));
        }
        assert(size >= 0 && size < capacity);
        hull[size++] = p[i];
    }
    --size;
    assert(size >= 0);
    hull = xrealloc(hull, size * sizeof(Point));
    *hsize = size;
    return hull;
}

int main() {
    Point points[] = {
        {16,  3}, {12, 17}, { 0,  6}, {-4, -6}, {16,  6},
        {16, -7}, {16, -3}, {17, -4}, { 5, 19}, {19, -8},
        { 3, 16}, {12, 13}, { 3, -4}, {17,  5}, {-3, 15},
        {-3, -9}, { 0, 11}, {-9, -3}, {-4, -2}, {12, 10}
    };
    int hsize;
    Point* hull = convexHull(points, sizeof(points)/sizeof(Point), &hsize);
    printf("Convex Hull: ");
    printPoints(hull, hsize);
    printf("\n");
    free(hull);

    return 0;
}


using System;
using System.Collections.Generic;

namespace ConvexHull {
    class Point : IComparable<Point> {
        private int x, y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int X { get => x; set => x = value; }
        public int Y { get => y; set => y = value; }

        public int CompareTo(Point other) {
            return x.CompareTo(other.x);
        }

        public override string ToString() {
            return string.Format("({0}, {1})", x, y);
        }
    }

    class Program {
        private static List<Point> ConvexHull(List<Point> p) {
            if (p.Count == 0) return new List<Point>();
            p.Sort();
            List<Point> h = new List<Point>();

            // lower hull
            foreach (var pt in p) {
                while (h.Count >= 2 && !Ccw(h[h.Count - 2], h[h.Count - 1], pt)) {
                    h.RemoveAt(h.Count - 1);
                }
                h.Add(pt);
            }

            // upper hull
            int t = h.Count + 1;
            for (int i = p.Count - 1; i >= 0; i--) {
                Point pt = p[i];
                while (h.Count >= t && !Ccw(h[h.Count - 2], h[h.Count - 1], pt)) {
                    h.RemoveAt(h.Count - 1);
                }
                h.Add(pt);
            }

            h.RemoveAt(h.Count - 1);
            return h;
        }

        private static bool Ccw(Point a, Point b, Point c) {
            return ((b.X - a.X) * (c.Y - a.Y)) > ((b.Y - a.Y) * (c.X - a.X));
        }

        static void Main(string[] args) {
            List<Point> points = new List<Point>() {
                new Point(16, 3),
                new Point(12, 17),
                new Point(0, 6),
                new Point(-4, -6),
                new Point(16, 6),

                new Point(16, -7),
                new Point(16, -3),
                new Point(17, -4),
                new Point(5, 19),
                new Point(19, -8),

                new Point(3, 16),
                new Point(12, 13),
                new Point(3, -4),
                new Point(17, 5),
                new Point(-3, 15),

                new Point(-3, -9),
                new Point(0, 11),
                new Point(-9, -3),
                new Point(-4, -2),
                new Point(12, 10)
            };

            List<Point> hull = ConvexHull(points);
            Console.Write("Convex Hull: [");
            for (int i = 0; i < hull.Count; i++) {
                if (i > 0) {
                    Console.Write(", ");
                }
                Point pt = hull[i];
                Console.Write(pt);
            }
            Console.WriteLine("]");
        }
    }
}


#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
#include <tuple>

typedef std::tuple<int, int> point;

std::ostream& print(std::ostream& os, const point& p) {
    return os << "(" << std::get<0>(p) << ", " << std::get<1>(p) << ")";
}

std::ostream& print(std::ostream& os, const std::vector<point>& v) {
    auto it = v.cbegin();
    auto end = v.cend();

    os << "[";

    if (it != end) {
        print(os, *it);
        it = std::next(it);
    }
    while (it != end) {
        os << ", ";
        print(os, *it);
        it = std::next(it);
    }

    return os << "]";
}

// returns true if the three points make a counter-clockwise turn
bool ccw(const point& a, const point& b, const point& c) {
    return ((std::get<0>(b) - std::get<0>(a)) * (std::get<1>(c) - std::get<1>(a)))
         > ((std::get<1>(b) - std::get<1>(a)) * (std::get<0>(c) - std::get<0>(a)));
}

std::vector<point> convexHull(std::vector<point> p) {
    if (p.size() == 0) return std::vector<point>();
    std::sort(p.begin(), p.end(), [](point& a, point& b){
        if (std::get<0>(a) < std::get<0>(b)) return true;
        return false;
    });

    std::vector<point> h;

    // lower hull
    for (const auto& pt : p) {
        while (h.size() >= 2 && !ccw(h.at(h.size() - 2), h.at(h.size() - 1), pt)) {
            h.pop_back();
        }
        h.push_back(pt);
    }

    // upper hull
    auto t = h.size() + 1;
    for (auto it = p.crbegin(); it != p.crend(); it = std::next(it)) {
        auto pt = *it;
        while (h.size() >= t && !ccw(h.at(h.size() - 2), h.at(h.size() - 1), pt)) {
            h.pop_back();
        }
        h.push_back(pt);
    }

    h.pop_back();
    return h;
}

int main() {
    using namespace std;

    vector<point> points = {
        make_pair(16, 3),  make_pair(12, 17), make_pair(0,  6),  make_pair(-4, -6), make_pair(16,  6),
        make_pair(16, -7), make_pair(16, -3), make_pair(17, -4), make_pair(5, 19),  make_pair(19, -8),
        make_pair(3, 16),  make_pair(12, 13), make_pair(3, -4),  make_pair(17,  5), make_pair(-3, 15),
        make_pair(-3, -9), make_pair(0, 11),  make_pair(-9, -3), make_pair(-4, -2), make_pair(12, 10)
    };

    auto hull = convexHull(points);
    auto it = hull.cbegin();
    auto end = hull.cend();

    cout << "Convex Hull: ";
    print(cout, hull);
    cout << endl;

    return 0;
}


#!/bin/sh
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "[email protected]"
|#
(progn ;;init forms
  (ros:ensure-asdf)
  #+quicklisp(ql:quickload '() :silent t)
  )

(defpackage :ros.script.convex-hull-task.3861520611
  (:use :cl))
(in-package :ros.script.convex-hull-task.3861520611)

;;;
;;; Convex hulls by Andrew's monotone chain algorithm.
;;;
;;; For a description of the algorithm, see
;;; https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169
;;;
;;; This program is translated rather faithfully from the Scheme,
;;; complete with tail recursions.
;;;

;; x and y coordinates of a "point". A "point" is represented by a
;; list of length 2.
(defun [email protected] (u) (car u))
(defun [email protected] (u) (cadr u))

(defun cross (u v)
  ;; Cross product (as a signed scalar).
  (- (* ([email protected] u) ([email protected] v)) (* ([email protected] u) ([email protected] v))))

(defun point- (u v)
  (list (- ([email protected] u) ([email protected] v)) (- ([email protected] u) ([email protected] v))))

(defun sort-points-vector (points-vector)
  ;; Ascending sort on x-coordinates, followed by ascending sort
  ;; on y-coordinates.
  (sort points-vector #'(lambda (u v)
                          (or (< ([email protected] u) ([email protected] v))
                              (and (= ([email protected] u) ([email protected] v))
                                   (< ([email protected] u) ([email protected] v)))))))

(defun construct-lower-hull (sorted-points-vector)
  (let* ((pt sorted-points-vector)
         (n (length pt))
         (hull (make-array n))
         (j 1))
    (setf (aref hull 0) (aref pt 0))
    (setf (aref hull 1) (aref pt 1))
    (loop for i from 2 to (1- n)
          do (progn
               (defun inner-loop ()
                 (if (or (zerop j)
                         (plusp
                          (cross (point- (aref hull j)
                                         (aref hull (1- j)))
                                 (point- (aref pt i)
                                         (aref hull (1- j))))))
                     (progn
                       (setf j (1+ j))
                       (setf (aref hull j) (aref pt i)))
                     (progn
                       (setf j (1- j))
                       (inner-loop))))
               (inner-loop)))
    (values (+ j 1) hull)))             ; Hull size, hull points.

(defun construct-upper-hull (sorted-points-vector)
  (let* ((pt sorted-points-vector)
         (n (length pt))
         (hull (make-array n))
         (j 1))
    (setf (aref hull 0) (aref pt (- n 1)))
    (setf (aref hull 1) (aref pt (- n 2)))
    (loop for i from (- n 3) downto 0
          do (progn
               (defun inner-loop ()
                 (if (or (zerop j)
                         (plusp
                          (cross (point- (aref hull j)
                                         (aref hull (1- j)))
                                 (point- (aref pt i)
                                         (aref hull (1- j))))))
                     (progn
                       (setf j (1+ j))
                       (setf (aref hull j) (aref pt i)))
                     (progn
                       (setf j (1- j))
                       (inner-loop))))
               (inner-loop)))
    (values (+ j 1) hull)))             ; Hull size, hull points.

(defun construct-hull (sorted-points-vector)
  ;; Notice that the lower and upper hulls could be constructed in
  ;; parallel. (The Scheme "let-values" macro made this apparent,
  ;; despite not actually doing the computation in parallel. The
  ;; coding here makes it less obvious.)
  (multiple-value-bind (lower-hull-size lower-hull)
      (construct-lower-hull sorted-points-vector)
    (multiple-value-bind (upper-hull-size upper-hull)
        (construct-upper-hull sorted-points-vector)
      (let* ((hull-size (+ lower-hull-size upper-hull-size -2))
             (hull (make-array hull-size)))
        (loop for i from 0 to (- lower-hull-size 2)
              do (setf (aref hull i) (aref lower-hull i)))
        (loop for i from 0 to (- upper-hull-size 2)
              do (setf (aref hull (+ i (1- lower-hull-size)))
                       (aref upper-hull i)))
        hull))))

(defun vector-delete-neighbor-dups (elt= v)
  ;; A partial clone of the SRFI-132 procedure of the same name. This
  ;; implementation is similar to the reference implementation for
  ;; SRFI-132, and may use a bunch of stack space.  That reference
  ;; implementation is by Olin Shivers and rests here:
  ;; https://github.com/scheme-requests-for-implementation/srfi-132/blob/master/sorting/delndups.scm
  ;; The license is:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code is
;;;     Copyright (c) 1998 by Olin Shivers.
;;; The terms are: You may do as you please with this code, as long as
;;; you do not delete this notice or hold me responsible for any outcome
;;; related to its use.
;;;
;;; Blah blah blah. Don't you think source files should contain more lines
;;; of code than copyright notice?
;;;
  (let ((start 0)
        (end (length v)))
    (let ((x (aref v start)))
      (defun recur (x i j)
        (if (< i end)
            (let ((y (aref v i))
                  (nexti (1+ i)))
              (if (funcall elt= x y)
                  (recur x nexti j)
                  (let ((ansvec (recur y nexti (1+ j))))
                    (setf (aref ansvec j) y)
                    ansvec)))
            (make-array j)))
      (let ((ans (recur x start 1)))
        (setf (aref ans 0) x)
        ans))))

(defun vector-convex-hull (points)
  (let* ((points-vector (coerce points 'vector))
         (sorted-points-vector
           (vector-delete-neighbor-dups
            #'equalp
            (sort-points-vector points-vector))))
    (if (<= (length sorted-points-vector) 2)
        sorted-points-vector
        (construct-hull sorted-points-vector))))

(defun list-convex-hull (points)
  (coerce (vector-convex-hull points) 'list))

(defconstant example-points
  '((16 3) (12 17) (0 6) (-4 -6) (16 6)
    (16 -7) (16 -3) (17 -4) (5 19) (19 -8)
    (3 16) (12 13) (3 -4) (17 5) (-3 15)
    (-3 -9) (0 11) (-9 -3) (-4 -2) (12 10)))

(defun main (&rest argv)
  (declare (ignorable argv))
  (write (list-convex-hull example-points))
  (terpri))

;;; vim: set ft=lisp lisp:


import std.algorithm.sorting;
import std.stdio;

struct Point {
    int x;
    int y;

    int opCmp(Point rhs) {
        if (x < rhs.x) return -1;
        if (rhs.x < x) return 1;
        return 0;
    }

    void toString(scope void delegate(const(char)[]) sink) const {
        import std.format;
        sink("(");
        formattedWrite(sink, "%d", x);
        sink(",");
        formattedWrite(sink, "%d", y);
        sink(")");
    }
}

Point[] convexHull(Point[] p) {
    if (p.length == 0) return [];
    p.sort;
    Point[] h;

    // lower hull
    foreach (pt; p) {
        while (h.length >= 2 && !ccw(h[$-2], h[$-1], pt)) {
            h.length--;
        }
        h ~= pt;
    }

    // upper hull
    auto t = h.length + 1;
    foreach_reverse (i; 0..(p.length - 1)) {
        auto pt = p[i];
        while (h.length >= t && !ccw(h[$-2], h[$-1], pt)) {
            h.length--;
        }
        h ~= pt;
    }

    h.length--;
    return h;
}

/* ccw returns true if the three points make a counter-clockwise turn */
auto ccw(Point a, Point b, Point c) {
    return ((b.x - a.x) * (c.y - a.y)) > ((b.y - a.y) * (c.x - a.x));
}

void main() {
    auto points = [
        Point(16,  3), Point(12, 17), Point( 0,  6), Point(-4, -6), Point(16,  6),
        Point(16, -7), Point(16, -3), Point(17, -4), Point( 5, 19), Point(19, -8),
        Point( 3, 16), Point(12, 13), Point( 3, -4), Point(17,  5), Point(-3, 15),
        Point(-3, -9), Point( 0, 11), Point(-9, -3), Point(-4, -2), Point(12, 10)
    ];
    auto hull = convexHull(points);
    writeln("Convex Hull: ", hull);
}


program ConvexHulls;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Types,
  System.SysUtils,
  System.Generics.Defaults,
  System.Generics.Collections;

  function Ccw(const a, b, c: TPoint): Boolean;
  begin
    Result := ((b.X - a.X) * (c.Y - a.Y)) > ((b.Y - a.Y) * (c.X - a.X));
  end;

  function ConvexHull(const p: TList<TPoint>): TList<TPoint>;
  var
    pt: TPoint;
    i, t: Integer;
  begin
    Result := TList<TPoint>.Create;

    if (p.Count = 0) then Exit;

    p.Sort(TComparer<TPoint>.Construct(
          function(const Left, Right: TPoint): Integer
          begin
            Result := Left.X - Right.X;
          end
    ));

    // lower hull
    for i := 0 to p.Count-1 do
    begin
      pt := p[i];
      while ((Result.Count >= 2) and (not Ccw(Result[Result.Count - 2], Result[Result.Count - 1], pt))) do
      begin
        Result.Delete(Result.Count - 1);
      end;
      Result.Add(pt);
    end;

    // upper hull
    t := Result.Count + 1;
    for i := p.Count-1 downto 0 do
    begin
      pt := p[i];
      while ((Result.Count >= t) and (not Ccw(Result[Result.Count - 2], Result[Result.Count - 1], pt))) do
      begin
        Result.Delete(Result.Count - 1);
      end;
      Result.Add(pt);
    end;

    Result.Delete(Result.Count - 1);
  end;

var
  points: TList<TPoint>;
  hull: TList<TPoint>;
  i: Integer;
begin

  hull := nil;
  points := TList<TPoint>.Create;
  try

    points.AddRange([
        Point(16, 3),
        Point(12, 17),
        Point(0, 6),
        Point(-4, -6),
        Point(16, 6),
        Point(16, -7),
        Point(16, -3),
        Point(17, -4),
        Point(5, 19),
        Point(19, -8),
        Point(3, 16),
        Point(12, 13),
        Point(3, -4),
        Point(17, 5),
        Point(-3, 15),
        Point(-3, -9),
        Point(0, 11),
        Point(-9, -3),
        Point(-4, -2),
        Point(12, 10)
        ]);

    hull := ConvexHull(points);

    // Output the result
    Write('Convex Hull: [');
    for i := 0 to hull.Count-1 do
    begin
      if (i > 0) then Write(', ');
      Write(Format('(%d, %d)', [hull[i].X, hull[i].Y]));
    end;
    WriteLn(']');

  finally
    hull.Free;
    points.Free;
  end;

end.


open System

type Point =
    struct
        val X : int
        val Y : int
        new (x : int, y : int ) = {X = x; Y = y}
    end

let (poly : Point list) = [ Point(16,  3); Point(12, 17); Point( 0,  6); Point(-4, -6); Point(16,  6);
                            Point(16, -7); Point(16, -3); Point(17, -4); Point( 5, 19); Point(19, -8);
                            Point( 3, 16); Point(12, 13); Point( 3, -4); Point(17,  5); Point(-3, 15);
                            Point(-3, -9); Point( 0, 11); Point(-9, -3); Point(-4, -2); Point(12, 10)]

let affiche (lst : Point list) =
    let mutable (str : string) = List.fold (fun  acc (p : Point) -> acc + sprintf "(%d, %d) " p.X p.Y) "Convex Hull: [" lst
    printfn "%s" (str.[0.. str.Length - 2] + "]")

let ccw (p1 : Point) (p2 : Point) (p3 : Point) =
    (p2.X - p1.X) * (p3.Y - p1.Y) > (p2.Y - p1.Y) * (p3.X - p1.X)

let convexHull (poly : Point list) =
    let mutable (outHull : Point list) = List.Empty
    let mutable (k : int) = 0

    for p in poly do
        while k >= 2 && not (ccw outHull.[k-2] outHull.[k-1] p) do
            k <- k - 1
        if k >= outHull.Length
        then outHull <- outHull @ [p]
        else outHull <- outHull.[0..k - 1] @ [p]
        k <- k + 1

    let (t : int) = k + 1
    for p in List.rev poly do
        while k >= t && not (ccw outHull.[k-2] outHull.[k-1] p) do
            k <- k - 1
        if k >= outHull.Length
        then outHull <- outHull @ [p]
        else outHull <- outHull.[0..k - 1] @ [p]
        k <- k + 1

    outHull.[0 .. k - 2]

affiche (convexHull (List.sortBy (fun (x : Point) -> x.X, x.Y) poly))


module convex_hulls
  !
  ! Convex hulls by Andrew's monotone chain algorithm.
  !
  ! For a description of the algorithm, see
  ! https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169
  !
  ! For brevity in the task, I shall use the built-in "complex" type
  ! to represent objects in the plane. One could have fun rewriting
  ! this implementation in terms of geometric algebra.
  !

  implicit none
  private

  public :: find_convex_hull

contains

  elemental function x (u)
    complex, intent(in) :: u
    real :: x

    x = real (u)
  end function x

  elemental function y (u)
    complex, intent(in) :: u
    real :: y

    y = aimag (u)
  end function y

  elemental function cross (u, v) result (p)
    complex, intent(in) :: u, v
    real :: p

    ! The cross product as a signed scalar.
    p = (x (u) * y (v)) - (y (u) * x (v))
  end function cross

  subroutine sort_points (num_points, points)
    integer, intent(in) :: num_points
    complex, intent(inout) :: points(0:*)

    ! Sort first in ascending order by x-coordinates, then in
    ! ascending order by y-coordinates. Any decent sort algorithm will
    ! suffice; for the sake of interest, here is the Shell sort of
    ! https://en.wikipedia.org/w/index.php?title=Shellsort&oldid=1084744510

    integer, parameter :: gaps(1:8) = (/ 701, 301, 132, 57, 23, 10, 4, 1 /)

    integer :: i, j, k, gap, offset
    complex :: temp
    logical :: done

    do k = 1, 8
       gap = gaps(k)
       do offset = 0, gap - 1
          do i = offset, num_points - 1, gap
             temp = points(i)
             j = i
             done = .false.
             do while (.not. done)
                if (j < gap) then
                   done = .true.
                else if (x (points(j - gap)) < x (temp)) then
                   done = .true.
                else if (x (points(j - gap)) == x (temp) .and. &
                     &    (y (points(j - gap)) <= y (temp))) then
                   done = .true.
                else
                   points(j) = points(j - gap)
                   j = j - gap
                end if
             end do
             points(j) = temp
          end do
       end do
    end do
  end subroutine sort_points

  subroutine delete_neighbor_duplicates (n, pt)
    integer, intent(inout) :: n
    complex, intent(inout) :: pt(0:*)

    call delete_trailing_duplicates
    call delete_nontrailing_duplicates

  contains

    subroutine delete_trailing_duplicates
      integer :: i
      logical :: done

      i = n - 1
      done = .false.
      do while (.not. done)
         if (i == 0) then
            n = 1
            done = .true.
         else if (pt(i - 1) /= pt(i)) then
            n = i + 1
            done = .true.
         else
            i = i - 1
         end if
      end do
    end subroutine delete_trailing_duplicates

    subroutine delete_nontrailing_duplicates
      integer :: i, j, num_deleted
      logical :: done

      i = 0
      do while (i < n - 1)
         j = i + 1
         done = .false.
         do while (.not. done)
            if (j == n) then
               done = .true.
            else if (pt(j) /= pt(i)) then
               done = .true.
            else
               j = j + 1
            end if
         end do
         if (j /= i + 1) then
            num_deleted = j - i - 1
            do while (j /= n)
               pt(j - num_deleted) = pt(j)
               j = j + 1
            end do
            n = n - num_deleted
         end if
         i = i + 1
      end do
    end subroutine delete_nontrailing_duplicates

  end subroutine delete_neighbor_duplicates

  subroutine construct_lower_hull (n, pt, hull_size, hull)
    integer, intent(in) :: n    ! Number of points.
    complex, intent(in) :: pt(0:*)
    integer, intent(inout) :: hull_size
    complex, intent(inout) :: hull(0:*)

    integer :: i, j
    logical :: done

    j = 1
    hull(0:1) = pt(0:1)
    do i = 2, n - 1
       done = .false.
       do while (.not. done)
          if (j == 0) then
             j = j + 1
             hull(j) = pt(i)
             done = .true.
          else if (0.0 < cross (hull(j) - hull(j - 1), &
               &                pt(i) - hull(j - 1))) then
             j = j + 1
             hull(j) = pt(i)
             done = .true.
          else
             j = j - 1
          end if
       end do
    end do
    hull_size = j + 1
  end subroutine construct_lower_hull

  subroutine construct_upper_hull (n, pt, hull_size, hull)
    integer, intent(in) :: n    ! Number of points.
    complex, intent(in) :: pt(0:*)
    integer, intent(inout) :: hull_size
    complex, intent(inout) :: hull(0:*)

    integer :: i, j
    logical :: done

    j = 1
    hull(0:1) = pt(n - 1 : n - 2 : -1)
    do i = n - 3, 0, -1
       done = .false.
       do while (.not. done)
          if (j == 0) then
             j = j + 1
             hull(j) = pt(i)
             done = .true.
          else if (0.0 < cross (hull(j) - hull(j - 1), &
               &                pt(i) - hull(j - 1))) then
             j = j + 1
             hull(j) = pt(i)
             done = .true.
          else
             j = j - 1
          end if
       end do
    end do
    hull_size = j + 1
  end subroutine construct_upper_hull

  subroutine contruct_hull (n, pt, hull_size, hull)
    integer, intent(in) :: n    ! Number of points.
    complex, intent(in) :: pt(0:*)
    integer, intent(inout) :: hull_size
    complex, intent(inout) :: hull(0:*)

    integer :: lower_hull_size, upper_hull_size
    complex :: lower_hull(0 : n - 1), upper_hull(0 : n - 1)
    integer :: ihull0

    ihull0 = lbound (hull, 1)

    ! A side note: the calls to construct_lower_hull and
    ! construct_upper_hull could be done in parallel.
    call construct_lower_hull (n, pt, lower_hull_size, lower_hull)
    call construct_upper_hull (n, pt, upper_hull_size, upper_hull)

    hull_size = lower_hull_size + upper_hull_size - 2

    hull(:ihull0 + lower_hull_size - 2) =                          &
         & lower_hull(:lower_hull_size - 2)
    hull(ihull0 + lower_hull_size - 1 : ihull0 + hull_size - 1) =  &
         & upper_hull(0 : upper_hull_size - 2)
  end subroutine contruct_hull

  subroutine find_convex_hull (n, points, hull_size, hull)
    integer, intent(in) :: n            ! Number of points.
    complex, intent(in) :: points(*)    ! Input points.
    integer, intent(inout) :: hull_size ! The size of the hull.
    complex, intent(inout) :: hull(*)   ! Points of the hull.

    !
    ! Yes, you can call this with something like
    !
    !    call find_convex_hull (n, points, n, points)
    !
    ! and in the program below I shall demonstrate that.
    !

    complex :: pt(0 : n - 1)
    integer :: ipoints0, ihull0, numpt

    ipoints0 = lbound (points, 1)
    ihull0 = lbound (hull, 1)

    pt = points(:ipoints0 + n - 1)
    numpt = n

    call sort_points (numpt, pt)
    call delete_neighbor_duplicates (numpt, pt)

    if (numpt == 0) then
       hull_size = 0
    else if (numpt <= 2) then
       hull_size = numpt
       hull(:ihull0 + numpt - 1) = pt(:numpt - 1)
    else
       call contruct_hull (numpt, pt, hull_size, hull)
    end if
  end subroutine find_convex_hull

end module convex_hulls

program convex_hull_task
  use, non_intrinsic :: convex_hulls
  implicit none

  complex, parameter :: example_points(20) =                   &
       & (/ (16, 3), (12, 17), (0, 6), (-4, -6), (16, 6),      &
       &    (16, -7), (16, -3), (17, -4), (5, 19), (19, -8),   &
       &    (3, 16), (12, 13), (3, -4), (17, 5), (-3, 15),     &
       &    (-3, -9), (0, 11), (-9, -3), (-4, -2), (12, 10) /)

  integer :: n, i
  complex :: points(0:100)
  character(len = 100) :: fmt

  n = 20
  points(1:n) = example_points
  call find_convex_hull (n, points(1:n), n, points(1:n))

  write (fmt, '("(", I20, ''("(", F3.0, 1X, F3.0, ") ")'', ")")') n
  write (*, fmt) (points(i), i = 1, n)

end program convex_hull_task


#include "crt.bi"
Screen 20
Window (-20,-20)-(30,30)

Type Point
    As Single x,y
    As Long done
End Type

#macro rotate(pivot,p,a,scale)
Type<Point>(scale*(Cos(a*.0174533)*(p.x-pivot.x)-Sin(a*.0174533)*(p.y-pivot.y))+pivot.x, _
scale*(Sin(a*.0174533)*(p.x-pivot.x)+Cos(a*.0174533)*(p.y-pivot.y))+pivot.y)
#endmacro

Dim As Point p(1 To ...)={(16,3),(12,17),(0,6),(-4,-6),(16,6),(16,-7),(16,-3),(17,-4),(5,19), _
(19,-8),(3,16),(12,13),(3,-4),(17,5),(-3,15),(-3,-9),(0,11),(-9,-3),(-4,-2),(12,10)}

Function south(p() As Point,Byref idx As Long) As Point
    Dim As Point s=Type(0,100)
    For n As Long=Lbound(p) To Ubound(p)
        Circle(p(n).x,p(n).y),.2,7,,,,f
        If s.y>p(n).y Then s=p(n):idx=n
    Next n
    Return s
End Function

Function segment_distance(lx1 As Single, _
                          ly1 As Single, _
                          lx2 As Single, _
                          ly2 As Single, _
                          px As Single,_
                          py As Single, _
                          Byref ox As Single=0,_
                          Byref oy As Single=0) As Single
    Dim As Single M1,M2,C1,C2,B
    B=(Lx2-Lx1):If B=0 Then B=1e-20
    M2=(Ly2-Ly1)/B:If M2=0 Then M2=1e-20
    M1=-1/M2
    C1=py-M1*px
    C2=(Ly1*Lx2-Lx1*Ly2)/B
    Var L1=((px-lx1)*(px-lx1)+(py-ly1)*(py-ly1)),L2=((px-lx2)*(px-lx2)+(py-ly2)*(py-ly2))
    Var a=((lx1-lx2)*(lx1-lx2) + (ly1-ly2)*(ly1-ly2))
    Var a1=a+L1
    Var a2=a+L2
    Var f1=a1>L2,f2=a2>L1
    If f1 Xor f2 Then
        Var d1=((px-Lx1)*(px-Lx1)+(py-Ly1)*(py-Ly1))
        Var d2=((px-Lx2)*(px-Lx2)+(py-Ly2)*(py-Ly2))
        If d1<d2 Then Ox=Lx1:Oy=Ly1 : Return Sqr(d1) Else  Ox=Lx2:Oy=Ly2:Return Sqr(d2)
    End If
    Var M=M1-M2:If M=0 Then M=1e-20
    Ox=(C2-C1)/(M1-M2)
    Oy=(M1*C2-M2*C1)/M
    Return Sqr((px-Ox)*(px-Ox)+(py-Oy)*(py-Oy))
End Function

Dim As Long idx
Var s= south(p(),idx)
p(idx).done=1
Redim As Point ans(1 To 1)
ans(1)=s
Dim As Point e=s
e.x=1000
Dim As Long count=1
Dim As Single z
Circle(s.x,s.y),.4,5

Do
    z+=.05
    Var pt=rotate(s,e,z,1)
    For n As Long=Lbound(p) To Ubound(p)
        If segment_distance(s.x,s.y,pt.x,pt.y,p(n).x,p(n).y)<.05  Then
            s=p(n)
            If p(n).done=0 Then
                count+=1
                Redim Preserve ans(1 To count)
                ans(count)=p(n)
                p(n).done=1
            End If
        End If
        Circle(s.x,s.y),.4,5
    Next n
Loop Until z>360

For n As Long=Lbound(ans) To Ubound(ans)
    printf (!"(%2.0f , %2.0f )\n", ans(n).x, ans(n).y)
Next
Sleep


package main

import (
	"fmt"
	"image"
	"sort"
)

// ConvexHull returns the set of points that define the
// convex hull of p in CCW order starting from the left most.
func (p points) ConvexHull() points {
	// From https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
	// with only minor deviations.
	sort.Sort(p)
	var h points

	// Lower hull
	for _, pt := range p {
		for len(h) >= 2 && !ccw(h[len(h)-2], h[len(h)-1], pt) {
			h = h[:len(h)-1]
		}
		h = append(h, pt)
	}

	// Upper hull
	for i, t := len(p)-2, len(h)+1; i >= 0; i-- {
		pt := p[i]
		for len(h) >= t && !ccw(h[len(h)-2], h[len(h)-1], pt) {
			h = h[:len(h)-1]
		}
		h = append(h, pt)
	}

	return h[:len(h)-1]
}

// ccw returns true if the three points make a counter-clockwise turn
func ccw(a, b, c image.Point) bool {
	return ((b.X - a.X) * (c.Y - a.Y)) > ((b.Y - a.Y) * (c.X - a.X))
}

type points []image.Point

func (p points) Len() int      { return len(p) }
func (p points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p points) Less(i, j int) bool {
	if p[i].X == p[j].X {
		return p[i].Y < p[i].Y
	}
	return p[i].X < p[j].X
}

func main() {
	pts := points{
		{16, 3}, {12, 17}, {0, 6}, {-4, -6}, {16, 6},
		{16, -7}, {16, -3}, {17, -4}, {5, 19}, {19, -8},
		{3, 16}, {12, 13}, {3, -4}, {17, 5}, {-3, 15},
		{-3, -9}, {0, 11}, {-9, -3}, {-4, -2}, {12, 10},
	}
	hull := pts.ConvexHull()
	fmt.Println("Convex Hull:", hull)
}


class ConvexHull {
    private static class Point implements Comparable<Point> {
        private int x, y

        Point(int x, int y) {
            this.x = x
            this.y = y
        }

        @Override
        int compareTo(Point o) {
            return Integer.compare(x, o.x)
        }

        @Override
        String toString() {
            return String.format("(%d, %d)", x, y)
        }
    }

    private static List<Point> convexHull(List<Point> p) {
        if (p.isEmpty()) return Collections.emptyList()
        p.sort(new Comparator<Point>() {
            @Override
            int compare(Point o1, Point o2) {
                return o1 <=> o2
            }
        })
        List<Point> h = new ArrayList<>()

        // lower hull
        for (Point pt : p) {
            while (h.size() >= 2 && !ccw(h.get(h.size() - 2), h.get(h.size() - 1), pt)) {
                h.remove(h.size() - 1)
            }
            h.add(pt)
        }

        // upper hull
        int t = h.size() + 1
        for (int i = p.size() - 1; i >= 0; i--) {
            Point pt = p.get(i)
            while (h.size() >= t && !ccw(h.get(h.size() - 2), h.get(h.size() - 1), pt)) {
                h.remove(h.size() - 1)
            }
            h.add(pt)
        }

        h.remove(h.size() - 1)
        return h
    }

    // ccw returns true if the three points make a counter-clockwise turn
    private static boolean ccw(Point a, Point b, Point c) {
        return ((b.x - a.x) * (c.y - a.y)) > ((b.y - a.y) * (c.x - a.x))
    }

    static void main(String[] args) {
        List<Point> points = Arrays.asList(new Point(16, 3),
                new Point(12, 17),
                new Point(0, 6),
                new Point(-4, -6),
                new Point(16, 6),

                new Point(16, -7),
                new Point(16, -3),
                new Point(17, -4),
                new Point(5, 19),
                new Point(19, -8),

                new Point(3, 16),
                new Point(12, 13),
                new Point(3, -4),
                new Point(17, 5),
                new Point(-3, 15),

                new Point(-3, -9),
                new Point(0, 11),
                new Point(-9, -3),
                new Point(-4, -2),
                new Point(12, 10))

        List<Point> hull = convexHull(points)
        println("Convex Hull: $hull")
    }
}


import Data.List (sortBy, groupBy, maximumBy)
import Data.Ord (comparing)

(x, y) = ((!! 0), (!! 1))

compareFrom
  :: (Num a, Ord a)
  => [a] -> [a] -> [a] -> Ordering
compareFrom o l r =
  compare ((x l - x o) * (y r - y o)) ((y l - y o) * (x r - x o))

distanceFrom
  :: Floating a
  => [a] -> [a] -> a
distanceFrom from to = ((x to - x from) ** 2 + (y to - y from) ** 2) ** (1 / 2)

convexHull
  :: (Floating a, Ord a)
  => [[a]] -> [[a]]
convexHull points =
  let o = minimum points
      presorted = sortBy (compareFrom o) (filter (/= o) points)
      collinears = groupBy (((EQ ==) .) . compareFrom o) presorted
      outmost = maximumBy (comparing (distanceFrom o)) <$> collinears
  in dropConcavities [o] outmost

dropConcavities
  :: (Num a, Ord a)
  => [[a]] -> [[a]] -> [[a]]
dropConcavities (left:lefter) (right:righter:rightest) =
  case compareFrom left right righter of
    LT -> dropConcavities (right : left : lefter) (righter : rightest)
    EQ -> dropConcavities (left : lefter) (righter : rightest)
    GT -> dropConcavities lefter (left : righter : rightest)
dropConcavities output lastInput = lastInput ++ output

main :: IO ()
main =
  mapM_ print $
  convexHull
    [ [16, 3]
    , [12, 17]
    , [0, 6]
    , [-4, -6]
    , [16, 6]
    , [16, -7]
    , [16, -3]
    , [17, -4]
    , [5, 19]
    , [19, -8]
    , [3, 16]
    , [12, 13]
    , [3, -4]
    , [17, 5]
    , [-3, 15]
    , [-3, -9]
    , [0, 11]
    , [-9, -3]
    , [-4, -2]
    , [12, 10]
    ]


typedef Point = {x:Float, y:Float};

class Main {

    // Calculate orientation for 3 points
    // 0 -> Straight line
    // 1 -> Clockwise
    // 2 -> Counterclockwise
    static function orientation(pt1:Point, pt2:Point, pt3:Point): Int
    {
      var val = ((pt2.x - pt1.x) * (pt3.y - pt1.y)) -
                ((pt2.y - pt1.y) * (pt3.x - pt1.x));
      if (val == 0)
        return 0;
      else if (val > 0)
        return 1;
      else return 2;
    }

    static function convexHull(pts:Array<Point>):Array<Point> {
      var result = new Array<Point>();

      // There must be at least 3 points
      if (pts.length < 3)
        for (i in pts) result.push(i);

      // Find the leftmost point
      var indexMinX = 0;
      for (i in 0...(pts.length - 1))
        if (pts[i].x < pts[indexMinX].x)
          indexMinX = i;

      var p = indexMinX;
      var q = 0;

      while (true) {
        // The leftmost point must be part of the hull.
        result.push(pts[p]);

        q = (p + 1) % pts.length;

        for (i in 0...(pts.length - 1))
          if (orientation(pts[p], pts[i], pts[q]) == 2) q = i;

        p = q;

        // Break from loop once we reach the first point again.
        if (p == indexMinX)
          break;
      }
      return result;
    }

    static function main() {
      var pts = new Array<Point>();
      pts.push({x: 16, y: 3});
      pts.push({x: 12, y: 17});
      pts.push({x: 0, y: 6});
      pts.push({x: -4, y: -6});
      pts.push({x: 16, y: 6});

      pts.push({x: 16, y: -7});
      pts.push({x: 16, y: -3});
      pts.push({x: 17, y: -4});
      pts.push({x: 5, y: 19});
      pts.push({x: 19, y: -8});

      pts.push({x: 3, y: 16});
      pts.push({x: 12, y: 13});
      pts.push({x: 3, y: -4});
      pts.push({x: 17, y: 5});
      pts.push({x: -3, y: 15});

      pts.push({x: -3, y: -9});
      pts.push({x: 0, y: 11});
      pts.push({x: -9, y: -3});
      pts.push({x: -4, y: -2});
      pts.push({x: 12, y: 10});

      var hull = convexHull(pts);
      Sys.print('Convex Hull: [');
      var length = hull.length;
      var i = 0;
      while (length > 0) {
        if (i > 0)
          Sys.print(', ');
        Sys.print('(${hull[i].x}, ${hull[i].y})');
        length--;
        i++;
      }
      Sys.println(']');
    }
}


#
# Convex hulls by Andrew's monotone chain algorithm.
#
# For a description of the algorithm, see
# https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169
#

record PlanePoint (x, y)

######################################################################
#
# Merge sort adapted from the Object Icon IPL (public domain code).
#

# A merge sort implementation. This returns a sorted copy, leaving the
# original unchanged.
#
# :Parameters :
# :  `l` - the list to sort
# :  `cmp` - a comparator function
#
procedure mergesort (l, cmp)
  return mergesort1 (l, cmp, 1, *l)
end

procedure mergesort1 (l, cmp, first, last)
  local l1, l2, l3, m, v1
  if last <= first then
      return l[first:last + 1]
  m := (first + last) / 2
  l1 := mergesort1 (l, cmp, first, m)
  l2 := mergesort1 (l, cmp, m + 1, last)
  l3 := []
  every v1 := !l1 do {
    while cmp (v1, l2[1]) > 0 do
        put (l3, get(l2))
    put (l3, v1)
  }
  every put(l3, !l2)
  return l3
end

######################################################################

procedure point_equals (p, q)
  if p.x = q.x & p.y = q.y then return else fail
end

# Impose a total order on points, making it one that will work for
# Andrew's monotone chain algorithm. *)
procedure point_comes_before (p, q)
  if (p.x < q.x) | (p.x = q.x & p.y < q.y) then return else fail
end

# Subtraction is really a vector or multivector operation.
procedure point_subtract (p, q)
  return PlanePoint (p.x - q.x, p.y - q.y)
end

# Cross product is really a multivector operation.
procedure point_cross (p, q)
  return (p.x * q.y) - (p.y * q.x)
end

procedure point_to_string (p)
  return "(" || string (p.x) || " " || string (p.y) || ")"
end

######################################################################

# Comparison like C's strcmp(3).
procedure compare_points (p, q)
  local cmp

  if point_comes_before (p, q) then
      cmp := -1
  else if point_comes_before (q, p) then
      cmp := 1
  else
      cmp := 0
  return cmp
end

procedure sort_points (points)
  # Non-destructive sort.
  return mergesort (points, compare_points)
end

procedure delete_neighbor_dups (arr, equals)
  local arr1, i

  if *arr = 0 then {
    arr1 := []
  } else {
    arr1 := [arr[1]]
    i := 2
    while i <= *arr do {
      if not (equals (arr[i], arr1[-1])) then
          put (arr1, arr[i])
      i +:= 1
    }
  }
  return arr1
end

procedure construct_lower_hull (pt)
  local hull, i, j

  hull := list (*pt)
  hull[1] := pt[1]
  hull[2] := pt[2]
  j := 2
  every i := 3 to *pt do {
    while (j ~= 1 &
           point_cross (point_subtract (hull[j], hull[j - 1]),
                        point_subtract (pt[i], hull[j - 1])) <= 0) do j -:= 1
    j +:= 1
    hull[j] := pt[i]
  }
  return hull[1 : j + 1]
end

procedure construct_upper_hull (pt)
  local hull, i, j

  hull := list (*pt)
  hull[1] := pt[-1]
  hull[2] := pt[-2]
  j := 2
  every i := 3 to *pt do {
    while (j ~= 1 &
           point_cross (point_subtract (hull[j], hull[j - 1]),
                        point_subtract (pt[-i], hull[j - 1])) <= 0) do j -:= 1
    j +:= 1
    hull[j] := pt[-i]
  }
  return hull[1 : j + 1]
end

procedure construct_hull (pt)
  local lower_hull, upper_hull

  lower_hull := construct_lower_hull (pt)
  upper_hull := construct_upper_hull (pt)
  return lower_hull[1 : -1] ||| upper_hull [1 : -1]
end

procedure find_convex_hull (points)
  local pt, hull

  if *points = 0 then {
    hull := []
  } else {
    pt := delete_neighbor_dups (sort_points (points), point_equals)
    if *pt <= 2 then {
      hull := pt
    } else {
      hull := construct_hull (pt)
    }
  }
  return hull
end

procedure main ()
  local example_points, hull

  example_points :=
      [PlanePoint (16.0, 3.0),
       PlanePoint (12.0, 17.0),
       PlanePoint (0.0, 6.0),
       PlanePoint (-4.0, -6.0),
       PlanePoint (16.0, 6.0),
       PlanePoint (16.0, -7.0),
       PlanePoint (16.0, -3.0),
       PlanePoint (17.0, -4.0),
       PlanePoint (5.0, 19.0),
       PlanePoint (19.0, -8.0),
       PlanePoint (3.0, 16.0),
       PlanePoint (12.0, 13.0),
       PlanePoint (3.0, -4.0),
       PlanePoint (17.0, 5.0),
       PlanePoint (-3.0, 15.0),
       PlanePoint (-3.0, -9.0),
       PlanePoint (0.0, 11.0),
       PlanePoint (-9.0, -3.0),
       PlanePoint (-4.0, -2.0),
       PlanePoint (12.0, 10.0)]

  hull := find_convex_hull (example_points)

  every write (point_to_string (!hull))
end

######################################################################


counterclockwise =: ({. , }. /: 12 o. }. - {.) @ /:~
crossproduct =: 11 o. [: (* +)/ }. - {.
removeinner =: #~ (1 , (0 > 3 crossproduct\ ]) , 1:)
hull =: [: removeinner^:_ counterclockwise


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;

public class ConvexHull {
    private static class Point implements Comparable<Point> {
        private int x, y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public int compareTo(Point o) {
            return Integer.compare(x, o.x);
        }

        @Override
        public String toString() {
            return String.format("(%d, %d)", x, y);
        }
    }

    private static List<Point> convexHull(List<Point> p) {
        if (p.isEmpty()) return emptyList();
        p.sort(Point::compareTo);
        List<Point> h = new ArrayList<>();

        // lower hull
        for (Point pt : p) {
            while (h.size() >= 2 && !ccw(h.get(h.size() - 2), h.get(h.size() - 1), pt)) {
                h.remove(h.size() - 1);
            }
            h.add(pt);
        }

        // upper hull
        int t = h.size() + 1;
        for (int i = p.size() - 1; i >= 0; i--) {
            Point pt = p.get(i);
            while (h.size() >= t && !ccw(h.get(h.size() - 2), h.get(h.size() - 1), pt)) {
                h.remove(h.size() - 1);
            }
            h.add(pt);
        }

        h.remove(h.size() - 1);
        return h;
    }

    // ccw returns true if the three points make a counter-clockwise turn
    private static boolean ccw(Point a, Point b, Point c) {
        return ((b.x - a.x) * (c.y - a.y)) > ((b.y - a.y) * (c.x - a.x));
    }

    public static void main(String[] args) {
        List<Point> points = Arrays.asList(new Point(16, 3),
                                           new Point(12, 17),
                                           new Point(0, 6),
                                           new Point(-4, -6),
                                           new Point(16, 6),

                                           new Point(16, -7),
                                           new Point(16, -3),
                                           new Point(17, -4),
                                           new Point(5, 19),
                                           new Point(19, -8),

                                           new Point(3, 16),
                                           new Point(12, 13),
                                           new Point(3, -4),
                                           new Point(17, 5),
                                           new Point(-3, 15),

                                           new Point(-3, -9),
                                           new Point(0, 11),
                                           new Point(-9, -3),
                                           new Point(-4, -2),
                                           new Point(12, 10));

        List<Point> hull = convexHull(points);
        System.out.printf("Convex Hull: %s\n", hull);
    }
}


function convexHull(points) {
    points.sort(comparison);
    var L = [];
    for (var i = 0; i < points.length; i++) {
        while (L.length >= 2 && cross(L[L.length - 2], L[L.length - 1], points[i]) <= 0) {
            L.pop();
        }
        L.push(points[i]);
    }
    var U = [];
    for (var i = points.length - 1; i >= 0; i--) {
        while (U.length >= 2 && cross(U[U.length - 2], U[U.length - 1], points[i]) <= 0) {
            U.pop();
        }
        U.push(points[i]);
    }
    L.pop();
    U.pop();
    return L.concat(U);
}

function comparison(a, b) {
    return a.x == b.x ? a.y - b.y : a.x - b.x;
}

function cross(a, b, o) {
    return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
}


# ccw returns true if the three points make a counter-clockwise turn
def ccw(a; b; c):
  a as [$ax, $ay]
  | b as [$bx, $by]
  | c as [$cx, $cy]
  | (($bx - $ax) * ($cy - $ay)) > (($by - $ay) * ($cx - $ax)) ;

def convexHull:
  if . == [] then []
  else sort as $pts
  # lower hull:
  | reduce $pts[] as $pt ([];
      until (length < 2 or ccw(.[-2]; .[-1]; $pt); .[:-1] )
      | . + [$pt] )
  # upper hull
  | (length + 1) as $t
  | reduce range($pts|length-2; -1; -1) as $i (.;
      $pts[$i] as $pt
      | until (length < $t or ccw(.[-2]; .[-1]; $pt); .[:-1] )
      | . + [$pt])
  | .[:-1]
  end ;


// version 1.1.3

class Point(val x: Int, val y: Int) : Comparable<Point> {

    override fun compareTo(other: Point) = this.x.compareTo(other.x)

    override fun toString() = "($x, $y)"
}

fun convexHull(p: Array<Point>): List<Point> {
    if (p.isEmpty()) return emptyList()
    p.sort()
    val h = mutableListOf<Point>()

    // lower hull
    for (pt in p) {
        while (h.size >= 2 && !ccw(h[h.size - 2], h.last(), pt)) {
            h.removeAt(h.lastIndex)
        }
        h.add(pt)
    }

    // upper hull
    val t = h.size + 1
    for (i in p.size - 2 downTo 0) {
        val pt = p[i]
        while (h.size >= t && !ccw(h[h.size - 2], h.last(), pt)) {
            h.removeAt(h.lastIndex)
        }
        h.add(pt)
    }

    h.removeAt(h.lastIndex)
    return h
}

/* ccw returns true if the three points make a counter-clockwise turn */
fun ccw(a: Point, b: Point, c: Point) =
    ((b.x - a.x) * (c.y - a.y)) > ((b.y - a.y) * (c.x - a.x))

fun main(args: Array<String>) {
    val points = arrayOf(
        Point(16,  3), Point(12, 17), Point( 0,  6), Point(-4, -6), Point(16,  6),
        Point(16, -7), Point(16, -3), Point(17, -4), Point( 5, 19), Point(19, -8),
        Point( 3, 16), Point(12, 13), Point( 3, -4), Point(17,  5), Point(-3, 15),
        Point(-3, -9), Point( 0, 11), Point(-9, -3), Point(-4, -2), Point(12, 10)
    )
    val hull = convexHull(points)
    println("Convex Hull: $hull")
}


function print_point(p)
    io.write("("..p.x..", "..p.y..")")
    return nil
end

function print_points(pl)
    io.write("[")
    for i,p in pairs(pl) do
        if i>1 then
            io.write(", ")
        end
        print_point(p)
    end
    io.write("]")
    return nil
end

function ccw(a,b,c)
    return (b.x - a.x) * (c.y - a.y) > (b.y - a.y) * (c.x - a.x)
end

function pop_back(ta)
    table.remove(ta,#ta)
    return ta
end

function convexHull(pl)
    if #pl == 0 then
        return {}
    end
    table.sort(pl, function(left,right)
        return left.x < right.x
    end)

    local h = {}

    -- lower hull
    for i,pt in pairs(pl) do
        while #h >= 2 and not ccw(h[#h-1], h[#h], pt) do
            table.remove(h,#h)
        end
        table.insert(h,pt)
    end

    -- upper hull
    local t = #h + 1
    for i=#pl, 1, -1 do
        local pt = pl[i]
        while #h >= t and not ccw(h[#h-1], h[#h], pt) do
            table.remove(h,#h)
        end
        table.insert(h,pt)
    end

    table.remove(h,#h)
    return h
end

-- main
local points = {
    {x=16,y= 3},{x=12,y=17},{x= 0,y= 6},{x=-4,y=-6},{x=16,y= 6},
    {x=16,y=-7},{x=16,y=-3},{x=17,y=-4},{x= 5,y=19},{x=19,y=-8},
    {x= 3,y=16},{x=12,y=13},{x= 3,y=-4},{x=17,y= 5},{x=-3,y=15},
    {x=-3,y=-9},{x= 0,y=11},{x=-9,y=-3},{x=-4,y=-2},{x=12,y=10}
}
local hull = convexHull(points)

io.write("Convex Hull: ")
print_points(hull)
print()


:- module convex_hull_task.

:- interface.
:- import_module io.
:- pred main(io, io).
:- mode main(di, uo) is det.

:- implementation.
:- import_module exception.
:- import_module float.
:- import_module int.
:- import_module list.
:- import_module pair.
:- import_module string.
:- import_module version_array.

%%--------------------------------------------------------------------

%% fetch_items/3 for version_array, similar to the library function
%% for regular array.
:- func fetch_items(version_array(T), int, int) = list(T).
fetch_items(Arr, I, J) = fetch_items_(Arr, I, J, []).

:- func fetch_items_(version_array(T), int, int, list(T)) = list(T).
fetch_items_(Arr, I, J, Lst0) = Lst :-
  if (J < I) then (Lst = Lst0)
  else (J1 = J - 1,
        Lst = fetch_items_(Arr, I, J1, [Arr^elem(J) | Lst0])).

%%--------------------------------------------------------------------

:- type point == pair(float).
:- type point_list == list(point).
:- type point_array == version_array(point).

:- pred point_comes_before(point, point).
:- mode point_comes_before(in, in) is semidet.
point_comes_before(P, Q) :-
  (fst(P) < fst(Q); fst(P) - fst(Q) = (0.0),
                    snd(P) < snd(Q)).

:- pred point_compare(point, point, comparison_result).
:- mode point_compare(in, in, out) is det.
point_compare(P, Q, Cmp) :-
  if (point_comes_before(P, Q)) then (Cmp = (<))
  else if (point_comes_before(Q, P)) then (Cmp = (>))
  else (Cmp = (=)).

:- func point_subtract(point, point) = point.
point_subtract(P, Q) = pair(fst(P) - fst(Q),
                            snd(P) - snd(Q)).

:- func point_cross_product(point, point) = float.
point_cross_product(P, Q) = (fst(P) * snd(Q)) - (snd(P) * fst(Q)).

:- func point_to_string(point) = string.
point_to_string(P) = ("(" ++ from_float(fst(P)) ++
                      " " ++ from_float(snd(P)) ++ ")").

%%--------------------------------------------------------------------

:- func convex_hull(point_list) = point_list.
convex_hull(Pt) = Hull :-
  Pt1 = unique_points_sorted(Pt),
  (if (Pt1 = []; Pt1 = [_]; Pt1 = [_, _]) then (Hull = Pt1)
   else (Hull = construct_hull(Pt1))).

:- func unique_points_sorted(point_list) = point_list.
unique_points_sorted(Pt0) = Pt :-
  sort_and_remove_dups(point_compare, Pt0, Pt).

:- func construct_hull(point_list) = point_list.
construct_hull(Pt) = (construct_lower_hull(Pt) ++
                      construct_upper_hull(Pt)).

:- func construct_lower_hull(point_list) = point_list.
construct_lower_hull(Pt) = Hull :-
  if (Pt = [P0, P1 | Rest])
  then (N = length(Pt),
        Arr0 = (version_array.init(N, P0)),
        Arr1 = (Arr0^elem(1) := P1),
        hull_construction(Rest, Arr1, Arr2, 1, N_Hull),
        %% In the fetch_items/3 call, we leave out the last item. It
        %% is redundant with the first item of the upper hull.
        N_Hull2 = N_Hull - 2,
        Hull = fetch_items(Arr2, 0, N_Hull2))
  else throw("construct_lower_hull expects list of length >= 3").

:- func construct_upper_hull(point_list) = point_list.
%% An upper hull is merely a lower hull for points going the other
%% way.
construct_upper_hull(Pt) = construct_lower_hull(reverse(Pt)).

:- pred hull_construction(point_list, point_array, point_array,
                          int, int).
:- mode hull_construction(in, in, out, in, out) is det.
hull_construction([], Arr0, Arr, J, N_Hull) :-
  Arr = Arr0,
  N_Hull = J + 1.
hull_construction([P | Rest], Arr0, Arr, J, N_Hull) :-
  if cross_test(P, Arr0, J)
  then (J1 = J + 1,
        Arr1 = (Arr0^elem(J1) := P),
        hull_construction(Rest, Arr1, Arr, J1, N_Hull))
  else (J1 = J - 1,
        hull_construction([P | Rest], Arr0, Arr, J1, N_Hull)).

:- pred cross_test(point, point_array, int).
:- mode cross_test(in, in, in) is semidet.
cross_test(P, Arr, J) :-
  if (J = 0) then true
  else (Elem_J = Arr^elem(J),
        J1 = J - 1,
        Elem_J1 = Arr^elem(J1),
        0.0 < point_cross_product(point_subtract(Elem_J, Elem_J1),
                                  point_subtract(P, Elem_J1))).

%%--------------------------------------------------------------------

main(!IO) :-
  Example_points = [pair(16.0, 3.0),
                    pair(12.0, 17.0),
                    pair(0.0, 6.0),
                    pair(-4.0, -6.0),
                    pair(16.0, 6.0),
                    pair(16.0, -7.0),
                    pair(16.0, -3.0),
                    pair(17.0, -4.0),
                    pair(5.0, 19.0),
                    pair(19.0, -8.0),
                    pair(3.0, 16.0),
                    pair(12.0, 13.0),
                    pair(3.0, -4.0),
                    pair(17.0, 5.0),
                    pair(-3.0, 15.0),
                    pair(-3.0, -9.0),
                    pair(0.0, 11.0),
                    pair(-9.0, -3.0),
                    pair(-4.0, -2.0),
                    pair(12.0, 10.0)],
  Hull = convex_hull(Example_points),
  HullStr = join_list(" ", map(point_to_string, Hull)),
  write_string(HullStr, !IO),
  nl(!IO).

%%%-------------------------------------------------------------------
%%% local variables:
%%% mode: mercury
%%% prolog-indent-width: 2
%%% end:


MODULE ConvexHull;
FROM FormatString IMPORT FormatString;
FROM Storage IMPORT ALLOCATE, DEALLOCATE;
FROM SYSTEM IMPORT TSIZE;
FROM Terminal IMPORT WriteString,WriteLn,ReadChar;

PROCEDURE WriteInt(n : INTEGER);
VAR buf : ARRAY[0..15] OF CHAR;
BEGIN
    FormatString("%i", buf, n);
    WriteString(buf);
END WriteInt;

TYPE
    Point = RECORD
        x, y : INTEGER;
    END;

PROCEDURE WritePoint(pt : Point);
BEGIN
    WriteString("(");
    WriteInt(pt.x);
    WriteString(", ");
    WriteInt(pt.y);
    WriteString(")");
END WritePoint;

TYPE
    NextNode = POINTER TO PNode;
    PNode = RECORD
        value : Point;
        next : NextNode;
    END;

PROCEDURE WriteNode(it : NextNode);
BEGIN
    IF it = NIL THEN
        RETURN
    END;
    WriteString("[");

    WritePoint(it^.value);
    it := it^.next;

    WHILE it # NIL DO
        WriteString(", ");
        WritePoint(it^.value);
        it := it^.next
    END;
    WriteString("]")
END WriteNode;

PROCEDURE AppendNode(pn : NextNode; p : Point) : NextNode;
VAR it,nx : NextNode;
BEGIN
    IF pn = NIL THEN
        ALLOCATE(it,TSIZE(PNode));
        it^.value := p;
        it^.next := NIL;
        RETURN it
    END;

    it := pn;
    WHILE it^.next # NIL DO
        it := it^.next
    END;

    ALLOCATE(nx,TSIZE(PNode));
    nx^.value := p;
    nx^.next := NIL;

    it^.next := nx;
    RETURN pn
END AppendNode;

PROCEDURE DeleteNode(VAR pn : NextNode);
BEGIN
    IF pn = NIL THEN RETURN END;
    DeleteNode(pn^.next);

    DEALLOCATE(pn,TSIZE(PNode));
    pn := NIL
END DeleteNode;

PROCEDURE SortNode(VAR pn : NextNode);
VAR
    it : NextNode;
    tmp : Point;
    done : BOOLEAN;
BEGIN
    REPEAT
        done := TRUE;
        it := pn;
        WHILE (it # NIL) AND (it^.next # NIL) DO
            IF it^.next^.value.x < it^.value.x THEN
                tmp := it^.value;
                it^.value := it^.next^.value;
                it^.next^.value := tmp;
                done := FALSE
            END;
            it := it^.next;
        END
    UNTIL done;
END SortNode;

PROCEDURE NodeLength(it : NextNode) : INTEGER;
VAR length : INTEGER;
BEGIN
    length := 0;
    WHILE it # NIL DO
        INC(length);
        it := it^.next;
    END;
    RETURN length
END NodeLength;

PROCEDURE ReverseNode(fp : NextNode) : NextNode;
VAR rp,tmp : NextNode;
BEGIN
    IF fp = NIL THEN RETURN NIL END;

    ALLOCATE(tmp,TSIZE(PNode));
    tmp^.value := fp^.value;
    tmp^.next := NIL;
    rp := tmp;
    fp := fp^.next;

    WHILE fp # NIL DO
        ALLOCATE(tmp,TSIZE(PNode));
        tmp^.value := fp^.value;
        tmp^.next := rp;
        rp := tmp;
        fp := fp^.next;
    END;

    RETURN rp
END ReverseNode;

(* ccw returns true if the three points make a counter-clockwise turn *)
PROCEDURE CCW(a,b,c : Point) : BOOLEAN;
BEGIN
    RETURN ((b.x - a.x) * (c.y - a.y)) > ((b.y - a.y) * (c.x - a.x))
END CCW;

PROCEDURE ConvexHull(p : NextNode) : NextNode;
VAR
    hull,it,h1,h2 : NextNode;
    t : INTEGER;
BEGIN
    IF p = NIL THEN RETURN NIL END;
    SortNode(p);
    hull := NIL;

    (* lower hull *)
    it := p;
    WHILE it # NIL DO
        IF hull # NIL THEN
            WHILE hull^.next # NIL DO
                (* At least two points in the list *)
                h2 := hull;
                h1 := hull^.next;
                WHILE h1^.next # NIL DO
                    h2 := h1;
                    h1 := h2^.next;
                END;

                IF CCW(h2^.value, h1^.value, it^.value) THEN
                    BREAK
                ELSE
                    h2^.next := NIL;
                    DeleteNode(h1);
                    h1 := NIL
                END
            END
        END;

        hull := AppendNode(hull, it^.value);
        it := it^.next;
    END;

    (* upper hull *)
    t := NodeLength(hull) + 1;
    p := ReverseNode(p);
    it := p;
    WHILE it # NIL DO
        WHILE NodeLength(hull) >= t DO
            h2 := hull;
            h1 := hull^.next;
            WHILE h1^.next # NIL DO
                h2 := h1;
                h1 := h2^.next;
            END;

            IF CCW(h2^.value, h1^.value, it^.value) THEN
                BREAK
            ELSE
                h2^.next := NIL;
                DeleteNode(h1);
                h1 := NIL
            END
        END;

        hull := AppendNode(hull, it^.value);
        it := it^.next;
    END;
    DeleteNode(p);

    h2 := hull;
    h1 := h2^.next;
    WHILE h1^.next # NIL DO
        h2 := h1;
        h1 := h1^.next;
    END;
    h2^.next := NIL;
    DeleteNode(h1);
    RETURN hull
END ConvexHull;

(* Main *)
VAR nodes,hull : NextNode;
BEGIN
    nodes := AppendNode(NIL, Point{16, 3});
    AppendNode(nodes, Point{12,17});
    AppendNode(nodes, Point{ 0, 6});
    AppendNode(nodes, Point{-4,-6});
    AppendNode(nodes, Point{16, 6});
    AppendNode(nodes, Point{16,-7});
    AppendNode(nodes, Point{16,-3});
    AppendNode(nodes, Point{17,-4});
    AppendNode(nodes, Point{ 5,19});
    AppendNode(nodes, Point{19,-8});
    AppendNode(nodes, Point{ 3,16});
    AppendNode(nodes, Point{12,13});
    AppendNode(nodes, Point{ 3,-4});
    AppendNode(nodes, Point{17, 5});
    AppendNode(nodes, Point{-3,15});
    AppendNode(nodes, Point{-3,-9});
    AppendNode(nodes, Point{ 0,11});
    AppendNode(nodes, Point{-9,-3});
    AppendNode(nodes, Point{-4,-2});
    AppendNode(nodes, Point{12,10});

    hull := ConvexHull(nodes);
    WriteNode(hull);
    DeleteNode(hull);

    DeleteNode(nodes);
    ReadChar
END ConvexHull.


type
  Point = object
    x: float
    y: float

# Calculate orientation for 3 points
# 0 -> Straight line
# 1 -> Clockwise
# 2 -> Counterclockwise
proc orientation(p, q, r: Point): int =
  let val = (q.y - p.y) * (r.x - q.x) -
    (q.x - p.x) * (r.y - q.y)

  if val == 0: 0
  elif val > 0: 1
  else: 2

proc calculateConvexHull(points: openArray[Point]): seq[Point] =
  result = newSeq[Point]()

  # There must be at least 3 points
  if len(points) < 3:
    for i in points: result.add(i)

  # Find the leftmost point
  var indexMinX = 0
  for i, _ in points:
    if points[i].x < points[indexMinX].x:
      indexMinX = i

  var p = indexMinX
  var q = 0

  while true:
    # The leftmost point must be part of the hull.
    result.add(points[p])

    q = (p + 1) mod len(points)

    for i in 0..<len(points):
      if orientation(points[p], points[i], points[q]) == 2:
        q = i

    p = q

    # Break from loop once we reach the first point again
    if p == indexMinX:
      break

var points = @[Point(x: 16, y: 3),
               Point(x: 12, y: 17),
               Point(x: 0, y: 6),
               Point(x: -4, y: -6),
               Point(x: 16, y: 6),
               Point(x: 16, y: -7),
               Point(x: 17, y: -4),
               Point(x: 5, y: 19),
               Point(x: 19, y: -8),
               Point(x: 3, y: 16),
               Point(x: 12, y: 13),
               Point(x: 3, y: -4),
               Point(x: 17, y: 5),
               Point(x: -3, y: 15),
               Point(x: -3, y: -9),
               Point(x: 0, y: 11),
               Point(x: -9, y: -3),
               Point(x: -4, y: -2),
               Point(x: 12, y: 10)]

let hull = calculateConvexHull(points)
for i in hull:
  echo i


# -*- ObjectIcon -*-
#
# Convex hulls by Andrew's monotone chain algorithm.
#
# For a description of the algorithm, see
# https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169
#

import io
import ipl.sort

class PlanePoint ()           # Enough plane geometry for our purpose.

  private readable x, y

  public new (x, y)
    self.x := x
    self.y := y
    return
  end

  public equals (other)
    if self.x = other.x & self.y = other.y then
      return
    else
      fail
  end

  # Impose a total order on points, making it one that will work for
  # Andrew's monotone chain algorithm. *)
  public comes_before (other)
    if (self.x < other.x) | (self.x = other.x & self.y < other.y) then
      return
    else
      fail
  end

  # Subtraction is really a vector or multivector operation.
  public minus (other)
    return PlanePoint (self.x - other.x, self.y - other.y)
  end

  # Cross product is really a multivector operation.
  public cross (other)
    return (self.x * other.y) - (self.y * other.x)
  end

  public to_string ()
    return "(" || string (self.x) || " " || string (self.y) || ")"
  end

end

# Comparison like C's strcmp(3).
procedure compare_points (p, q)
  local cmp

  if p.comes_before (q) then
    cmp := -1
  else if q.comes_before (p) then
    cmp := 1
  else
    cmp := 0
  return cmp
end

procedure sort_points (points)
  # Non-destructive sort.
  return mergesort (points, compare_points)
end

procedure delete_neighbor_dups (arr, equals)
  local arr1, i

  if *arr = 0 then {
    arr1 := []
  } else {
    arr1 := [arr[1]]
    i := 2
    while i <= *arr do {
      unless equals (arr[i], arr1[-1]) then
        put (arr1, arr[i])
      i +:= 1
    }
  }
  return arr1
end

procedure construct_lower_hull (pt)
  local hull, i, j

  hull := list (*pt)
  hull[1] := pt[1]
  hull[2] := pt[2]
  j := 2
  every i := 3 to *pt do {
    while (j ~= 1 &
           (hull[j].minus (hull[j - 1])).cross (pt[i].minus (hull[j - 1])) <= 0) do
      j -:= 1
    j +:= 1
    hull[j] := pt[i]
  }
  return hull[1 : j + 1]
end

procedure construct_upper_hull (pt)
  local hull, i, j

  hull := list (*pt)
  hull[1] := pt[-1]
  hull[2] := pt[-2]
  j := 2
  every i := 3 to *pt do {
    while (j ~= 1 &
           (hull[j].minus (hull[j - 1])).cross (pt[-i].minus (hull[j - 1])) <= 0) do
      j -:= 1
    j +:= 1
    hull[j] := pt[-i]
  }
  return hull[1 : j + 1]
end

procedure construct_hull (pt)
  local lower_hull, upper_hull

  lower_hull := construct_lower_hull (pt)
  upper_hull := construct_upper_hull (pt)
  return lower_hull[1 : -1] ||| upper_hull [1 : -1]
end

procedure points_equal (p, q)
  if p.equals (q) then
    return
  else
    fail
end

procedure find_convex_hull (points)
  local pt, hull

  if *points = 0 then {
    hull := []
  } else {
    pt := delete_neighbor_dups (sort_points (points), points_equal)
    if *pt <= 2 then
      hull := pt
    else
      hull := construct_hull (pt)
  }
  return hull
end

procedure main ()
  local example_points, hull

  example_points :=
    [PlanePoint (16.0, 3.0),
     PlanePoint (12.0, 17.0),
     PlanePoint (0.0, 6.0),
     PlanePoint (-4.0, -6.0),
     PlanePoint (16.0, 6.0),
     PlanePoint (16.0, -7.0),
     PlanePoint (16.0, -3.0),
     PlanePoint (17.0, -4.0),
     PlanePoint (5.0, 19.0),
     PlanePoint (19.0, -8.0),
     PlanePoint (3.0, 16.0),
     PlanePoint (12.0, 13.0),
     PlanePoint (3.0, -4.0),
     PlanePoint (17.0, 5.0),
     PlanePoint (-3.0, 15.0),
     PlanePoint (-3.0, -9.0),
     PlanePoint (0.0, 11.0),
     PlanePoint (-9.0, -3.0),
     PlanePoint (-4.0, -2.0),
     PlanePoint (12.0, 10.0)]

  hull := find_convex_hull (example_points)

  every write ((!hull).to_string ())
end


(*
 * Convex hulls by Andrew's monotone chain algorithm.
 *
 * For a description of the algorithm, see
 * https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169
 *)

(*------------------------------------------------------------------*)
(* Just enough plane geometry for our purpose.  *)

module Plane_point =
  struct
    type t = float * float

    let make (xy : t) = xy
    let to_tuple ((x, y) : t) = (x, y)

    let x ((x, _) : t) = x
    let y ((_, y) : t) = y

    let equal (u : t) (v : t) = (x u = x v && y u = y v)

    (* Impose a total order on points, making it one that will work
       for Andrew's monotone chain algorithm. *)
    let order (u : t) (v : t) =
      (x u < x v) || (x u = x v && y u < y v)

    (* The Array module's sort routines expect a "cmp" function. *)
    let cmp u v =
      if order u v then
        (-1)
      else if order v u then
        (1)
      else
        (0)

    (* Subtraction is really a vector or multivector operation. *)
    let sub (u : t) (v : t) = make (x u -. x v, y u -. y v)

    (* Cross product is really a multivector operation. *)
    let cross (u : t) (v : t) = (x u *. y v) -. (y u *. x v)

    let to_string ((x, y) : t) =
      "(" ^ string_of_float x ^ " " ^ string_of_float y ^ ")"
  end
;;

(*------------------------------------------------------------------*)
(* We want something akin to array_delete_neighbor_dups of Scheme's
   SRFI-132. *)

let array_delete_neighbor_dups equal arr =
  (* Returns a Seq.t rather than an array. *)
  let rec loop i lst =
    (* Cons a list of non-duplicates, going backwards through the
       array so the list will be in forwards order. *)
    if i = 0 then
      arr.(i) :: lst
    else if equal arr.(i - 1) arr.(i) then
      loop (i - 1) lst
    else
      loop (i - 1) (arr.(i) :: lst)
  in
  let n = Array.length arr in
  List.to_seq (if n = 0 then [] else loop (n - 1) [])
;;

(*------------------------------------------------------------------*)
(* The convex hull algorithm. *)

let cross_test pt_i hull j =
  let hull_j = hull.(j)
  and hull_j1 = hull.(j - 1) in
  0.0 < Plane_point.(cross (sub hull_j hull_j1)
                       (sub pt_i hull_j1))

let construct_lower_hull n pt =
  let hull = Array.make n (Plane_point.make (0.0, 0.0)) in
  let () = hull.(0) <- pt.(0)
  and () = hull.(1) <- pt.(1) in
  let rec outer_loop i j =
    if i = n then
      j + 1
    else
      let pt_i = pt.(i) in
      let rec inner_loop j =
        if j = 0 || cross_test pt_i hull j then
          begin
            hull.(j + 1) <- pt_i;
            j + 1
          end
        else
          inner_loop (j - 1)
      in
      outer_loop (i + 1) (inner_loop j)
  in
  let hull_size = outer_loop 2 1 in
  (hull_size, hull)

let construct_upper_hull n pt =
  let hull = Array.make n (Plane_point.make (0.0, 0.0)) in
  let () = hull.(0) <- pt.(n - 1)
  and () = hull.(1) <- pt.(n - 2) in
  let rec outer_loop i j =
    if i = (-1) then
      j + 1
    else
      let pt_i = pt.(i) in
      let rec inner_loop j =
        if j = 0 || cross_test pt_i hull j then
          begin
            hull.(j + 1) <- pt_i;
            j + 1
          end
        else
          inner_loop (j - 1)
      in
      outer_loop (i - 1) (inner_loop j)
  in
  let hull_size = outer_loop (n - 3) 1 in
  (hull_size, hull)

let construct_hull n pt =

  (* Side note: Construction of the lower and upper hulls can be done
     in parallel. *)
  let (lower_hull_size, lower_hull) = construct_lower_hull n pt
  and (upper_hull_size, upper_hull) = construct_upper_hull n pt in

  let hull_size = lower_hull_size + upper_hull_size - 2 in
  let hull = Array.make hull_size (Plane_point.make (0.0, 0.0)) in

  begin
    Array.blit lower_hull 0 hull 0 (lower_hull_size - 1);
    Array.blit upper_hull 0 hull (lower_hull_size - 1)
      (upper_hull_size - 1);
    hull
  end

let plane_convex_hull points =
  (* Takes an arbitrary sequence of points, which may be in any order
     and may contain duplicates. Returns an ordered array of points
     that make up the convex hull. If the sequence of points is empty,
     the returned array is empty. *)
  let pt = Array.of_seq points in
  let () = Array.fast_sort Plane_point.cmp pt in
  let pt = Array.of_seq
             (array_delete_neighbor_dups Plane_point.equal pt) in
  let n = Array.length pt in
  if n <= 2 then
    pt
  else
    construct_hull n pt
;;

(*------------------------------------------------------------------*)

let example_points =
  [Plane_point.make (16.0, 3.0);
   Plane_point.make (12.0, 17.0);
   Plane_point.make (0.0, 6.0);
   Plane_point.make (-4.0, -6.0);
   Plane_point.make (16.0, 6.0);
   Plane_point.make (16.0, -7.0);
   Plane_point.make (16.0, -3.0);
   Plane_point.make (17.0, -4.0);
   Plane_point.make (5.0, 19.0);
   Plane_point.make (19.0, -8.0);
   Plane_point.make (3.0, 16.0);
   Plane_point.make (12.0, 13.0);
   Plane_point.make (3.0, -4.0);
   Plane_point.make (17.0, 5.0);
   Plane_point.make (-3.0, 15.0);
   Plane_point.make (-3.0, -9.0);
   Plane_point.make (0.0, 11.0);
   Plane_point.make (-9.0, -3.0);
   Plane_point.make (-4.0, -2.0);
   Plane_point.make (12.0, 10.0)]
;;

let hull = plane_convex_hull (List.to_seq example_points)
;;

Array.iter
  (fun p -> (print_string (Plane_point.to_string p);
             print_string " "))
  hull;
print_newline ()
;;

(*------------------------------------------------------------------*)


{$mode ISO}

program convex_hull_task (output);

{ Convex hulls, by Andrew's monotone chain algorithm.

  For a description of the algorithm, see
  https://en.wikibooks.org/w/index.php?title=Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain&stableid=40169 }

const max_points = 1000;
type points_range = 0 .. max_points - 1;

type point =
  record
    x, y : real
  end;
type point_array = array [points_range] of point;

var ciura_gaps : array [1 .. 8] of integer;

var example_points : point_array;
var hull           : point_array;
var hull_size      : integer;
var index          : integer;

function make_point (x, y : real) : point;
begin
  make_point.x := x;
  make_point.y := y;
end;

{ The cross product as a signed scalar. }
function cross (u, v : point) : real;
begin
  cross := (u.x * v.y) - (u.y * v.x)
end;

function point_subtract (u, v : point) : point;
begin
  point_subtract := make_point (u.x - v.x, u.y - v.y)
end;

function point_equal (u, v : point) : boolean;
begin
  point_equal := (u.x = v.x) and (u.y = v.y)
end;

procedure sort_points (num_points : integer;
                       var points : point_array);
{ Sort first in ascending order by x-coordinates, then in
  ascending order by y-coordinates. Any decent sort algorithm will
  suffice; for the sake of interest, here is the Shell sort of
  https://en.wikipedia.org/w/index.php?title=Shellsort&oldid=1084744510 }
var
  i, j, k, gap, offset : integer;
  temp                 : point;
  done                 : boolean;
begin
  for k := 1 to 8 do
    begin
      gap := ciura_gaps[k];
      for offset := 0 to gap - 1 do
        begin
          i := offset;
          while i <= num_points - 1 do
            begin
              temp := points[i];
              j := i;
              done := false;
              while not done do
                begin
                  if j < gap then
                    done := true
                  else if points[j - gap].x < temp.x then
                    done := true
                  else if ((points[j - gap].x = temp.x)
                             and (points[j - gap].y < temp.y)) then
                    done := true
                  else
                    begin
                      points[j] := points[j - gap];
                      j := j - gap
                    end
                end;
              points[j] := temp;
              i := i + gap
            end
        end
    end
end; { sort_points }

procedure delete_neighbor_duplicates (var n  : integer;
                                      var pt : point_array);

  procedure delete_trailing_duplicates;
  var
    i    : integer;
    done : boolean;
  begin
    i := n - 1;
    done := false;
    while not done do
      begin
        if i = 0 then
          begin
            n := 1;
            done := true
          end
        else if not point_equal (pt[i - 1], pt[i]) then
          begin
            n := i + 1;
            done := true
          end
        else
          i := i + 1
      end
  end;

  procedure delete_nontrailing_duplicates;
  var
    i, j, num_deleted : integer;
    done              : boolean;
  begin
    i := 0;
    while i < n - 1 do
      begin
        j := i + 1;
        done := false;
        while not done do
          begin
            if j = n then
              done := true
            else if not point_equal (pt[j], pt[i]) then
              done := true
            else
              j := j + 1
          end;
        if j <> i + 1 then
          begin
            num_deleted := j - i - 1;
            while j <> n do
              begin
                pt[j - num_deleted] := pt[j];
                j := j + 1
              end;
            n := n - num_deleted
          end;
        i := i + 1
      end
  end;

begin
  delete_trailing_duplicates;
  delete_nontrailing_duplicates
end; { delete_neighbor_duplicates }

procedure construct_lower_hull (n             : integer;
                                pt            : point_array;
                                var hull_size : integer;
                                var hull      : point_array);
var
  i, j : integer;
  done : boolean;
begin
  j := 1;
  hull[0] := pt[0];
  hull[1] := pt[1];
  for i := 2 to n - 1 do
    begin
      done := false;
      while not done do
        begin
          if j = 0 then
            begin
              j := j + 1;
              hull[j] := pt[i];
              done := true
            end
          else if 0.0 < cross (point_subtract (hull[j],
                                               hull[j - 1]),
                               point_subtract (pt[i],
                                               hull[j - 1])) then
            begin
              j := j + 1;
              hull[j] := pt[i];
              done := true
            end
          else
            j := j - 1
        end
    end;
  hull_size := j + 1
end; { construct_lower_hull }

procedure construct_upper_hull (n             : integer;
                                pt            : point_array;
                                var hull_size : integer;
                                var hull      : point_array);
var
  i, j : integer;
  done : boolean;
begin
  j := 1;
  hull[0] := pt[n - 1];
  hull[1] := pt[n - 2];
  for i := n - 3 downto 0 do
    begin
      done := false;
      while not done do
        begin
          if j = 0 then
            begin
              j := j + 1;
              hull[j] := pt[i];
              done := true
            end
          else if 0.0 < cross (point_subtract (hull[j],
                                               hull[j - 1]),
                               point_subtract (pt[i],
                                               hull[j - 1])) then
            begin
              j := j + 1;
              hull[j] := pt[i];
              done := true
            end
          else
            j := j - 1
        end
    end;
  hull_size := j + 1
end; { construct_upper_hull }

procedure contruct_hull (n             : integer;
                         pt            : point_array;
                         var hull_size : integer;
                         var hull      : point_array);
var
  i                                : integer;
  lower_hull_size, upper_hull_size : integer;
  lower_hull, upper_hull           : point_array;
begin
  { A side note: the calls to construct_lower_hull and
    construct_upper_hull could be done in parallel. }
  construct_lower_hull (n, pt, lower_hull_size, lower_hull);
  construct_upper_hull (n, pt, upper_hull_size, upper_hull);

  hull_size := lower_hull_size + upper_hull_size - 2;

  for i := 0 to lower_hull_size - 2 do
    hull[i] := lower_hull[i];
  for i := 0 to upper_hull_size - 2 do
    hull[lower_hull_size - 1 + i] := upper_hull[i]
end; { contruct_hull }

procedure find_convex_hull (n             : integer;
                            points        : point_array;
                            var hull_size : integer;
                            var hull      : point_array);
var
  pt    : point_array;
  numpt : integer;
  i     : integer;
begin
  for i := 0 to n - 1 do
    pt[i] := points[i];
  numpt := n;

  sort_points (numpt, pt);
  delete_neighbor_duplicates (numpt, pt);

  if numpt = 0 then
    hull_size := 0
  else if numpt <= 2 then
    begin
      hull_size := numpt;
      for i := 0 to numpt - 1 do
        hull[i] := pt[i]
    end
  else
    contruct_hull (numpt, pt, hull_size, hull)
end; { find_convex_hull }

begin
  ciura_gaps[1] := 701;
  ciura_gaps[2] := 301;
  ciura_gaps[3] := 132;
  ciura_gaps[4] := 57;
  ciura_gaps[5] := 23;
  ciura_gaps[6] := 10;
  ciura_gaps[7] := 4;
  ciura_gaps[8] := 1;

  example_points[0] := make_point (16, 3);
  example_points[1] := make_point (12, 17);
  example_points[2] := make_point (0, 6);
  example_points[3] := make_point (-4, -6);
  example_points[4] := make_point (16, 6);
  example_points[5] := make_point (16, -7);
  example_points[6] := make_point (16, -3);
  example_points[7] := make_point (17, -4);
  example_points[8] := make_point (5, 19);
  example_points[9] := make_point (19, -8);
  example_points[10] := make_point (3, 16);
  example_points[11] := make_point (12, 13);
  example_points[12] := make_point (3, -4);
  example_points[13] := make_point (17, 5);
  example_points[14] := make_point (-3, 15);
  example_points[15] := make_point (-3, -9);
  example_points[16] := make_point (0, 11);
  example_points[17] := make_point (-9, -3);
  example_points[18] := make_point (-4, -2);
  example_points[19] := make_point (12, 10);

  find_convex_hull (19, example_points, hull_size, hull);

  for index := 0 to hull_size - 1 do
    writeln (hull[index].x, ' ', hull[index].y)
end.

{--------------------------------------------------------------------}
{ The Emacs Pascal mode is intolerable.
  Until I can find a substitute: }
{ local variables:  }
{ mode: fundamental }
{ end:              }