public void FixIt(TspSolution solution)
        {
            var route = solution.Route;

            double largestDistance = 0;
            TsPoint largest1 = null;
            TsPoint largest2 = null;
            for (var i = 0; i < route.Count; ++i)
            {
                TsPoint last;

                if (i == 0)
                {
                    last = solution.Route[i];
                    ++i;
                }
                else
                {
                    last = solution.Route[i - 1];
                }
                TsPoint current = solution.Route[i];
                var distance = last.DistanceFrom(current);
                if (distance > largestDistance)
                {
                    largestDistance = distance;
                    largest1 = last;
                    largest2 = current;
                }
            }

            Console.WriteLine("largest distance is between {0} and {1} with distance {2}", largest1, largest2, largestDistance);

            var closest1 = solution.Route.Aggregate(solution.Route.First(p => p != largest1),
                                                    (min, curr) =>
                                                    curr != largest1 && (curr.DistanceFrom(largest1) < min.DistanceFrom(largest1)) ? curr : min);

            Console.WriteLine("closest to {0} is {1} with distance of {2}", largest1, closest1, closest1.DistanceFrom(largest1));

            var closest2 = solution.Route.Aggregate(solution.Route.First(p => p != largest2),
                                                    (min, curr) =>
                                                    curr != largest2 && (curr.DistanceFrom(largest2) < min.DistanceFrom(largest2)) ? curr : min);

            Console.WriteLine("closest to {0} is {1} with distance of {2}", largest2, closest2, closest2.DistanceFrom(largest2));

            TsPoint c1Left = null;
            TsPoint c1Right = null;

            for (int i = 0; i < solution.Route.Count; i++)
            {
                if (solution.Route[i].Id != closest1.Id) continue;

                c1Left = solution.Route[i - 1];
                c1Right = solution.Route[i + 1];
            }
            if (c1Left == null || c1Right == null)
                Console.WriteLine("didnt find for c1");
            else
                Console.WriteLine("{0} has left {1} with distance of {2} and right {3} with distance of {4}", closest1, c1Left, closest1.DistanceFrom(c1Left), c1Right, closest1.DistanceFrom(c1Right));
        }
        private void CreateSolution(IEnumerable<TsPoint> points, TspSolution solution, TsPoint firstPoint)
        {
            var notVisited = new List<TsPoint>(points.Where(p => p.Id != firstPoint.Id));
            var last = firstPoint;

            while (notVisited.Any())
            {
                var next = GetClosestPoint(last, notVisited);

                //check for potential orphans
                var pointsAbove = notVisited.Where(p => p != next && p.Y > last.Y).ToArray();
                var pointsBelow = notVisited.Where(p => p != next && p.Y < last.Y).ToArray();
                var pointsRight = notVisited.Where(p => p != next && p.X > last.X).ToArray();
                var pointsLeft = notVisited.Where(p => p != next && p.X < last.X).ToArray();

                TsPoint theAbove = null;
                TsPoint theBelow = null;
                TsPoint theRight = null;
                TsPoint theLeft = null;

                if (pointsAbove.Length == 1)
                {
                    theAbove = pointsAbove.Single();
                    if (next.DistanceFrom(last) / theAbove.DistanceFrom(last) < _threshold) theAbove = null;
                }
                if (pointsBelow.Length == 1)
                {
                    theBelow = pointsBelow.Single();
                    if (next.DistanceFrom(last) / theBelow.DistanceFrom(last) < _threshold) theBelow = null;
                }
                if (pointsRight.Length == 1)
                {
                    theRight = pointsRight.Single();
                    if (next.DistanceFrom(last) / theRight.DistanceFrom(last) < _threshold) theRight = null;
                }
                if (pointsLeft.Length == 1)
                {
                    theLeft = pointsLeft.Single();
                    if (next.DistanceFrom(last) / theLeft.DistanceFrom(last) < _threshold) theLeft = null;
                }

                var nextPoints = new[] { theAbove, theBelow, theRight, theLeft }.Where(p => p != null).ToList();

                if (nextPoints.Any())
                {
                    var unOrphaned = GetClosestPoint(last, nextPoints);
                    notVisited.Remove(unOrphaned);
                    last = unOrphaned;
                    solution.AddNext(unOrphaned);
                }
                else
                {
                    notVisited.Remove(next);
                    last = next;
                    solution.AddNext(next);
                }
            }
        }
        public TspSolution Execute(TsPoint[] points)
        {
            var sortedPoints = points.OrderBy(p => p.X).ThenBy(p => p.Y);

            var firstPoint = sortedPoints.First();
            var solution = new TspSolution(firstPoint);

            foreach (var point in sortedPoints.Skip(1))
                solution.AddNext(point);

            solution.Close();
            return solution;
        }
        private TspSolution CreateSolution(TsPoint[] points, TsPoint firstPoint)
        {
            var solution = new TspSolution(firstPoint);

            var alreadyVisited = new List<int> { firstPoint.Id };

            var last = firstPoint;

            while (alreadyVisited.Count != points.Length)
            {
                var notVisited = points.Where(p => !alreadyVisited.Contains(p.Id)).ToArray();
                var next = notVisited.Aggregate(notVisited.First(),
                                                (min, curr) =>
                                                curr.DistanceFrom(last) < min.DistanceFrom(last) ? curr : min);

                alreadyVisited.Add(next.Id);
                last = next;
                solution.AddNext(next);
            }
            solution.Close();
            return solution;
        }
        public TspSolution Execute(TsPoint[] points)
        {
            var minX = points.Min(p => p.X);
            var maxX = points.Max(p => p.X);
            var increment = (maxX - minX) / _steps;

            TspSolution solution = null;

            for (int i = 1; i <= _steps; i++)
            {
                var range = minX + increment;
                if (i == _steps) ++range;
                var thesePoints = points.Where(p => p.X >= minX && p.X < range).ToList();

                TsPoint firstPoint;
                if (solution == null)
                {
                    firstPoint = thesePoints.OrderBy(p => p.Y).First();
                    solution = new TspSolution(firstPoint);
                }
                else
                {
                    firstPoint = GetClosestPoint(solution.LastItem, thesePoints);
                    solution.AddNext(firstPoint);
                }

                CreateSolution(thesePoints, solution, firstPoint);
                minX = range;
            }

            if (solution != null)
            {
                solution.Close();
                solution.OutputToDebug();
            }
            return solution;
        }
        public TspSolution Execute(TsPoint[] points)
        {
            var minX = points.Min(p => p.X);
            var maxX = points.Max(p => p.X);
            var minY = points.Min(p => p.Y);
            var maxY = points.Max(p => p.Y);

            const int pageSize = 7;
            var stepX = (maxX - minX) / pageSize;
            var stepY = (maxY - minY) / pageSize;

            var groups = new List<TspGroup>();

            var goingDown = false;
            var sortNumber = 0;

            for (var x = minX; x < maxX; x = x + stepX)
            {
                if (goingDown)
                    sortNumber += pageSize-1;
                else if (sortNumber > 0)
                    sortNumber += pageSize+1;

                for (var y = minY; y < maxY; y = y + stepY)
                {
                    var sort = goingDown ? sortNumber-- : sortNumber++;
                    //Console.WriteLine(sort);

                    var group = new TspGroup {SortNumber = sort, MinX = x, MaxX = x+stepX, MinY = y, MaxY = y+stepY};
                    groups.Add(group);

                    if ((y + stepY) > (maxY - (stepY / pageSize)))
                    {
                        group.MaxY = maxY + 1;
                        y = maxY;
                    }
                    if ((x + stepX) > (maxX - (stepX / pageSize)))
                    {
                        group.MaxX = maxX + 1;
                    }
                }

                if ((x + stepX) > (maxX - (stepX / pageSize)))
                {
                    x = maxX;
                }

                goingDown = !goingDown;
            }

            var sortedGroups = groups.OrderBy(g => g.SortNumber).ToArray();

            for (var i = 0; i < sortedGroups.Length; i++)
            {
                var group = sortedGroups[i];
                bool goingUp = ((group.SortNumber / pageSize) % 2) == 0;
                TsPoint[] pointsPage;

                var pointsPage1 = points.Where(p =>
                                          p.X >= group.MinX && p.X < group.MaxX &&
                                          p.Y >= group.MinY && p.Y < group.MaxY);

                if (goingUp) pointsPage = pointsPage1.OrderBy(p => p.X).ThenBy(p => p.Y).ToArray();
                else pointsPage = pointsPage1.OrderBy(p => p.X).ThenByDescending(p => p.Y).ToArray();

                var firstPoint = pointsPage.First();
                if (_solution == null)
                {
                    _solution = new TspSolution(firstPoint);
                }
                else
                {
                    var lastItem = _solution.LastItem;
                    firstPoint = pointsPage.Aggregate(firstPoint,
                                                    (min, curr) =>
                                                    curr.DistanceFrom(lastItem) < min.DistanceFrom(lastItem) ? curr : min);
                    _solution.AddNext(firstPoint);
                }

                var alreadyVisited = new List<int> { firstPoint.Id };
                var last = firstPoint;

                while (alreadyVisited.Count != pointsPage.Length)
                {
                    var notVisited = pointsPage.Where(p => !alreadyVisited.Contains(p.Id)).ToArray();
                    var next = notVisited.Aggregate(notVisited.First(),
                                                    (min, curr) =>
                                                    curr.DistanceFrom(last) < min.DistanceFrom(last) ? curr : min);

                    alreadyVisited.Add(next.Id);
                    last = next;
                    _solution.AddNext(next);
                }
            }
            _solution.Close();
            return _solution;
        }
        private void CreateSolutionOld(TsPoint[] points, TspSolution solution, TsPoint firstPoint)
        {
            var alreadyVisited = new List<int> { firstPoint.Id };

            var last = firstPoint;

            while (alreadyVisited.Count != points.Length)
            {
                var notVisited = points.Where(p => !alreadyVisited.Contains(p.Id)).ToArray();
                var next = notVisited.Aggregate(notVisited.First(),
                                                (min, curr) =>
                                                curr.DistanceFrom(last) < min.DistanceFrom(last) ? curr : min);

                if (next.Y < solution.LastItem.Y)
                {
                    //i went down, so is there only 1 left above me?
                    var above = points.Where(p => !alreadyVisited.Contains(p.Id) && p != next && p.Y > next.Y).ToArray();
                    if (above.Length == 1)
                    {
                        var theAbove = above.Single();
                        if (next.DistanceFrom(last) / theAbove.DistanceFrom(last) > .95)
                            next = theAbove;
                    }
                }
                else
                {
                    //i went up, so is there only 1 left below me?
                    var below = points.Where(p => !alreadyVisited.Contains(p.Id) && p != next && p.Y < next.Y).ToArray();
                    if (below.Length == 1)
                    {
                        var theBelow = below.Single();
                        if (next.DistanceFrom(last) / theBelow.DistanceFrom(last) > .95)
                            next = theBelow;
                    }
                }

                alreadyVisited.Add(next.Id);
                last = next;
                solution.AddNext(next);
            }
        }
        public TspSolution Execute(TsPoint[] points)
        {
            var firstPoint = points.First();
            var solution = new TspSolution(firstPoint);

            //var alreadyVisited = new List<int> { firstPoint.Id };
            var notVisited = new List<TsPoint>(points.Where(p => p.Id != firstPoint.Id));

            var last = firstPoint;

            //while (alreadyVisited.Count != points.Length)
            while(notVisited.Any())
            {
                //var notVisited = points.Where(p => !alreadyVisited.Contains(p.Id)).ToArray();
                var next = GetClosestPoint(last, notVisited);

                //var nextNotVisited = points.Where(p => !alreadyVisited.Contains(p.Id) && p != next).ToArray();

                //check for potential orphans
                var pointsAbove = notVisited.Where(p => p != next && p.Y > last.Y).ToArray();
                var pointsBelow = notVisited.Where(p => p != next && p.Y < last.Y).ToArray();
                var pointsRight = notVisited.Where(p => p != next && p.X > last.X).ToArray();
                var pointsLeft = notVisited.Where(p => p != next && p.X < last.X).ToArray();

                TsPoint theAbove = null;
                TsPoint theBelow = null;
                TsPoint theRight = null;
                TsPoint theLeft = null;

                if (pointsAbove.Length == 1)
                {
                    theAbove = pointsAbove.Single();
                    if (next.DistanceFrom(last) / theAbove.DistanceFrom(last) < _threshold) theAbove = null;
                }
                if (pointsBelow.Length == 1)
                {
                    theBelow = pointsBelow.Single();
                    if (next.DistanceFrom(last) / theBelow.DistanceFrom(last) < _threshold) theBelow = null;
                }
                if (pointsRight.Length == 1)
                {
                    theRight = pointsRight.Single();
                    if (next.DistanceFrom(last) / theRight.DistanceFrom(last) < _threshold) theRight = null;
                }
                if (pointsLeft.Length == 1)
                {
                    theLeft = pointsLeft.Single();
                    if (next.DistanceFrom(last) / theLeft.DistanceFrom(last) < _threshold) theLeft = null;
                }

                var nextPoints = new[] {theAbove, theBelow, theRight, theLeft}.Where(p => p != null).ToList();

                if (nextPoints.Any())
                {
                    var unOrphaned = GetClosestPoint(last, nextPoints);
                    //alreadyVisited.Add(unOrphaned.Id);
                    notVisited.Remove(unOrphaned);
                    last = unOrphaned;
                    solution.AddNext(unOrphaned);
                }
                else
                {
                    //alreadyVisited.Add(next.Id);
                    notVisited.Remove(next);
                    last = next;
                    solution.AddNext(next);
                }
            }
            solution.Close();
            //solution.OutputToDebug();
            return solution;
        }