/// <summary>
        /// Measures distance between two points
        /// </summary>
        public static double Dist(ClusterGame x, ClusterGame y)
        {
            double diffNA = y._NAsales - x._NAsales;
            double diffEU = y._EUsales - x._EUsales;

            return(diffNA * diffNA + diffEU * diffEU);
        }
        /// <summary>
        /// Returns list of points in selected region around the specified point
        /// </summary>
        private static List <ClusterGame> GetNeighbours(List <ClusterGame> points, ClusterGame p, double region)
        {
            List <ClusterGame> reg = new List <ClusterGame>();

            for (int i = 0; i < points.Count; i++)
            {
                double dist = Dist(p, points[i]);
                if (dist <= region)
                {
                    reg.Add(points[i]);
                }
            }
            return(reg);
        }
        /// <summary>
        /// DBSCAN clustering function on the list of points
        /// </summary>
        private static List <List <ClusterGame> > StartClustering(List <ClusterGame> points, double region, int min_points)
        {
            region = square(region);
            int ClstId = 1;

            if (points == null)
            {
                return(null);
            }
            List <List <ClusterGame> > clusters = new List <List <ClusterGame> >();

            for (int counter = 0; counter < points.Count; counter++)
            {
                ClusterGame p = points[counter];
                if (p._cluster == ClusterGame._checked)
                {
                    if (CheckCluster(points, p, ClstId, region, min_points))
                    {
                        ClstId++;
                    }
                }
            }

            // Assign point to clusters
            int max = points.OrderBy
                          (p => p._cluster).Last()._cluster;

            if (max < 1)
            {
                return(clusters);
            }

            for (int i = 0; i < max; i++)
            {
                clusters.Add(new List <ClusterGame>());
            }

            foreach (ClusterGame p in points)
            {
                if (p._cluster > 0)
                {
                    clusters[p._cluster - 1].Add(p);
                }
            }

            return(clusters);
        }
        /// <summary>
        /// Check if the cluster can be expanded
        /// </summary>
        private static bool CheckCluster(List <ClusterGame> points, ClusterGame p, int clusterId, double region, int min_points)
        {
            List <ClusterGame> neighboursList = GetNeighbours(points, p, region);

            // mark point as NOISE if the count of neighbours is bellow treshold
            if (neighboursList.Count < min_points)
            {
                MarkNoise(p);
                return(false);
            }
            //Expand cluster if the count of neighbours is above treshold
            else
            {
                //Remove points from the list
                for (int i = 0; i < neighboursList.Count; i++)
                {
                    neighboursList[i]._cluster = clusterId;
                }
                neighboursList.Remove(p);
                //Repeat for every neighbour point
                while (neighboursList.Count > 0)
                {
                    ClusterGame current = neighboursList[0];

                    List <ClusterGame> result = GetNeighbours(points, current, region);
                    if (result.Count >= min_points)
                    {
                        for (int i = 0; i < result.Count; i++)
                        {
                            ClusterGame res = result[i];
                            if (res._cluster == ClusterGame._checked ||
                                res._cluster == ClusterGame._isnoise)
                            {
                                if (res._cluster == ClusterGame._checked)
                                {
                                    neighboursList.Add(res);
                                }
                                res._cluster = clusterId;
                            }
                        }
                    }
                    neighboursList.Remove(current);
                }
                return(true);
            }
        }
 private static void MarkNoise(ClusterGame p)
 {
     p._cluster = ClusterGame._isnoise;
 }
        /// <summary>
        /// Returns cluster with the biggest amount of point aka best candidate
        /// </summary>
        private static List <ClusterGame> BestCluster(List <ClusterGame> points, double maxDiameter)
        {
            double diametersqrd           = maxDiameter * maxDiameter;
            List <List <ClusterGame> > c  = new List <List <ClusterGame> >();
            List <ClusterGame>         cc = null;

            int[] candidateNumbers       = new int[points.Count];
            int   totalPointsAllocated   = 0;
            int   currentCandidateNumber = 0;

            for (int i = 0; i < points.Count; i++)
            {
                if (totalPointsAllocated == points.Count)
                {
                    break;
                }
                if (candidateNumbers[i] > 0)
                {
                    continue;
                }
                currentCandidateNumber++;
                cc = new List <ClusterGame>();
                cc.Add(points[i]);
                candidateNumbers[i] = currentCandidateNumber;
                totalPointsAllocated++;
                ClusterGame latestPoint      = points[i];
                double[]    diametersSquared = new double[points.Count];


                while (true)
                {
                    if (totalPointsAllocated == points.Count)
                    {
                        break;
                    }
                    int    closest            = -1;
                    double minDiameterSquared = Int32.MaxValue;
                    for (int j = i + 1; j < points.Count; j++)
                    {
                        if (candidateNumbers[j] > 0)
                        {
                            continue;
                        }

                        double distSquared = Dist(latestPoint, points[j]);
                        if (distSquared > diametersSquared[j])
                        {
                            diametersSquared[j] = distSquared;
                        }

                        if (diametersSquared[j] < minDiameterSquared)
                        {
                            minDiameterSquared = diametersSquared[j];
                            closest            = j;
                        }
                    }


                    if ((double)minDiameterSquared <= diametersqrd)
                    {
                        cc.Add(points[closest]);
                        candidateNumbers[closest] = currentCandidateNumber;
                        totalPointsAllocated++;
                    }
                    else
                    {
                        break;
                    }
                }
                c.Add(cc);
            }
            int maxPoints           = -1;
            int bestCandidateNumber = 0;

            for (int i = 0; i < c.Count; i++)
            {
                if (c[i].Count > maxPoints)
                {
                    maxPoints           = c[i].Count;
                    bestCandidateNumber = i + 1;
                }
            }
            for (int i = candidateNumbers.Length - 1; i >= 0; i--)
            {
                if (candidateNumbers[i] == bestCandidateNumber)
                {
                    points.RemoveAt(i);
                }
            }
            return(c[bestCandidateNumber - 1]);
        }