using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace CommonUsage.Mathematics
{

    using T3 = Tuple<float, float, float>;
    using D3 = Tuple<double, double, double>;

    public class CommonMath
    {
	    public class PrimeEnumerator<T>
	    {
		    public PrimeEnumerator(List<T> items, Func<T, bool> process)
		    {
                _n = items.Count;
                _items = items;
                _process = process;

                foreach (var pNum in _primes)
                {
	                if (_n % pNum != 0)
	                {
		                _a = pNum;
		                _b = 11;
		                break;
	                }
                }
			}

		    public void Enumerate()
		    {
			    using (var enumerator = Get().GetEnumerator())
			    {
                    while (enumerator.MoveNext()) { }
			    }
		    }

		    private readonly int _n, _a, _b;
		    private readonly int[] _primes = new[] { 29, 23, 19, 17, 13 };
		    private List<T> _items;
            private readonly Func<T, bool> _process;

		    private IEnumerable<bool> Get()
		    {
			    for (var i = 0; i < _n; ++i)
			    {
				    var id = (i * _a + _b) % _n;
				    yield return _process(_items[id]);
			    }
			}
	    }

        private static IEnumerable<IEnumerable<T>> GetPermutationsInternal<T>(IEnumerable<T> list, int length)
        {
            if (length == 1) return list.Select(t => new T[] { t });

            return GetPermutationsInternal(list, length - 1)
                .SelectMany(t => list.Where(e => !t.Contains(e)),
                    (t1, t2) => t1.Concat(new T[] { t2 }));
        }

        /// <summary>
        /// 得到一组数据的所有排列。
        /// </summary>
        /// <typeparam name="T">元素数据类型</typeparam>
        /// <param name="list">所有待选元素</param>
        /// <param name="selectNum">所选出的元素数量</param>
        /// <returns></returns>
        public static List<List<T>> GetPermutations<T>(List<T> list, int selectNum)
        {
            return GetPermutationsInternal(list, selectNum).Select(ll => ll.ToList()).ToList();
        }

        public static (float bias, Vector2 hPnt, float d) Project2DLine(Vector2 pnt, Vector2 segSt,
            Vector2 segEnd)
        {
            var dir = Vector2.Normalize(segEnd - segSt);
            var fd = Vector2.Dot(pnt - segSt, dir);
            var hPnt = segSt + fd * dir;
            var bias = dir.X * (pnt.Y-segSt.Y) - (pnt.X-segSt.X) * dir.Y;
            return (bias, hPnt, fd);
        }

        public static (float bias, Vector2 hPnt, float fd) Project2DLine(Vector2 pnt, Vector2 segSt, float tangent)
        {
	        var dir = new Vector2((float)System.Math.Cos(tangent / 180 * System.Math.PI), (float)System.Math.Sin(tangent / 180 * System.Math.PI));
	        var fd = Vector2.Dot(pnt - segSt, dir);
	        var hPnt = segSt + fd * dir;
	        var bias = dir.X * (pnt.Y - segSt.Y) - (pnt.X - segSt.X) * dir.Y;
	        return (bias, hPnt, fd);
		}

        public class LineEqu
        {
            public double A, B, C, ln, dAB;
            public double px1, px2, py1, py2;
            public float midX;
            public float midY;
        }
        // Fit line with PCA.
        public LineEqu CalcLine(IEnumerable<Vector2> tls)
        {
            var lidarPoint2Ds = tls as Vector2[] ?? tls.ToArray();
            float fx = lidarPoint2Ds.Average(f => f.X);
            float fy = lidarPoint2Ds.Average(f => f.Y);
            float fxx = lidarPoint2Ds.Average(f => f.X * f.X);
            float fxy = lidarPoint2Ds.Average(f => f.X * f.Y);
            float fyy = lidarPoint2Ds.Average(f => f.Y * f.Y);
            float a = fxx - fx * fx, b = fxy - fx * fy, c = fyy - fy * fy;
            double sqt = System.Math.Sqrt((a - c) * (a - c) + 4 * b * b);
            double l1 = a + c + sqt;
            double l2 = a + c - sqt;
            double dx, dy;
            if (System.Math.Abs(a - l1 / 2) > System.Math.Abs(c - l1 / 2))
            {
                dy = l1 / 2 - a; dx = b;
            }
            else
            {
                dx = l1 / 2 - c; dy = b;
            }
            double norm = System.Math.Sqrt(dx * dx + dy * dy);
            dx /= norm; dy /= norm;
            double A = dy, B = -dx, C = dx * fy - dy * fx;
            double dAB = System.Math.Sqrt(A * A + B * B);

            return new CommonMath.LineEqu
            {
                A = A,
                B = B,
                C = C,
                ln = lidarPoint2Ds.Average(p => System.Math.Abs(p.X * A + p.Y * B + C) / dAB),
                midX = fx,
                midY = fy
            };
        }

        public static double QuadInterp3(double[] confsF)
        {
            if (confsF[0] > confsF[1] && confsF[0] > confsF[2])
            {
                //printf("left overflow...\n");
                return -1;
            }

            if (confsF[1] > confsF[0] && confsF[1] > confsF[2])
            {
                return (-(confsF[2] - confsF[0]) / 2.0f / (confsF[0] + confsF[2] - 2.0f * confsF[1] + 0.0001f));
            }
            if (confsF[2] > confsF[0] && confsF[2] > confsF[1])
            {
                //printf("right overflow...\n");
                return 1;
            }
            return 0;
        }

        public static double cross(PointF O, PointF A, PointF B)
        {
            return (A.X - O.X) * (B.Y - O.Y) - (A.Y - O.Y) * (B.X - O.X);
        }

        public static List<PointF> GetConvexHull(List<PointF> points)
        {
            if (points == null)
                return null;

            if (points.Count() <= 1)
                return points;

            int n = points.Count(), k = 0;
            List<PointF> H = new List<PointF>(new PointF[2 * n]);

            points.Sort((a, b) =>
                a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X));

            // Build lower hull
            for (int i = 0; i < n; ++i)
            {
                while (k >= 2 && cross(H[k - 2], H[k - 1], points[i]) <= 0)
                    k--;
                H[k++] = points[i];
            }

            // Build upper hull
            for (int i = n - 2, t = k + 1; i >= 0; i--)
            {
                while (k >= t && cross(H[k - 2], H[k - 1], points[i]) <= 0)
                    k--;
                H[k++] = points[i];
            }

            return H.Take(k - 1).ToList();
        }

        public static bool IsPointInPolygon4(PointF[] polygon, PointF testPoint)
        {
            // ray casting odd even test.
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++)
            {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y ||
                    polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
                {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) *
                        (polygon[j].X - polygon[i].X) < testPoint.X)
                    {
                        result = !result;
                    }
                }

                j = i;
            }

            return result;
        }

        public static double Exp(double val)
        {
            if (val < -20) return 0.0000001;
            if (val > 20) return 99999999999999;
            long tmp = (long)(1512775 * val + 1072632447);
            return BitConverter.Int64BitsToDouble(tmp << 32);
        }
        public static double gaussmf(double x, double sig, double c)
        {
            return Exp(-(x - c) * (x - c) / (2 * sig * sig));
        }

        public static float Exp(float x)
        {
            if (x < -10) return 0;
            if (x > 10) return 99999999999999;
            x = 1.0f + x / 64f;
            x *= x;
            x *= x;
            x *= x;
            x *= x;
            x *= x;
            x *= x;
            return x;
        }
        public static float gaussmf(float x, float sig, float c)
        {
            return Exp(-(x - c) * (x - c) / (2 * sig * sig));
        }

        public static D3 Transform2D(D3 src, D3 t)
        {
            var rth = src.Item3 / 180.0 * System.Math.PI;
            var p1dtx = (src.Item1 + System.Math.Cos(rth) * t.Item1 -
                                System.Math.Sin(rth) * t.Item2);
            var p1dty = (src.Item2 + System.Math.Sin(rth) * t.Item1 +
                                System.Math.Cos(rth) * t.Item2);
            var p1dtth = src.Item3 + t.Item3;
            return Tuple.Create(p1dtx, p1dty, p1dtth);
        }

        public struct LngLatToXY
        {
            public double scale;
            public double rad;
            public double biasX, biasY;
        }

        public static LngLatToXY GetTransformLngLatToXY(Vector2 lnglat1, Vector2 xy1, Vector2 lnglat2, Vector2 xy2)
        {
            var scale = (xy1 - xy2).Length() / (lnglat1 - lnglat2).Length();
            var dxy = (xy1 - xy2);
            var dlnglat = lnglat1 - lnglat2;
            var rad = System.Math.Atan2(dxy.X, dxy.Y) - System.Math.Atan2(dlnglat.X, dlnglat.Y);
            var intm = lnglat1 * scale;
            var biasX = xy1.X - (intm.X * System.Math.Cos(rad) - intm.Y * System.Math.Sin(rad));
            var biasY = xy1.Y - (intm.X * System.Math.Sin(rad) + intm.Y * System.Math.Cos(rad));
            return new LngLatToXY {rad = rad, biasX = biasX, biasY = biasY, scale = scale};
        }

        public Vector2 TransformLngLatToXY(Vector2 lnglat, LngLatToXY t)
        {
            var intm = lnglat * (float) t.scale;
            return new Vector2((float) (intm.X * System.Math.Cos(t.rad) - intm.Y * System.Math.Sin(t.rad) + t.biasX),
                (float) (intm.X * System.Math.Sin(t.rad) + intm.Y * System.Math.Cos(t.rad) + t.biasY));
        }

        public static D3 ReverseTransform(D3 dest, D3 t)
        {
            var rth = (dest.Item3 - t.Item3) / 180.0 * System.Math.PI;
            var nxT = (dest.Item1 - System.Math.Cos(rth) * t.Item1 +
                              System.Math.Sin(rth) * t.Item2);
            var nyT = (dest.Item2 - System.Math.Sin(rth) * t.Item1 -
                              System.Math.Cos(rth) * t.Item2);
            var pth = dest.Item3 - t.Item3;
            return Tuple.Create(nxT, nyT, pth);
        }

        public static D3 SolveTransform2D(D3 src, D3 dest)
        {
            var th = dest.Item3 - src.Item3;
            th = (th - System.Math.Round((th) / 360.0f) * 360);
            var rth = src.Item3 / 180.0 * System.Math.PI;
            var x = ((dest.Item1 - src.Item1) * System.Math.Cos(rth) +
                            (dest.Item2 - src.Item2) * System.Math.Sin(rth));
            var y = (-(dest.Item1 - src.Item1) * System.Math.Sin(rth) +
                            (dest.Item2 - src.Item2) * System.Math.Cos(rth));
            return Tuple.Create(x, y, th);
        }

        public static T3 Transform2D(T3 src, T3 t)
        {
            var rth = src.Item3 / 180.0 * System.Math.PI;
            var p1dtx = (float)(src.Item1 + System.Math.Cos(rth) * t.Item1 -
                                System.Math.Sin(rth) * t.Item2);
            var p1dty = (float)(src.Item2 + System.Math.Sin(rth) * t.Item1 +
                                System.Math.Cos(rth) * t.Item2);
            var p1dtth = src.Item3 + t.Item3;
            return Tuple.Create(p1dtx, p1dty, p1dtth);
        }

        public static Vector2 Transform2D(Vector3 src, Vector3 t)
        {
	        var tup = Transform2D(Tuple.Create(src.X, src.Y, src.Z), Tuple.Create(t.X, t.Y, t.Z));
	        return new Vector2(tup.Item1, tup.Item2);
        }

        public static Vector2 Transform2D(Vector2 srcPos, float srcTh, Vector2 dt, float dth = 0)
        {
	        var tup = Transform2D(Tuple.Create(srcPos.X, srcPos.Y, srcTh), Tuple.Create(dt.X, dt.Y, dth));
	        return new Vector2(tup.Item1, tup.Item2);
		}

		public static T3 ReverseTransform(T3 dest, T3 t)
        {
            var rth = (dest.Item3 - t.Item3) / 180.0 * System.Math.PI;
            var nxT = (float)(dest.Item1 - System.Math.Cos(rth) * t.Item1 +
                              System.Math.Sin(rth) * t.Item2);
            var nyT = (float)(dest.Item2 - System.Math.Sin(rth) * t.Item1 -
                              System.Math.Cos(rth) * t.Item2);
            var pth = dest.Item3 - t.Item3;
            return Tuple.Create(nxT, nyT, pth);
        }

        public static T3 SolveTransform2D(T3 src, T3 dest)
        {
            var th = dest.Item3 - src.Item3;
            th = (float)(th - System.Math.Round((th) / 360.0f) * 360);
            var rth = src.Item3 / 180.0 * System.Math.PI;
            var x = (float)((dest.Item1 - src.Item1) * System.Math.Cos(rth) +
                            (dest.Item2 - src.Item2) * System.Math.Sin(rth));
            var y = (float)(-(dest.Item1 - src.Item1) * System.Math.Sin(rth) +
                            (dest.Item2 - src.Item2) * System.Math.Cos(rth));
            return Tuple.Create(x, y, th);
        }

		public static Vector2 SolveTransform2D(Vector2 srcPos, float srcTh, Vector2 dt, float dth = 0)
		{
			var tup = SolveTransform2D(Tuple.Create(srcPos.X, srcPos.Y, srcTh), Tuple.Create(dt.X, dt.Y, dth));
			return new Vector2(tup.Item1, tup.Item2);
		}

		public static double dist(double x1, double y1, double x2, double y2)
        {
            return System.Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct FloatIntUnion
        {
            [FieldOffset(0)] public float f;

            [FieldOffset(0)] public int tmp;
        }

        public static float Sqrt(float z)
        {
            FloatIntUnion u;
            u.tmp = 0;
            u.f = z;
            u.tmp -= 1 << 23; /* Subtract 2^m. */
            u.tmp >>= 1; /* Divide by 2. */
            u.tmp += 1 << 29; /* Add ((b + 1) / 2) * 2^m. */
            return u.f;
        }

        public static float dist2(float x1, float y1, float x2, float y2)
        {
            return ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }


        public static float d2(float x1, float y1, float x2, float y2)
        {
            return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
        }

        public static float ThAverage(List<float> angles)
        {
	        var anchor = angles[0];
	        var diff = 0f;
	        foreach (var angle in angles)
		        diff += ThDiff(angle, anchor);
	        return RoundTh(anchor + diff / angles.Count);
        }

        public static float ThDiff(float th1, float th2)
        {
            return (float)(th1 - th2 -
                           System.Math.Round((th1 - th2) / 360.0f) * 360);
        }

        public static double ThDiff(double th1, double th2)
        {
            return th1 - th2 -
                           System.Math.Round((th1 - th2) / 360.0f) * 360;
        }

        public static double refine(double x)
        {
            if (x < 1 && x > -1) return x;
            if (x > 1)
                return (2 / (1 + System.Math.Exp(-((x - 1) * 2))));
            return (2 / (1 + System.Math.Exp(-((x + 1) * 2)))) - 2;
        }

        /// <summary>
        /// 求点p到两点式直线p1p2的距离
        /// </summary>
        /// <param name="x">点p的x坐标</param>
        /// <param name="y">点p的y坐标</param>
        /// <param name="x1">直线点p1的x坐标</param>
        /// <param name="y1">直线点p1的y坐标</param>
        /// <param name="x2">直线点p2的x坐标</param>
        /// <param name="y2">直线点p2的y坐标</param>
        /// <returns></returns>
        public static double Point2LineDist(double x, double y, double x1, double y1, double x2, double y2)
        {
            double a1 = -(y1 - y2) / 10;
            double b1 = (x1 - x2) / 10;
            double c1 = (x1 * (y1 - y2) - y1 * (x1 - x2)) / 10;
            return System.Math.Abs(a1 * x + b1 * y + c1) / System.Math.Sqrt(a1 * a1 + b1 * b1);
        }

        public static double Point2LineDist(Vector2 p, LineSegment ll)
        {
            double a1 = -(ll.Src.Y - ll.Dst.Y) / 10;
            double b1 = (ll.Src.X - ll.Dst.X) / 10;
            double c1 = (ll.Src.X * (ll.Src.Y - ll.Dst.Y) - ll.Src.Y * (ll.Src.X - ll.Dst.X)) / 10;
            return System.Math.Abs(a1 * p.X + b1 * p.Y + c1) / System.Math.Sqrt(a1 * a1 + b1 * b1);
        }

        /// <summary>
        /// 两条两点式直线间的夹角
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="x3"></param>
        /// <param name="y3"></param>
        /// <param name="x4"></param>
        /// <param name="y4"></param>
        /// <returns>角度制</returns>
        public static double AngleBetweenLines(double x1, double y1, double x2, double y2, double x3, double y3,
            double x4, double y4)
        {
            var vec1 = new Vector2((float)(x2 - x1), (float)(y2 - y1));
            var vec2 = new Vector2((float)(x4 - x3), (float)(y4 - y3));
            return System.Math.Acos(System.Math.Abs(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length())) / System.Math.PI * 180;
        }

        public static double AngleBetweenLines(LineSegment ls1, LineSegment ls2)
        {
	        return AngleBetweenLines(ls1.Src.X, ls1.Src.Y, ls1.Dst.X, ls1.Dst.Y, ls2.Src.X, ls2.Src.Y, ls2.Dst.X,
		        ls2.Dst.Y);
        }

		/// <summary>
		/// 两向量间夹角
		/// </summary>
		/// <param name="x1"></param>
		/// <param name="y1"></param>
		/// <param name="x2"></param>
		/// <param name="y2"></param>
		/// <param name="x3"></param>
		/// <param name="y3"></param>
		/// <param name="x4"></param>
		/// <param name="y4"></param>
		/// <returns>角度制</returns>
		public static double AngleBetweenVectors(double x1, double y1, double x2, double y2, double x3, double y3,
            double x4, double y4)
        {
            var vec1 = new Vector2((float)(x2 - x1), (float)(y2 - y1));
            var vec2 = new Vector2((float)(x4 - x3), (float)(y4 - y3));
            return System.Math.Acos(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length()) / System.Math.PI * 180;
        }

        /// <summary>
        /// 两向量间夹角.
        /// </summary>
        /// <param name="vec1"></param>
        /// <param name="vec2"></param>
        /// <returns>角度制</returns>
        public static double AngleBetweenVectors(Vector2 vec1, Vector2 vec2)
        {
            return System.Math.Acos(Vector2.Dot(vec1, vec2) / vec1.Length() / vec2.Length()) / System.Math.PI * 180;
        }

        public static double AngleBetweenVectors(Vector3 vector1, Vector3 vector2)
        {
	        float dotProduct = Vector3.Dot(vector1, vector2);
	        float magnitude1 = vector1.Length();
	        float magnitude2 = vector2.Length();
	        float cosine = dotProduct / (magnitude1 * magnitude2);

	        return System.Math.Acos(cosine) / System.Math.PI * 180;
        }

		/// <summary>
		/// 求点到直线的垂足
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="x1"></param>
		/// <param name="y1"></param>
		/// <param name="x2"></param>
		/// <param name="y2"></param>
		/// <returns></returns>
		public static (double, double) PerpendicularPoint(double x, double y, double x1, double y1, double x2, double y2)
        {
            double lx = x2 - x1, ly = y2 - y1, dAB = lx * lx + ly * ly;
            var u = ((x - x1) * lx + (y - y1) * ly) / dAB;
            return new(x1 + u * lx, y1 + u * ly);
        }

        public static Vector2 PerpendicularPoint(Vector2 p, LineSegment ls)
        {
            double lx = ls.Dst.X - ls.Src.X, ly = ls.Dst.Y - ls.Src.Y, dAB = lx * lx + ly * ly;
            var u = ((p.X - ls.Src.X) * lx + (p.Y - ls.Src.Y) * ly) / dAB;
            return new Vector2((float)(ls.Src.X + u * lx), (float)(ls.Src.Y + u * ly));
        }

        public static double PerpendicularPosition(double x, double y, double x1, double y1, double x2, double y2)
        {
            double lx = x2 - x1, ly = y2 - y1;
            var dAB = CommonMath.Sqrt((float)(lx * lx + ly * ly));
            lx /= dAB;
            ly /= dAB;
            return (x - x1) * lx + (y - y1) * ly;
        }


        /// <summary>
        /// 最小二乘法拟合直线，得到两点式。
        /// </summary>
        /// <param name="pts">待拟合的点集，应至少有2个点。</param>
        /// <param name="maxDist2Line">检查是否所有点距直线的距离均小于maxDist2Line，若为-1则不检查。</param>
        /// <returns>返回两点式的两个端点坐标。若坐标为全0，则拟合失败。</returns>
        public static (bool, Vector2, Vector2) FitLineSegment(List<Vector2> pts, double maxDist2Line = -1)
        {
            if (pts.Count < 2)
            {
                Console.WriteLine($"Points too Few! {pts.Count}! Cannot perform line fitting!",
                    MethodBase.GetCurrentMethod()?.Name ?? "FitLine");
                return (false, Vector2.Zero, Vector2.Zero);
            };

            // y = kx + b
            double A = 0, B = 0, C = 0, D = 0;
            foreach (var p in pts)
            {
                A += p.X * p.X;
                B += p.X;
                C += p.X * p.Y;
                D += p.Y;
            }

            var tmp = A * pts.Count - B * B;
            var k = (C * pts.Count - B * D) / tmp;
            var b = (A * D - C * B) / tmp;

            double x1 = 0,
                y1 = k * x1 + b,
                x2 = 1000,
                y2 = k * x2 + b;

            double CalcDist(ref bool fail, ref Vector2 endP, ref Vector2 endQ)
            {
                double distSum = 0;
                double lx = x2 - x1, ly = y2 - y1, dAB = lx * lx + ly * ly;
                double minU = double.MaxValue, maxU = double.MinValue;

                foreach (var p in pts)
                {
                    var u = ((p.X - x1) * lx + (p.Y - y1) * ly) / dAB;
                    var perp = new Vector2((float)(x1 + u * lx), (float)(y1 + u * ly));
                    if (u < minU)
                    {
                        endP = perp;
                        minU = u;
                    }
                    if (u > maxU)
                    {
                        endQ = perp;
                        maxU = u;
                    }

                    var curDist = dist(perp.X, perp.Y, p.X, p.Y);
                    if (maxDist2Line > -1 && curDist > maxDist2Line) fail = true;
                    distSum += curDist;
                }

                return distSum;
            }

            var kbFail = false;
            Vector2 endP1 = new Vector2(), endQ1 = new Vector2();
            double kbDist = CalcDist(ref kbFail, ref endP1, ref endQ1);
            
            // x = my + n
            A = 0;
            B = 0;
            C = 0;
            D = 0;
            foreach (var p in pts)
            {
                A += p.X * p.Y;
                B += p.Y * p.Y;
                C += p.Y;
                D += p.X;
            }

            tmp = C * C - B * pts.Count;
            var m = (C * D - A * pts.Count) / tmp;
            var n = (A * C - B * D) / tmp;
            y1 = 0;
            x1 = m * y1 + n;
            y2 = 1000;
            x2 = m * y2 + n;

            var mnFail = false;
            Vector2 endP2 = new Vector2(), endQ2 = new Vector2();
            double mnDist = CalcDist(ref mnFail, ref endP2, ref endQ2);

            Vector2 endP = endP1, endQ = endQ1;
            if (mnDist < kbDist)
            {
                if (mnFail) return (false, new Vector2(), new Vector2());
                endP = endP2;
                endQ = endQ2;
            }
            else if (kbFail) return (false, new Vector2(), new Vector2());

            return (true, endP, endQ);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int toId(int x, int y, int z)
        {
            return (x * 1140671485 + 12820163) ^ (y * 134775813 + 1) ^ (z * 1103515245 + 12345);
        }

        public class Clustering<T>
        {
	        public int numIteration = 3;
	        public int itemNumThreshold = 10;

	        public Func<T, T, bool> inRange;
	        public Func<List<T>, T> average;
	        private readonly List<T> _inputData;
	        private Dictionary<T, List<T>> _clusters = new Dictionary<T, List<T>>();

	        public Clustering(List<T> data, Func<T, T, bool> inRange, Func<List<T>, T> average)
	        {
		        _inputData = data;
		        this.inRange = inRange;
		        this.average = average;
	        }

	        public Dictionary<T, List<T>> GetClusters()
	        {
		        var tmp = new List<(T center, List<T> items)>();

		        for (var iter = 0; iter < numIteration; iter++)
		        {
			        tmp = tmp.Where(cluster => cluster.items.Count > itemNumThreshold)
				        .Select(cluster => (average(cluster.items), new List<T>())).ToList();

			        foreach (var data in _inputData)
			        {
				        var added = false;
				        foreach (var cluster in tmp)
				        {
					        if (inRange(cluster.center, data))
					        {
						        cluster.items.Add(data);
						        added = true;
						        break;
					        }
				        }

				        if (!added)
					        tmp.Add((data, new List<T>() { data }));
			        }
		        }

		        _clusters = tmp.Where(cluster => cluster.items.Count > itemNumThreshold)
			        .ToDictionary(cluster => cluster.center, cluster => cluster.items);
		        return _clusters;
	        }
        }

        public static (bool, Vector2) TwoLinesIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D)
        {
	        // Line AB represented as a1x + b1y = c1
	        double a1 = B.Y - A.Y;
	        double b1 = A.X - B.X;
	        double c1 = a1 * (A.X) + b1 * (A.Y);

	        // Line CD represented as a2x + b2y = c2
	        double a2 = D.Y - C.Y;
	        double b2 = C.X - D.X;
	        double c2 = a2 * (C.X) + b2 * (C.Y);

	        double determinant = a1 * b2 - a2 * b1;

	        if (determinant == 0)
	        {
		        // The lines are parallel. This is simplified
		        // by returning a pair of FLT_MAX
		        return new(false, new Vector2());
	        }
	        else
	        {
		        double x = (b2 * c1 - b1 * c2) / determinant;
		        double y = (a1 * c2 - a2 * c1) / determinant;
		        return (true, new Vector2((float)x, (float)y));
	        }
        }

        public static bool IsAtLeft(Vector2 anchor, Vector2 dest, Vector2 p)
        {
	        var v1 = new Vector3(anchor - p, 0);
	        var v2 = new Vector3(dest - p, 0);
	        return Vector3.Cross(v1, v2).Z > 0;
        }

        /// <summary>
        /// 将角度转化至-180到180度的范围内。
        /// </summary>
        /// <param name="th"></param>
        /// <returns></returns>
        public static double RoundTh(double th)
        {
	        return th - System.Math.Round(th / 360) * 360;
        }

        /// <summary>
        /// 将角度转化至-180到180度的范围内。
        /// </summary>
        /// <param name="th"></param>
        /// <returns></returns>
        public static float RoundTh(float th)
        {
	        return th - (float)System.Math.Round(th / 360f) * 360f;
        }
	}
}
