private async Task <Partition> Subdivide(int lidx, int ridx)
        {
            Debug.Assert(lidx != ridx);
            if (ridx - lidx == 1) //2 points
            {
                QuadEdge edge = QuadEdge.MakeEdge(this._points[lidx], this._points[lidx + 1]);
                return(new Partition
                {
                    Left = edge,
                    Right = edge.Inverse
                });
            }
            if (ridx - lidx == 2) //3 points
            {
                QuadEdge a = QuadEdge.MakeEdge(this._points[lidx], this._points[lidx + 1]);
                QuadEdge b = QuadEdge.MakeEdge(this._points[lidx + 1], this._points[lidx + 2]);
                QuadEdge.Splice(a.Inverse, b);

                if (Point.IsCounterClockWise(this._points[lidx], this._points[lidx + 1], this._points[lidx + 2]))
                {
                    QuadEdge c = QuadEdge.ConnectLeft(b, a);
                    return(new Partition
                    {
                        Left = a,
                        Right = b.Inverse
                    });
                }
                if (Point.IsCounterClockWise(this._points[lidx], this._points[lidx + 2], this._points[lidx + 1]))
                {
                    QuadEdge c = QuadEdge.ConnectLeft(b, a);
                    return(new Partition
                    {
                        Left = c.Inverse,
                        Right = c
                    });
                }
                return(new Partition
                {
                    Left = a,
                    Right = b.Inverse
                });
            }
            int midx = (lidx + ridx) / 2;
            Task <Partition> leftTask  = Subdivide(lidx, midx);
            Task <Partition> rightTask = Subdivide(midx + 1, ridx);

            return(Merge(await leftTask, await rightTask));
        }
        private static List <Point> GetResult(Partition partition)
        {
            var      result   = new List <Point>();
            QuadEdge baseEdge = QuadEdge.ConnectLeft(partition.Left.Inverse, partition.Right);
            QuadEdge e        = baseEdge.DestinationNext.Inverse;
            Point    u        = baseEdge.Destination;

            while (true)
            {
                while (e != baseEdge && !CanMoveForward(e.DestinationNext, baseEdge))
                {
                    u = e.Destination;
                    e = e.DestinationNext.Inverse;
                }

                if (e != baseEdge)
                {
                    result.Add(e.Origin);
                    result.Add(e.Destination);
                }

                while (!CanMoveForward(e.OriginNext, baseEdge))
                {
                    result.Add(u);
                    if (u == baseEdge.Destination)
                    {
                        baseEdge.Delete();
                        return(result);
                    }
                    e = e.OriginNext.Inverse;
                    while (CanMoveForward(e.DestinationNext, baseEdge))
                    {
                        e = e.DestinationNext;
                    }
                    u = e.Origin;

                    result.Add(e.Origin);
                    result.Add(e.Destination);
                }
                e = e.OriginNext;
            }
        }
        private static Partition Merge(Partition leftPartition, Partition rightPartition)
        {
            QuadEdge left  = leftPartition.Left;                                                             //ldo
            QuadEdge right = rightPartition.Right;                                                           //rdo

            LowestCommonTangent(leftPartition, rightPartition, out QuadEdge lowLeft, out QuadEdge lowRight); //ldi, rdi

            QuadEdge edgeBase = QuadEdge.ConnectLeft(lowRight.Inverse, lowLeft);
            QuadEdge lcand    = edgeBase.RightPrevious;
            QuadEdge rcand    = edgeBase.OriginPrevious;

            if (edgeBase.Origin == right.Origin)
            {
                right = edgeBase;
            }
            if (edgeBase.Destination == left.Origin)
            {
                left = edgeBase.Inverse;
            }

            while (true)
            {
                QuadEdge temp = lcand.OriginNext;
                if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination))
                {
                    while (edgeBase.Origin.InCircle(lcand.Destination, temp.Destination, lcand.Origin))
                    {
                        lcand.Delete();
                        lcand = temp;
                        temp  = lcand.OriginNext;
                    }
                }

                temp = rcand.OriginPrevious;
                if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination))
                {
                    while (edgeBase.Destination.InCircle(temp.Destination, rcand.Destination, rcand.Origin))
                    {
                        rcand.Delete();
                        rcand = temp;
                        temp  = rcand.OriginPrevious;
                    }
                }

                bool leftValid  = Point.IsCounterClockWise(edgeBase.Origin, lcand.Destination, edgeBase.Destination);
                bool rightValid = Point.IsCounterClockWise(edgeBase.Origin, rcand.Destination, edgeBase.Destination);
                if (!leftValid && !rightValid)
                {
                    break;
                }

                if (!leftValid ||
                    rightValid && rcand.Destination.InCircle(lcand.Destination, lcand.Origin, rcand.Origin))
                {
                    edgeBase = QuadEdge.ConnectLeft(rcand, edgeBase.Inverse);
                    rcand    = edgeBase.Inverse.LeftNext;
                }
                else
                {
                    edgeBase = QuadEdge.ConnectRight(lcand, edgeBase).Inverse;
                    lcand    = edgeBase.RightPrevious;
                }
            }

            return(new Partition
            {
                Left = left,
                Right = right
            });
        }