/// <summary>
        /// Returns the customer that least increases the length of the given route.
        /// </summary>
        /// <param name="problem"></param>
        /// <param name="route"></param>
        /// <param name="customers"></param>
        /// <param name="costs"></param>
        /// <returns></returns>
        public static CheapestInsertionResult CalculateBestPlacement(
            IProblemWeights problem,
            IRoute route,
            ICollection <int> customers,
            IInsertionCosts costs)
        {
            // initialize the best placement result.
            CheapestInsertionResult best = new CheapestInsertionResult();

            best.Increase = float.MaxValue;

            // loop over all customers in the route.
            if (route.Count > 0)
            { // there have to be at least two customers.
                IEnumerator <int> route_enumerator = route.GetEnumerator();
                if (!route_enumerator.MoveNext())
                { // this route is empty
                    throw new ArgumentException("Route needs to be initialized with at least two customers!");
                }
                int customer_before = route_enumerator.Current;
                int customer_after  = -1;
                while (route_enumerator.MoveNext())
                { // keep moving!
                    customer_after = route_enumerator.Current;
                    InsertionCost cost  = costs.PopCheapest(customer_before, customer_after);
                    bool          found = false;
                    while (!found)
                    {     // test if the costs are empty.
                        if (cost == null)
                        { // re-initialize the costs with all customers under consideration.
                            foreach (int customer in customers)
                            {
                                costs.Add(customer_before, customer_after, customer,
                                          (float)problem.WeightMatrix[customer_before][customer] +
                                          (float)problem.WeightMatrix[customer][customer_after] -
                                          (float)problem.WeightMatrix[customer_before][customer_after]);
                            }

                            // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                        else if (customers.Contains(cost.Customer))
                        { // the customer is found!
                            found = true;
                        }
                        else
                        { // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                    }

                    if (cost.Cost < best.Increase)
                    { // the costs is better than the current cost!
                        best = new CheapestInsertionResult()
                        {
                            Customer       = cost.Customer,
                            CustomerAfter  = customer_after,
                            CustomerBefore = customer_before,
                            Increase       = cost.Cost
                        };
                        if (best.Increase == 0)
                        { // immidiately return if smaller than epsilon.
                            return(best);
                        }
                    }

                    // set the after to the before.
                    customer_before = route_enumerator.Current;
                }

                // if the round is a round try first-last.
                if (route.IsRound)
                { // the route is a round!
                    customer_after = route.First;
                    InsertionCost cost  = costs.PopCheapest(customer_before, customer_after);
                    bool          found = false;
                    while (!found)
                    {     // test if the costs are empty.
                        if (cost == null)
                        { // re-initialize the costs with all customers under consideration.
                            foreach (int customer in customers)
                            {
                                costs.Add(customer_before, customer_after, customer,
                                          (float)problem.WeightMatrix[customer_before][customer] +
                                          (float)problem.WeightMatrix[customer][customer_after] -
                                          (float)problem.WeightMatrix[customer_before][customer_after]);
                            }

                            // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                        else if (customers.Contains(cost.Customer))
                        { // the customer is found!
                            found = true;
                        }
                        else
                        { // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                    }

                    if (cost.Cost < best.Increase)
                    { // the costs is better than the current cost!
                        best = new CheapestInsertionResult()
                        {
                            Customer       = cost.Customer,
                            CustomerAfter  = customer_after,
                            CustomerBefore = customer_before,
                            Increase       = cost.Cost
                        };
                        if (best.Increase == 0)
                        { // immidiately return if smaller than epsilon.
                            return(best);
                        }
                    }
                }
            }
            else
            { // route needs to be initialized.
                throw new ArgumentException("Route needs to be initialized with at least two customers!");
            }

            // return result.
            return(best);
        }
        /// <summary>
        /// Returns the customer that least increases the length of the given route.
        /// </summary>
        /// <param name="problem"></param>
        /// <param name="route"></param>
        /// <param name="customers"></param>
        /// <param name="costs"></param>
        /// <returns></returns>
        public static CheapestInsertionResult CalculateBestPlacement(
            IProblemWeights problem,
            IRoute route,
            ICollection<int> customers,
            IInsertionCosts costs)
        {
            // initialize the best placement result.
            CheapestInsertionResult best = new CheapestInsertionResult();
            best.Increase = float.MaxValue;

            // loop over all customers in the route.
            if (route.Count > 0)
            { // there have to be at least two customers.
                IEnumerator<int> route_enumerator = route.GetEnumerator();
                if (!route_enumerator.MoveNext())
                { // this route is empty
                    throw new ArgumentException("Route needs to be initialized with at least two customers!");
                }
                int customer_before = route_enumerator.Current;
                int customer_after = -1;
                while (route_enumerator.MoveNext())
                { // keep moving!
                    customer_after = route_enumerator.Current;
                    InsertionCost cost = costs.PopCheapest(customer_before, customer_after);
                    bool found = false;
                    while(!found)
                    { // test if the costs are empty.
                        if (cost == null)
                        { // re-initialize the costs with all customers under consideration.
                            foreach (int customer in customers)
                            {
                                costs.Add(customer_before, customer_after, customer,
                                    (float)problem.WeightMatrix[customer_before][customer] +
                                    (float)problem.WeightMatrix[customer][customer_after] -
                                    (float)problem.WeightMatrix[customer_before][customer_after]);
                            }

                            // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                        else if (customers.Contains(cost.Customer))
                        { // the customer is found!
                            found = true;
                        }
                        else
                        { // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                    }

                    if (cost.Cost < best.Increase)
                    { // the costs is better than the current cost!
                        best = new CheapestInsertionResult()
                            {
                                Customer = cost.Customer,
                                CustomerAfter = customer_after,
                                CustomerBefore = customer_before,
                                Increase = cost.Cost
                            };
                        if (best.Increase == 0)
                        { // immidiately return if smaller than epsilon.
                            return best;
                        }
                    }

                    // set the after to the before.
                    customer_before = route_enumerator.Current;
                }

                // if the round is a round try first-last.
                if (route.IsRound)
                { // the route is a round!
                    customer_after = route.First;
                    InsertionCost cost = costs.PopCheapest(customer_before, customer_after);
                    bool found = false;
                    while (!found)
                    { // test if the costs are empty.
                        if (cost == null)
                        { // re-initialize the costs with all customers under consideration.
                            foreach (int customer in customers)
                            {
                                costs.Add(customer_before, customer_after, customer,
                                    (float)problem.WeightMatrix[customer_before][customer] +
                                    (float)problem.WeightMatrix[customer][customer_after] -
                                    (float)problem.WeightMatrix[customer_before][customer_after]);
                            }

                            // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                        else if (customers.Contains(cost.Customer))
                        { // the customer is found!
                            found = true;
                        }
                        else
                        { // pop the cheapest again!
                            cost = costs.PopCheapest(customer_before, customer_after);
                        }
                    }

                    if (cost.Cost < best.Increase)
                    { // the costs is better than the current cost!
                        best = new CheapestInsertionResult()
                        {
                            Customer = cost.Customer,
                            CustomerAfter = customer_after,
                            CustomerBefore = customer_before,
                            Increase = cost.Cost
                        };
                        if (best.Increase == 0)
                        { // immidiately return if smaller than epsilon.
                            return best;
                        }
                    }
                }
            }
            else
            { // route needs to be initialized.
                throw new ArgumentException("Route needs to be initialized with at least two customers!");
            }

            // return result.
            return best;
        }