static ListDualStack <int> ConvexHullGrahamScan2(Point[] points)
        {
            var hull = new ListDualStack <int>();

            var sortedIndeces = Enumerable.Range(0, points.Length)
                                .OrderBy(x => points[x].Y)
                                .ToArray();

            hull.Add(sortedIndeces[0]);
            hull.Add(sortedIndeces[1]);

            for (int i = 2; i < sortedIndeces.Length; ++i)
            {
                while (hull.Count > 1)
                {
                    var area = DirectedArea(
                        points[hull.SecondLast],
                        points[hull.Last],
                        points[sortedIndeces[i]]);

                    if (area > Epsillon)
                    {
                        break;
                    }

                    hull.RemoveLast();
                }

                hull.Add(sortedIndeces[i]);
            }
            for (int i = sortedIndeces.Length - 2; i >= 0; --i)
            {
                while (hull.Count > 1)
                {
                    var area = DirectedArea(
                        points[hull.SecondLast],
                        points[hull.Last],
                        points[sortedIndeces[i]]);

                    if (area > Epsillon)
                    {
                        break;
                    }

                    hull.RemoveLast();
                }

                hull.Add(sortedIndeces[i]);
            }
            hull.RemoveLast();

            return(hull);
        }
        static ListDualStack <int> ConvexHullGrahamScan(Point[] points)
        {
            var hull = new ListDualStack <int>();

            var firstIndex = 0;

            for (int i = 1; i < points.Length; ++i)
            {
                if (points[firstIndex].Y > points[i].Y)
                {
                    firstIndex = i;
                }
            }
            hull.Add(firstIndex);
            var firstPoint = points[firstIndex];

            var sortedPoints = new int[points.Length - 1];

            for (int i = 0; i < sortedPoints.Length; ++i)
            {
                if (i == firstIndex)
                {
                    sortedPoints[i] = points.Length - 1;
                }
                else
                {
                    sortedPoints[i] = i;
                }
            }

            Array.Sort(sortedPoints, (ia, ib) =>
            {
                var a    = points[ia];
                var b    = points[ib];
                var area = DirectedArea(firstPoint, a, b);
                if (area < -Epsillon)
                {
                    return(1);
                }
                if (area > Epsillon)
                {
                    return(-1);
                }
                return(0);
            });

            hull.Add(sortedPoints[0]);

            for (int i = 1; i < sortedPoints.Length; ++i)
            {
                var currIndex = sortedPoints[i];

                while (true)
                {
                    var area = DirectedArea(
                        points[hull.SecondLast],
                        points[hull.Last],
                        points[currIndex]);

                    if (area > Epsillon)
                    {
                        break;
                    }
                    hull.RemoveLast();
                }
                hull.Add(currIndex);
            }

            while (true)
            {
                var area = DirectedArea(
                    points[hull.SecondLast],
                    points[hull.Last],
                    points[hull.First]);

                if (area > Epsillon)
                {
                    break;
                }
                hull.RemoveLast();
            }

            return(hull);
        }