public static VrpData ReadData(string path)
        {
            var data = new VrpData();

            using (var reader = File.OpenText(path))
            {
                var line = reader.ReadLine().Split();

                data.N = int.Parse(line[0]);
                data.V = int.Parse(line[1]);
                data.C = int.Parse(line[2]);

                data.Customers = new Customer[data.N];

                for (int i = 0; i < data.N; i++)
                {
                    line = reader.ReadLine().Split();
                    data.Customers[i] = new Customer
                    {
                        Id = i,
                        Demand = int.Parse(line[0]),
                        Point = new Point2DReal(double.Parse(line[1]), double.Parse(line[2]))
                    };
                }
            }

            return data;
        }
        public ClustersModel GetKmeanClusters(VrpData data, int clustersCount)
        {
            var centers = new Point2DReal[clustersCount];
            var perm = Permutations.GetRandomPermutation(data.N - 1);
            for (int i = 0; i < clustersCount; i++)
            {
                centers[i] = data.Customers[perm[i] + 1].Point;
            }

            var colors = new int[data.N];
            var count = new int[clustersCount];

            var changed = true;
            while (changed)
            {
                changed = false;
                count = new int[clustersCount];
                for (int i = 1; i < data.N; i++)
                {
                    var bestCenter = -1;
                    var bestDist = 0D;
                    for (int j = 0; j < clustersCount; j++)
                    {
                        if (bestCenter == -1 || centers[j].Dist(data.Customers[i].Point) < bestDist)
                        {
                            bestCenter = j;
                            bestDist = centers[j].Dist(data.Customers[i].Point);
                        }
                    }
                    if (colors[i] != bestCenter)
                    {
                        colors[i] = bestCenter;
                        changed = true;
                    }
                }
                for (int j = 0; j < clustersCount; j++)
                {
                    var center = new Point2DReal(0, 0);
                    for (int i = 1; i < data.N; i++)
                    {
                        if (colors[i] == j)
                        {
                            center = center + data.Customers[i].Point;
                            count[j]++;
                        }
                    }
                    centers[j] = center * (1D / count[j]);
                }
            }

            var result = new ClustersModel();
            result.Color = colors;
            result.Count = count;
            result.Centers = centers;
            return result;
        }
        protected void CalcTotalDist(VrpData data, VrpResult result)
        {
            double totalDist = 0;
            foreach (var route in result.Routes)
            {
                for (int i = 0; i < route.Count - 1; i++)
                {
                    totalDist += data.Customers[route[i]].Point.Dist(data.Customers[route[i + 1]].Point);
                }
            }

            result.Dist = totalDist;
        }
        protected static void ApplyTsp(VrpData data, VrpResult result)
        {
            var tspSolver = new Opt2();
            for (int i = 0; i < data.V; i++)
            {
                var route = result.Routes[i];
                if (route.Count > 4)
                {
                    route.RemoveAt(route.Count - 1);
                    var measure =
                        new MatrixMeasureFactory().CreateMatrixMeasure(route.Select(x => data.Customers[x].Point).ToArray());
                    var tspRoute = tspSolver.GetPath(route.Count, measure);

                    int startIndex = 0;
                    for (int j = 0; j < tspRoute.Length; j++)
                    {
                        if (tspRoute[j] == 0)
                        {
                            startIndex = j;
                            break;
                        }
                    }
                    result.Routes[i] = new List<int>();
                    for (int j = startIndex; j < tspRoute.Length; j++)
                    {
                        result.Routes[i].Add(route[tspRoute[j]]);
                    }
                    for (int j = 0; j < startIndex; j++)
                    {
                        result.Routes[i].Add(route[tspRoute[j]]);
                    }
                    result.Routes[i].Add(0);
                }
                else
                {
                    result.Routes[i] = route;
                }
            }
        }
        public override VrpResult Solve(VrpData data)
        {
            var sortedCustomers = data.Customers.OrderByDescending(c => c.Demand).ToArray();

            var result = new VrpResult();
            result.Routes = new List<int>[data.V];
            var n = data.N;
            var v = data.V;
            var used = new bool[n];
            used[0] = true;

            for (int i = 0; i < v; i++)
            {
                var route = new List<int> { 0 };

                var cap = data.C;
                foreach (var customer in sortedCustomers.Where(c => !used[c.Id]).ToList())
                {
                    if (customer.Demand <= cap)
                    {
                        cap -= customer.Demand;
                        route.Add(customer.Id);
                        used[customer.Id] = true;
                    }
                }

                route.Add(0);
                result.Routes[i] = route;
            }

            ApplyTsp(data, result);

            CalcTotalDist(data, result);

            return result;
        }
        private void buttonUpload_Click(object sender, EventArgs e)
        {
            var dialog = new OpenFileDialog();
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var path = dialog.FileName;
                _vrpData = VrpData.ReadData(path);
                var minx = 1e30;
                var maxx = -1e30;
                var miny = 1e30;
                var maxy = -1e30;
                foreach (var point in _vrpData.Customers.Select(c => c.Point))
                {
                    minx = Math.Min(minx, point.X);
                    maxx = Math.Max(maxx, point.X);
                    miny = Math.Min(miny, point.Y);
                    maxy = Math.Max(maxy, point.Y);
                }

                foreach (var point in _vrpData.Customers.Select(c => c.Point))
                {
                    point.X = (point.X - minx) / (maxx - minx) * (m_Width - 60) + 30;
                    point.Y = (point.Y - miny) / (maxy - miny) * (m_Height - 60) + 30;
                }
                m_XScale = (maxx - minx) / (m_Width - 60);
                m_XShift = 30;
                m_YScale = (maxy - miny) / (m_Height - 60);
                m_YShift = 30;
            }

            GetPath();
            RefreshDraw();
        }
        public override VrpResult Solve(VrpData data)
        {
            VrpResult bestResult = null;
            var timelimit = BaseTimelimit;
            var stopwatch = Stopwatch.StartNew();

            /*var rnd = new Random();

            var pr = new double[data.V];
            for (int i = 0; i < data.V; i++)
            {
                pr[i] = 1D / (data.V - 1);
            }
            pr[0] = 0;*/

            while (true)
            {
                stopwatch.Stop();
                if (bestResult != null && (bestResult.Dist < 1400 || stopwatch.ElapsedMilliseconds > timelimit))
                {
                    break;
                }
                stopwatch.Start();

                /*var r = rnd.NextDouble() * pr.Sum();
                var sum = 0D;
                var v = 2;
                for (int i = 0; i < data.V; i++)
                {
                    sum += pr[i];
                    if (sum > r)
                    {
                        v = i + 1;
                        break;
                    }
                }*/
                var v = data.V;

                var baseClusters = GetKmeanClusters(data, v);

                var found = UpdateClustersBurn(data, baseClusters, v);

                if (!found)
                {
                    //pr[v - 1] /= 2;
                    continue;
                }

                var result = new VrpResult();
                result.Routes = new List<int>[data.V];
                for (int i = 0; i < data.V; i++)
                {
                    result.Routes[i] = new List<int> { 0 };
                }
                for (int i = 1; i < data.N; i++)
                {
                    result.Routes[baseClusters.Color[i]].Add(i);
                }
                for (int i = 0; i < data.V; i++)
                {
                    result.Routes[i].Add(0);
                }

                ApplyTsp(data, result);
                CalcTotalDist(data, result);

                if (bestResult == null || bestResult.Dist > result.Dist)
                {
                    //pr[v - 1] *= (1 << (data.V - v));
                    bestResult = result;
                }
            }

            return bestResult;
        }
        private bool UpdateClustersWarehouse(VrpData data, ClustersModel clusters, int v)
        {
            var solver = new GreedyHeuristic();

            var warehouseData = new WarehouseInputData();
            warehouseData.N = v;
            warehouseData.M = data.N;
            warehouseData.T = new double[data.N,v];
            for (int i = 0; i < data.N; i++)
            {
                for (int j = 0; j < v; j++)
                {
                    warehouseData.T[i, j] = clusters.Centers[j].Dist(data.Customers[i].Point);
                }
            }
            warehouseData.Consumers = data.Customers.Select(c => new Consumer {Demand = c.Demand, Id = c.Id}).ToArray();
            warehouseData.Warehouses = new global::Warehouse.Warehouse[v];
            for (int i = 0; i < v; i++)
            {
                warehouseData.Warehouses[i] = new global::Warehouse.Warehouse {Cap = data.C, Id = i, S = 0};
            }
            var result = solver.Solve(warehouseData);
            if (result.SolutionFound)
            {
                clusters.Color = result.Solution;
                return true;
            }
            return false;
        }
 private bool UpdateClustersMIP(VrpData data, ClustersModel baseClusters, int v)
 {
     throw new NotImplementedException();
 }
        // burn
        private static bool UpdateClustersBurn(VrpData data, ClustersModel baseClusters, int v)
        {
            var rnd = new Random();

            var found = false;

            for (int count = 0; count < 10; count++)
            {
                var clusters = new ClustersModel();
                clusters.Color = new int[data.N];
                Array.Copy(baseClusters.Color, clusters.Color, data.N);
                clusters.Count = new int[v];
                Array.Copy(baseClusters.Count, clusters.Count, v);
                clusters.Centers = new Point2DReal[v];
                for (int i = 0; i < v; i++)
                {
                    clusters.Centers[i] = new Point2DReal(baseClusters.Centers[i]);
                }

                var w = new int[v];
                for (int i = 0; i < data.N; i++)
                {
                    w[clusters.Color[i]] += data.Customers[i].Demand;
                }

                var baseOverweight = w.Sum(x => x > data.C ? x - data.C : 0);
                var overweight = baseOverweight;

                var radiuses = new double[v];
                for (int i = 0; i < data.N; i++)
                {
                    var color = clusters.Color[i];
                    var dist = data.Customers[i].Point.Dist(clusters.Centers[color]);
                    if (dist > radiuses[color])
                    {
                        radiuses[color] = dist;
                    }
                }
                var baseRadius = radiuses.Sum();
                var radius = baseRadius;

                var E = 1D;
                var minE = double.MaxValue;

                var bestColors = new int[data.N];
                var bestW = new int[v];
                for (int k = 0; k < 1000; k++)
                {
                    if (E < minE)
                    {
                        minE = E;
                        Array.Copy(clusters.Color, bestColors, data.N);
                        Array.Copy(w, bestW, v);
                    }

                    var id = rnd.Next(data.N);
                    var weight = data.Customers[id].Demand;
                    var newColor = rnd.Next(v - 1);
                    var oldColor = clusters.Color[id];
                    if (oldColor <= newColor)
                    {
                        newColor++;
                    }
                    var weightDelta = -(w[newColor] + weight > data.C ? w[newColor] + weight - data.C : 0)
                                      + (w[newColor] > data.C ? w[newColor] - data.C : 0)
                                      - (w[oldColor] - weight > data.C ? w[oldColor] - weight - data.C : 0)
                                      + (w[oldColor] > data.C ? w[oldColor] - data.C : 0);
                    var newOverweight = overweight - weightDelta;

                    var point = data.Customers[id].Point;
                    var newCenter = (clusters.Centers[newColor] * clusters.Count[newColor] + point)
                                    * (1D / (clusters.Count[newColor] + 1));
                    var newDist = point.Dist(newCenter);
                    var newRadius = radius;
                    if (newDist > radiuses[newColor])
                    {
                        newRadius += newDist - radiuses[newColor];
                    }

                    var newE = (double) newOverweight / baseOverweight + K * (newRadius - baseRadius) / baseRadius;

                    var alpha = rnd.NextDouble();

                    var T = 10 / (k + 1);
                    var h = Math.Exp(-(newE - E) / T);
                    if (alpha < h)
                    {
                        E = newE;
                        overweight = newOverweight;
                        radius = newRadius;
                        w[newColor] += weight;
                        w[oldColor] -= weight;
                        clusters.Color[id] = newColor;
                        clusters.Centers[newColor] = newCenter;
                        clusters.Centers[oldColor] = (clusters.Centers[oldColor] * clusters.Count[oldColor] - point)
                                                     * (1D / (clusters.Count[oldColor] - 1));
                        clusters.Count[newColor]++;
                        clusters.Count[oldColor]--;
                        if (newDist > radiuses[newColor])
                        {
                            radiuses[newColor] = newDist;
                        }
                    }
                }

                if (bestW.All(x => x <= data.C))
                {
                    found = true;
                    baseClusters.Color = bestColors;
                    break;
                }
            }
            return found;
        }
 public abstract VrpResult Solve(VrpData data);