#region License Information
/* HeuristicLab
* Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
namespace HeuristicLab.Common {
public static class EnumerableExtensions {
///
/// Selects all elements in the sequence that are maximal with respect to the given value.
///
///
/// Runtime complexity of the operation is O(N).
///
/// The type of the elements.
/// The enumeration in which the items with a maximal value should be found.
/// The function that selects the value to compare.
/// All elements in the enumeration where the selected value is the maximum.
public static IEnumerable MaxItems(this IEnumerable source, Func valueSelector) {
IEnumerator enumerator = source.GetEnumerator();
if (!enumerator.MoveNext()) return Enumerable.Empty();
IComparable max = valueSelector(enumerator.Current);
var result = new List();
result.Add(enumerator.Current);
while (enumerator.MoveNext()) {
T item = enumerator.Current;
IComparable comparison = valueSelector(item);
if (comparison.CompareTo(max) > 0) {
result.Clear();
result.Add(item);
max = comparison;
} else if (comparison.CompareTo(max) == 0) {
result.Add(item);
}
}
return result;
}
///
/// Selects all elements in the sequence that are minimal with respect to the given value.
///
///
/// Runtime complexity of the operation is O(N).
///
/// The type of the elements.
/// The enumeration in which items with a minimal value should be found.
/// The function that selects the value.
/// All elements in the enumeration where the selected value is the minimum.
public static IEnumerable MinItems(this IEnumerable source, Func valueSelector) {
IEnumerator enumerator = source.GetEnumerator();
if (!enumerator.MoveNext()) return Enumerable.Empty();
IComparable min = valueSelector(enumerator.Current);
var result = new List();
result.Add(enumerator.Current);
while (enumerator.MoveNext()) {
T item = enumerator.Current;
IComparable comparison = valueSelector(item);
if (comparison.CompareTo(min) < 0) {
result.Clear();
result.Add(item);
min = comparison;
} else if (comparison.CompareTo(min) == 0) {
result.Add(item);
}
}
return result;
}
///
/// Compute the n-ary cartesian product of arbitrarily many sequences: http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx
///
/// The type of the elements inside each sequence
/// The collection of sequences
/// An enumerable sequence of all the possible combinations of elements
public static IEnumerable> CartesianProduct(this IEnumerable> sequences) {
IEnumerable> result = new[] { Enumerable.Empty() };
return sequences.Where(s => s.Any()).Aggregate(result, (current, s) => (from seq in current from item in s select seq.Concat(new[] { item })));
}
///
/// Compute all k-combinations of elements from the provided collection.
/// The collection of elements
/// The combination group size
/// An enumerable sequence of all the possible k-combinations of elements
///
public static IEnumerable> Combinations(this IList elements, int k) {
if (k > elements.Count)
throw new ArgumentException();
if (k == 1) {
foreach (var element in elements)
yield return new[] { element };
yield break;
}
int n = elements.Count;
var range = Enumerable.Range(0, k).ToArray();
var length = BinomialCoefficient(n, k);
for (int i = 0; i < length; ++i) {
yield return range.Select(x => elements[x]).ToArray();
if (i == length - 1) break;
var m = k - 1;
var max = n - 1;
while (range[m] == max) { --m; --max; }
range[m]++;
for (int j = m + 1; j < k; ++j) {
range[j] = range[j - 1] + 1;
}
}
}
///
/// This function gets the total number of unique combinations based upon N and K,
/// where N is the total number of items and K is the size of the group.
/// It calculates the total number of unique combinations C(N, K) = N! / ( K! (N - K)! )
/// using the recursion C(N+1, K+1) = (N+1 / K+1) * C(N, K).
/// http://blog.plover.com/math/choose.html
/// https://en.wikipedia.org/wiki/Binomial_coefficient#Multiplicative_formula
/// The number of elements
/// The size of the group
/// The binomial coefficient C(N, K)
///
public static long BinomialCoefficient(long n, long k) {
if (k > n) return 0;
if (k == n) return 1;
if (k > n - k)
k = n - k;
// enable explicit overflow checking for very large coefficients
checked {
long r = 1;
for (long d = 1; d <= k; d++) {
r *= n--;
r /= d;
}
return r;
}
}
}
}