Enumerable.cs :  » 2.6.4-mono-.net-core » System.Linq » System » Linq » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » 2.6.4 mono .net core » System.Linq 
System.Linq » System » Linq » Enumerable.cs
//
// Enumerable.cs
//
// Authors:
//  Marek Safar (marek.safar@gmail.com)
//  Antonello Provenzano  <antonello@deveel.com>
//  Alejandro Serrano "Serras" (trupill@yahoo.es)
//  Jb Evain (jbevain@novell.com)
//
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

// precious: http://www.hookedonlinq.com

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace System.Linq{
  public static class Enumerable
  {
    enum Fallback {
      Default,
      Throw
    }

    static class PredicateOf<T> {
      public static readonly Func<T, bool> Always = (t) => true;
    }

    static class Function<T> {
      public static readonly Func<T, T> Identity = (t) => t;
    }
    
    static class EmptyOf<T> {
      public static readonly T[] Instance = new T [0];
    }
    
    static class ReadOnlyCollectionOf<T> {
      public static readonly ReadOnlyCollection<T> Empty = new ReadOnlyCollection<T> (EmptyOf<T>.Instance);
    }

    #region Aggregate

    public static TSource Aggregate<TSource> (this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)
    {
      Check.SourceAndFunc (source, func);

      // custom foreach so that we can efficiently throw an exception
      // if zero elements and treat the first element differently
      using (var enumerator = source.GetEnumerator ()) {
        if (!enumerator.MoveNext ())
          throw new InvalidOperationException ("No elements in source list");

        TSource folded = enumerator.Current;
        while (enumerator.MoveNext ())
          folded = func (folded, enumerator.Current);
        return folded;
      }
    }

    public static TAccumulate Aggregate<TSource, TAccumulate> (this IEnumerable<TSource> source,
      TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)
    {
      Check.SourceAndFunc (source, func);

      TAccumulate folded = seed;
      foreach (TSource element in source)
        folded = func (folded, element);

      return folded;
    }

    public static TResult Aggregate<TSource, TAccumulate, TResult> (this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector)
    {
      Check.SourceAndFunc (source, func);
      if (resultSelector == null)
        throw new ArgumentNullException ("resultSelector");

      var result = seed;
      foreach (var e in source)
        result = func (result, e);

      return resultSelector (result);
    }

    #endregion

    #region All

    public static bool All<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      foreach (var element in source)
        if (!predicate (element))
          return false;

      return true;
    }

    #endregion

    #region Any

    public static bool Any<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var collection = source as ICollection<TSource>;
      if (collection != null)
        return collection.Count > 0;

      using (var enumerator = source.GetEnumerator ())
        return enumerator.MoveNext ();
    }

    public static bool Any<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      foreach (TSource element in source)
        if (predicate (element))
          return true;

      return false;
    }

    #endregion

    #region AsEnumerable

    public static IEnumerable<TSource> AsEnumerable<TSource> (this IEnumerable<TSource> source)
    {
      return source;
    }

    #endregion

    #region Average

    public static double Average (this IEnumerable<int> source)
    {
      Check.Source (source);

      long total = 0;
      int count = 0;
      foreach (var element in source){
        total = checked (total + element);
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / (double) count;
    }

    public static double Average (this IEnumerable<long> source)
    {
      Check.Source (source);

      long total = 0;
      long count = 0;
      foreach (var element in source){
        total += element;
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / (double) count;
    }

    public static double Average (this IEnumerable<double> source)
    {
      Check.Source (source);

      double total = 0;
      long count = 0;
      foreach (var element in source){
        total += element;
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / count;
    }

    public static float Average (this IEnumerable<float> source)
    {
      Check.Source (source);

      float total = 0;
      long count = 0;
      foreach (var element in source){
        total += element;
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / count;
    }

    public static decimal Average (this IEnumerable<decimal> source)
    {
      Check.Source (source);

      decimal total = 0;
      long count = 0;
      foreach (var element in source){
        total += element;
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / count;
    }

    static TResult? AverageNullable<TElement, TAggregate, TResult> (this IEnumerable<TElement?> source,
      Func<TAggregate, TElement, TAggregate> func, Func<TAggregate, long, TResult> result)
      where TElement : struct
      where TAggregate : struct
      where TResult : struct
    {
      Check.Source (source);

      var total = default (TAggregate);
      long counter = 0;
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        total = func (total, element.Value);
        counter++;
      }

      if (counter == 0)
        return null;

      return new TResult? (result (total, counter));
    }

    public static double? Average (this IEnumerable<int?> source)
    {
      Check.Source (source);

      long total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        total = total + element.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new double? (total / (double) counter);
    }

    public static double? Average (this IEnumerable<long?> source)
    {
      Check.Source (source);

      long total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        total = checked (total + element.Value);
        counter++;
      }

      if (counter == 0)
        return null;

      return new double? (total / (double) counter);

    }

    public static double? Average (this IEnumerable<double?> source)
    {
      Check.Source (source);

      double total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        total = total + element.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new double? (total / counter);

    }

    public static decimal? Average (this IEnumerable<decimal?> source)
    {
      Check.Source (source);

      decimal total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        total = total + element.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new decimal? (total / counter);

    }

    public static float? Average (this IEnumerable<float?> source)
    {
      Check.Source (source);

      float total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        total = total + element.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new float? (total / counter);

    }

    public static double Average<TSource> (this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
      Check.SourceAndSelector (source, selector);

      long total = 0;
      long count = 0;
      foreach (var element in source){
        total += selector (element);
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / (double) count;
    }

    public static double? Average<TSource> (this IEnumerable<TSource> source, Func<TSource, int?> selector)
    {
      Check.SourceAndSelector (source, selector);

      long total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        var value = selector (element);
        if (!value.HasValue)
          continue;

        total = total + value.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new double? (total / (double) counter);
    }

    public static double Average<TSource> (this IEnumerable<TSource> source, Func<TSource, long> selector)
    {
      Check.SourceAndSelector (source, selector);

      long total = 0;
      long count = 0;
      foreach (var element in source){
        total = checked (total + selector (element));
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / (double) count;

    }

    public static double? Average<TSource> (this IEnumerable<TSource> source, Func<TSource, long?> selector)
    {
      Check.SourceAndSelector (source, selector);

      long total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        var value = selector (element);
        if (!value.HasValue)
          continue;

        total = checked (total + value.Value);
        counter++;
      }

      if (counter == 0)
        return null;

      return new double? (total / (double) counter);
    }

    public static double Average<TSource> (this IEnumerable<TSource> source, Func<TSource, double> selector)
    {
      Check.SourceAndSelector (source, selector);

      double total = 0;
      long count = 0;
      foreach (var element in source){
        total += selector (element);
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / count;

    }

    public static double? Average<TSource> (this IEnumerable<TSource> source, Func<TSource, double?> selector)
    {
      Check.SourceAndSelector (source, selector);

      double total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        var value = selector (element);
        if (!value.HasValue)
          continue;

        total = total + value.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new double? (total / counter);

    }

    public static float Average<TSource> (this IEnumerable<TSource> source, Func<TSource, float> selector)
    {
      Check.SourceAndSelector (source, selector);

      float total = 0;
      long count = 0;
      foreach (var element in source){
        total += selector (element);
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / count;
    }

    public static float? Average<TSource> (this IEnumerable<TSource> source, Func<TSource, float?> selector)
    {
      Check.SourceAndSelector (source, selector);

      float total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        var value = selector (element);
        if (!value.HasValue)
          continue;

        total = total + value.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new float? (total / counter);
    }

    public static decimal Average<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal> selector)
    {
      Check.SourceAndSelector (source, selector);

      decimal total = 0;
      long count = 0;
      foreach (var element in source){
        total += selector (element);
        count++;
      }
      if (count == 0)
        throw new InvalidOperationException ();
      return total / count;
    }

    public static decimal? Average<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal?> selector)
    {
      Check.SourceAndSelector (source, selector);

      decimal total = 0;
      long counter = 0;
      
      foreach (var element in source) {
        var value = selector (element);
        if (!value.HasValue)
          continue;

        total = total + value.Value;
        counter++;
      }

      if (counter == 0)
        return null;

      return new decimal? (total / counter);
    }

    #endregion

    #region Cast

    public static IEnumerable<TResult> Cast<TResult> (this IEnumerable source)
    {
      Check.Source (source);

      var actual = source as IEnumerable<TResult>;
      if (actual != null)
        return actual;

      return CreateCastIterator<TResult> (source);
    }

    static IEnumerable<TResult> CreateCastIterator<TResult> (IEnumerable source)
    {
      foreach (TResult element in source)
        yield return element;
    }

    #endregion

    #region Concat

    public static IEnumerable<TSource> Concat<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
      Check.FirstAndSecond (first, second);

      return CreateConcatIterator (first, second);
    }

    static IEnumerable<TSource> CreateConcatIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
      foreach (TSource element in first)
        yield return element;
      foreach (TSource element in second)
        yield return element;
    }

    #endregion

    #region Contains

    public static bool Contains<TSource> (this IEnumerable<TSource> source, TSource value)
    {
      var collection = source as ICollection<TSource>;
      if (collection != null)
        return collection.Contains (value);

      return Contains<TSource> (source, value, null);
    }

    public static bool Contains<TSource> (this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
    {
      Check.Source (source);

      if (comparer == null)
        comparer = EqualityComparer<TSource>.Default;

      foreach (var element in source)
        if (comparer.Equals (element, value))
          return true;

      return false;
    }
    #endregion

    #region Count

    public static int Count<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var collection = source as ICollection<TSource>;
      if (collection != null)
        return collection.Count;

      int counter = 0;
      using (var enumerator = source.GetEnumerator ())
        while (enumerator.MoveNext ())
          counter++;

      return counter;
    }

    public static int Count<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> selector)
    {
      Check.SourceAndSelector (source, selector);

      int counter = 0;
      foreach (var element in source)
        if (selector (element))
          counter++;

      return counter;
    }

    #endregion

    #region DefaultIfEmpty

    public static IEnumerable<TSource> DefaultIfEmpty<TSource> (this IEnumerable<TSource> source)
    {
      return DefaultIfEmpty (source, default (TSource));
    }

    public static IEnumerable<TSource> DefaultIfEmpty<TSource> (this IEnumerable<TSource> source, TSource defaultValue)
    {
      Check.Source (source);

      return CreateDefaultIfEmptyIterator (source, defaultValue);
    }

    static IEnumerable<TSource> CreateDefaultIfEmptyIterator<TSource> (IEnumerable<TSource> source, TSource defaultValue)
    {
      bool empty = true;
      foreach (TSource item in source) {
        empty = false;
        yield return item;
      }

      if (empty)
        yield return defaultValue;
    }

    #endregion

    #region Distinct

    public static IEnumerable<TSource> Distinct<TSource> (this IEnumerable<TSource> source)
    {
      return Distinct<TSource> (source, null);
    }

    public static IEnumerable<TSource> Distinct<TSource> (this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
      Check.Source (source);

      if (comparer == null)
        comparer = EqualityComparer<TSource>.Default;

      return CreateDistinctIterator (source, comparer);
    }

    static IEnumerable<TSource> CreateDistinctIterator<TSource> (IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
      var items = new HashSet<TSource> (comparer);
      foreach (var element in source) {
        if (! items.Contains (element)) {
          items.Add (element);
          yield return element;
        }
      }
    }

    #endregion

    #region ElementAt

    static TSource ElementAt<TSource> (this IEnumerable<TSource> source, int index, Fallback fallback)
    {
      long counter = 0L;

      foreach (var element in source) {
        if (index == counter++)
          return element;
      }

      if (fallback == Fallback.Throw)
        throw new ArgumentOutOfRangeException ();

      return default (TSource);
    }

    public static TSource ElementAt<TSource> (this IEnumerable<TSource> source, int index)
    {
      Check.Source (source);

      if (index < 0)
        throw new ArgumentOutOfRangeException ();

      var list = source as IList<TSource>;
      if (list != null)
        return list [index];

      return source.ElementAt (index, Fallback.Throw);
    }

    #endregion

    #region ElementAtOrDefault

    public static TSource ElementAtOrDefault<TSource> (this IEnumerable<TSource> source, int index)
    {
      Check.Source (source);

      if (index < 0)
        return default (TSource);

      var list = source as IList<TSource>;
      if (list != null)
        return index < list.Count ? list [index] : default (TSource);

      return source.ElementAt (index, Fallback.Default);
    }

    #endregion

    #region Empty

    public static IEnumerable<TResult> Empty<TResult> ()
    {
      return EmptyOf<TResult>.Instance;
    }

    #endregion

    #region Except

    public static IEnumerable<TSource> Except<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
      return Except (first, second, null);
    }

    public static IEnumerable<TSource> Except<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      Check.FirstAndSecond (first, second);

      if (comparer == null)
        comparer = EqualityComparer<TSource>.Default;

      return CreateExceptIterator (first, second, comparer);
    }

    static IEnumerable<TSource> CreateExceptIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      var items = new HashSet<TSource> (second, comparer);
      foreach (var element in first) {
        if (!items.Contains (element, comparer))
          yield return element;
      }
    }

    #endregion

    #region First

    static TSource First<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate, Fallback fallback)
    {
      foreach (var element in source)
        if (predicate (element))
          return element;

      if (fallback == Fallback.Throw)
        throw new InvalidOperationException ();

      return default (TSource);
    }

    public static TSource First<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var list = source as IList<TSource>;
      if (list != null) {
        if (list.Count != 0)
          return list [0];
      } else {
        using (var enumerator = source.GetEnumerator ()) {
          if (enumerator.MoveNext ())
            return enumerator.Current;
        }
      }

      throw new InvalidOperationException ("The source sequence is empty");
    }

    public static TSource First<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return source.First (predicate, Fallback.Throw);
    }

    #endregion

    #region FirstOrDefault

    public static TSource FirstOrDefault<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      return source.First (PredicateOf<TSource>.Always, Fallback.Default);
    }

    public static TSource FirstOrDefault<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return source.First (predicate, Fallback.Default);
    }

    #endregion

    #region GroupBy

    private static List<T> ContainsGroup<K, T> (
        Dictionary<K, List<T>> items, K key, IEqualityComparer<K> comparer)
    {
      IEqualityComparer<K> comparerInUse = (comparer ?? EqualityComparer<K>.Default);
      foreach (KeyValuePair<K, List<T>> value in items) {
        if (comparerInUse.Equals (value.Key, key))
          return value.Value;
      }
      return null;
    }

    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector)
    {
      return GroupBy<TSource, TKey> (source, keySelector, null);
    }

    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
      Check.SourceAndKeySelector (source, keySelector);

      return CreateGroupByIterator (source, keySelector, comparer);
    }

    static IEnumerable<IGrouping<TKey, TSource>> CreateGroupByIterator<TSource, TKey> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
      var groups = new Dictionary<TKey, List<TSource>> ();
      var nullList = new List<TSource> ();
      int counter = 0;
      int nullCounter = -1;

      foreach (TSource element in source) {
        TKey key = keySelector (element);
        if (key == null) {
          nullList.Add (element);
          if (nullCounter == -1) {
            nullCounter = counter;
            counter++;
          }
        } else {
          List<TSource> group = ContainsGroup<TKey, TSource> (groups, key, comparer);
          if (group == null) {
            group = new List<TSource> ();
            groups.Add (key, group);
            counter++;
          }
          group.Add (element);
        }
      }

      counter = 0;
      foreach (var group in groups) {
        if (counter == nullCounter) {
          yield return new Grouping<TKey, TSource> (default (TKey), nullList);
          counter++;
        }

        yield return new Grouping<TKey, TSource> (group.Key, group.Value);
        counter++;
      }

      if (counter == nullCounter) {
        yield return new Grouping<TKey, TSource> (default (TKey), nullList);
        counter++;
      }
    }

    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
    {
      return GroupBy<TSource, TKey, TElement> (source, keySelector, elementSelector, null);
    }

    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
    {
      Check.SourceAndKeyElementSelectors (source, keySelector, elementSelector);

      return CreateGroupByIterator (source, keySelector, elementSelector, comparer);
    }

    static IEnumerable<IGrouping<TKey, TElement>> CreateGroupByIterator<TSource, TKey, TElement> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
    {
      var groups = new Dictionary<TKey, List<TElement>> ();
      var nullList = new List<TElement> ();
      int counter = 0;
      int nullCounter = -1;

      foreach (TSource item in source) {
        TKey key = keySelector (item);
        TElement element = elementSelector (item);
        if (key == null) {
          nullList.Add (element);
          if (nullCounter == -1) {
            nullCounter = counter;
            counter++;
          }
        } else {
          List<TElement> group = ContainsGroup<TKey, TElement> (groups, key, comparer);
          if (group == null) {
            group = new List<TElement> ();
            groups.Add (key, group);
            counter++;
          }
          group.Add (element);
        }
      }

      counter = 0;
      foreach (var group in groups) {
        if (counter == nullCounter) {
          yield return new Grouping<TKey, TElement> (default (TKey), nullList);
          counter++;
        }

        yield return new Grouping<TKey, TElement> (group.Key, group.Value);
        counter++;
      }

      if (counter == nullCounter) {
        yield return new Grouping<TKey, TElement> (default (TKey), nullList);
        counter++;
      }
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector,
      Func<TKey, IEnumerable<TElement>, TResult> resultSelector)
    {
      return GroupBy (source, keySelector, elementSelector, resultSelector, null);
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector,
      Func<TKey, IEnumerable<TElement>, TResult> resultSelector,
      IEqualityComparer<TKey> comparer)
    {
      Check.GroupBySelectors (source, keySelector, elementSelector, resultSelector);

      return CreateGroupByIterator (source, keySelector, elementSelector, resultSelector, comparer);
    }

    static IEnumerable<TResult> CreateGroupByIterator<TSource, TKey, TElement, TResult> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector,
      Func<TKey, IEnumerable<TElement>, TResult> resultSelector,
      IEqualityComparer<TKey> comparer)
    {
      IEnumerable<IGrouping<TKey, TElement>> groups = GroupBy<TSource, TKey, TElement> (
        source, keySelector, elementSelector, comparer);

      foreach (IGrouping<TKey, TElement> group in groups)
        yield return resultSelector (group.Key, group);
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector,
      Func<TKey, IEnumerable<TSource>, TResult> resultSelector)
    {
      return GroupBy (source, keySelector, resultSelector, null);
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector,
      Func<TKey, IEnumerable<TSource>, TResult> resultSelector,
      IEqualityComparer<TKey> comparer)
    {
      Check.SourceAndKeyResultSelectors (source, keySelector, resultSelector);

      return CreateGroupByIterator (source, keySelector, resultSelector, comparer);
    }

    static IEnumerable<TResult> CreateGroupByIterator<TSource, TKey, TResult> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector,
      Func<TKey, IEnumerable<TSource>, TResult> resultSelector,
      IEqualityComparer<TKey> comparer)
    {
      IEnumerable<IGrouping<TKey,TSource>> groups = GroupBy<TSource, TKey> (source, keySelector, comparer);

      foreach (IGrouping<TKey, TSource> group in groups)
        yield return resultSelector (group.Key, group);
    }

    #endregion

    # region GroupJoin

    public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult> (this IEnumerable<TOuter> outer,
      IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
      Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector)
    {
      return GroupJoin (outer, inner, outerKeySelector, innerKeySelector, resultSelector, null);
    }

    public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult> (this IEnumerable<TOuter> outer,
      IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
      Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector,
      IEqualityComparer<TKey> comparer)
    {
      Check.JoinSelectors (outer, inner, outerKeySelector, innerKeySelector, resultSelector);

      if (comparer == null)
        comparer = EqualityComparer<TKey>.Default;

      return CreateGroupJoinIterator (outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
    }

    static IEnumerable<TResult> CreateGroupJoinIterator<TOuter, TInner, TKey, TResult> (this IEnumerable<TOuter> outer,
      IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
      Func<TInner, TKey> innerKeySelector, Func<TOuter, IEnumerable<TInner>, TResult> resultSelector,
      IEqualityComparer<TKey> comparer)
    {
      ILookup<TKey, TInner> innerKeys = ToLookup<TInner, TKey> (inner, innerKeySelector, comparer);
      /*Dictionary<K, List<U>> innerKeys = new Dictionary<K, List<U>> ();
      foreach (U element in inner)
      {
          K innerKey = innerKeySelector (element);
          if (!innerKeys.ContainsKey (innerKey))
              innerKeys.Add (innerKey, new List<U> ());
          innerKeys[innerKey].Add (element);
      }*/

      foreach (TOuter element in outer) {
        TKey outerKey = outerKeySelector (element);
        if (innerKeys.Contains (outerKey))
          yield return resultSelector (element, innerKeys [outerKey]);
        else
          yield return resultSelector (element, Empty<TInner> ());
      }
    }

    #endregion

    #region Intersect

    public static IEnumerable<TSource> Intersect<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
      return Intersect (first, second, null);
    }

    public static IEnumerable<TSource> Intersect<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      Check.FirstAndSecond (first, second);

      if (comparer == null)
        comparer = EqualityComparer<TSource>.Default;

      return CreateIntersectIterator (first, second, comparer);
    }

    static IEnumerable<TSource> CreateIntersectIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      var items = new HashSet<TSource> (second, comparer);
      foreach (TSource element in first) {
        if (items.Remove (element))
          yield return element;
      }
    }

    #endregion

    # region Join

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult> (this IEnumerable<TOuter> outer,
      IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
      Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer)
    {
      Check.JoinSelectors (outer, inner, outerKeySelector, innerKeySelector, resultSelector);

      if (comparer == null)
        comparer = EqualityComparer<TKey>.Default;

      return CreateJoinIterator (outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
    }

    static IEnumerable<TResult> CreateJoinIterator<TOuter, TInner, TKey, TResult> (this IEnumerable<TOuter> outer,
      IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
      Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer)
    {
      ILookup<TKey, TInner> innerKeys = ToLookup<TInner, TKey> (inner, innerKeySelector, comparer);
      /*Dictionary<K, List<U>> innerKeys = new Dictionary<K, List<U>> ();
      foreach (U element in inner)
      {
          K innerKey = innerKeySelector (element);
          if (!innerKeys.ContainsKey (innerKey))
              innerKeys.Add (innerKey, new List<U> ());
          innerKeys[innerKey].Add (element);
      }*/

      foreach (TOuter element in outer) {
        TKey outerKey = outerKeySelector (element);
        if (innerKeys.Contains (outerKey)) {
          foreach (TInner innerElement in innerKeys [outerKey])
            yield return resultSelector (element, innerElement);
        }
      }
    }

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult> (this IEnumerable<TOuter> outer,
      IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector,
      Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector)
    {
      return outer.Join (inner, outerKeySelector, innerKeySelector, resultSelector, null);
    }

    #endregion

    #region Last

    static TSource Last<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate, Fallback fallback)
    {
      var empty = true;
      var item = default (TSource);

      foreach (var element in source) {
        if (!predicate (element))
          continue;

        item = element;
        empty = false;
      }

      if (!empty)
        return item;

      if (fallback == Fallback.Throw)
        throw new InvalidOperationException ();

      return item;
    }

    public static TSource Last<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var collection = source as ICollection<TSource>;
      if (collection != null && collection.Count == 0)
        throw new InvalidOperationException ();

      var list = source as IList<TSource>;
      if (list != null)
        return list [list.Count - 1];

      return source.Last (PredicateOf<TSource>.Always, Fallback.Throw);
    }

    public static TSource Last<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return source.Last (predicate, Fallback.Throw);
    }

    #endregion

    #region LastOrDefault

    public static TSource LastOrDefault<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var list = source as IList<TSource>;
      if (list != null)
        return list.Count > 0 ? list [list.Count - 1] : default (TSource);

      return source.Last (PredicateOf<TSource>.Always, Fallback.Default);
    }

    public static TSource LastOrDefault<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return source.Last (predicate, Fallback.Default);
    }

    #endregion

    #region LongCount

    public static long LongCount<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

#if !NET_2_1
      var array = source as TSource [];
      if (array != null)
        return array.LongLength;
#endif

      long counter = 0;
      using (var enumerator = source.GetEnumerator ())
        while (enumerator.MoveNext ())
          counter++;

      return counter;
    }

    public static long LongCount<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> selector)
    {
      Check.SourceAndSelector (source, selector);

      long counter = 0;
      foreach (TSource element in source)
        if (selector (element))
          counter++;

      return counter;
    }

    #endregion

    #region Max

    public static int Max (this IEnumerable<int> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = int.MinValue;
      foreach (var element in source){
        max = Math.Max (element, max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static long Max (this IEnumerable<long> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = long.MinValue;
      foreach (var element in source){
        max = Math.Max (element, max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static double Max (this IEnumerable<double> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = double.MinValue;
      foreach (var element in source){
        max = Math.Max (element, max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static float Max (this IEnumerable<float> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = float.MinValue;
      foreach (var element in source){
        max = Math.Max (element, max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static decimal Max (this IEnumerable<decimal> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = decimal.MinValue;
      foreach (var element in source){
        max = Math.Max (element, max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static int? Max (this IEnumerable<int?> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = int.MinValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        max = Math.Max (element.Value, max);
        empty = false;
      }

      if (empty)
        return null;

      return max;
    }

    public static long? Max (this IEnumerable<long?> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = long.MinValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        max = Math.Max (element.Value, max);
        empty = false;
      }

      if (empty)
        return null;

      return max;
    }

    public static double? Max (this IEnumerable<double?> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = double.MinValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        max = Math.Max (element.Value, max);
        empty = false;
      }

      if (empty)
        return null;

      return max;
    }

    public static float? Max (this IEnumerable<float?> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = float.MinValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        max = Math.Max (element.Value, max);
        empty = false;
      }

      if (empty)
        return null;

      return max;
    }

    public static decimal? Max (this IEnumerable<decimal?> source)
    {
      Check.Source (source);

      bool empty = true;
      var max = decimal.MinValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        max = Math.Max (element.Value, max);
        empty = false;
      }

      if (empty)
        return null;

      return max;
    }

    // TODO: test nullable and non-nullable
    public static TSource Max<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var comparer = Comparer<TSource>.Default;

      TSource max = default (TSource);
      
      if (default (TSource) == null){
        foreach (var element in source) {
          if (element == null)
            continue;

          if (max == null || comparer.Compare (element, max) > 0)
            max = element;
        }
      } else {
        bool empty = true;
        foreach (var element in source) {
          if (empty){
            max = element;
            empty = false;
            continue;
          }
          if (comparer.Compare (element, max) > 0)
            max = element;
        }
        if (empty)
          throw new InvalidOperationException ();
      }
      return max;
    }

    public static int Max<TSource> (this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var max = int.MinValue;
      foreach (var element in source){
        max = Math.Max (selector (element), max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static long Max<TSource> (this IEnumerable<TSource> source, Func<TSource, long> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var max = long.MinValue;
      foreach (var element in source){
        max = Math.Max (selector (element), max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static double Max<TSource> (this IEnumerable<TSource> source, Func<TSource, double> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var max = double.MinValue;
      foreach (var element in source){
        max = Math.Max (selector (element), max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static float Max<TSource> (this IEnumerable<TSource> source, Func<TSource, float> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var max = float.MinValue;
      foreach (var element in source){
        max = Math.Max (selector (element), max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    public static decimal Max<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var max = decimal.MinValue;
      foreach (var element in source){
        max = Math.Max (selector (element), max);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return max;
    }

    static U Iterate<T, U> (IEnumerable<T> source, U initValue, Func<T, U, U> selector)
    {
      bool empty = true;
      foreach (var element in source) {
        initValue = selector (element, initValue);
        empty = false;
      }

      if (empty)
        throw new InvalidOperationException ();

      return initValue;
    }

    public static int? Max<TSource> (this IEnumerable<TSource> source, Func<TSource, int?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      int? max = null;
      foreach (var element in source) {
        int? item = selector (element);

        if (!max.HasValue)
          max = item;
        else if (item > max)
          max = item;
        empty = false;
      }

      if (empty)
        return null;
      return max;
    }

    public static long? Max<TSource> (this IEnumerable<TSource> source, Func<TSource, long?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      long? max = null;
      foreach (var element in source) {
        long? item = selector (element);

        if (!max.HasValue)
          max = item;
        else if (item > max)
          max = item;
        empty = false;
      }

      if (empty)
        return null;
      return max;
    }

    public static double? Max<TSource> (this IEnumerable<TSource> source, Func<TSource, double?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      double? max = null;
      foreach (var element in source) {
        double? item = selector (element);

        if (!max.HasValue)
          max = item;
        else if (item > max)
          max = item;
        empty = false;
      }

      if (empty)
        return null;
      return max;
    }

    public static float? Max<TSource> (this IEnumerable<TSource> source, Func<TSource, float?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      float? max = null;
      foreach (var element in source) {
        float? item = selector (element);

        if (!max.HasValue)
          max = item;
        else if (item > max)
          max = item;
        empty = false;
      }

      if (empty)
        return null;
      return max;
    }

    public static decimal? Max<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      decimal? max = null;
      foreach (var element in source) {
        decimal? item = selector (element);

        if (!max.HasValue)
          max = item;
        else if (item > max)
          max = item;
        empty = false;
      }

      if (empty)
        return null;
      return max;
    }

    public static TResult Max<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
      Check.SourceAndSelector (source, selector);

      // TODO: inline
      return source.Select (selector).Max ();
    }

    #endregion

    #region Min

    public static int Min (this IEnumerable<int> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = int.MaxValue;
      foreach (var element in source){
        min = Math.Min (element, min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static long Min (this IEnumerable<long> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = long.MaxValue;
      foreach (var element in source){
        min = Math.Min (element, min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static double Min (this IEnumerable<double> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = double.MaxValue;
      foreach (var element in source){
        min = Math.Min (element, min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static float Min (this IEnumerable<float> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = float.MaxValue;
      foreach (var element in source){
        min = Math.Min (element, min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static decimal Min (this IEnumerable<decimal> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = decimal.MaxValue;
      foreach (var element in source){
        min = Math.Min (element, min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static int? Min (this IEnumerable<int?> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = int.MaxValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        min = Math.Min (element.Value, min);
        empty = false;
      }

      if (empty)
        return null;

      return min;
    }

    public static long? Min (this IEnumerable<long?> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = long.MaxValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        min = Math.Min (element.Value, min);
        empty = false;
      }

      if (empty)
        return null;

      return min;
    }

    public static double? Min (this IEnumerable<double?> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = double.MaxValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        min = Math.Min (element.Value, min);
        empty = false;
      }

      if (empty)
        return null;

      return min;
    }

    public static float? Min (this IEnumerable<float?> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = float.MaxValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        min = Math.Min (element.Value, min);
        empty = false;
      }

      if (empty)
        return null;

      return min;
    }

    public static decimal? Min (this IEnumerable<decimal?> source)
    {
      Check.Source (source);

      bool empty = true;
      var min = decimal.MaxValue;
        
      foreach (var element in source) {
        if (!element.HasValue)
          continue;

        min = Math.Min (element.Value, min);
        empty = false;
      }

      if (empty)
        return null;

      return min;
    }

    public static TSource Min<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var comparer = Comparer<TSource>.Default;

      TSource min = default (TSource);
      
      if (default (TSource) == null){
        foreach (var element in source) {
          if (element == null)
            continue;

          if (min == null || comparer.Compare (element, min) < 0)
            min = element;
        }
      } else {
        bool empty = true;
        foreach (var element in source) {
          if (empty){
            min = element;
            empty = false;
            continue;
          }
          if (comparer.Compare (element, min) < 0)
            min = element;
        }
        if (empty)
          throw new InvalidOperationException ();
      }
      return min;
    }

    public static int Min<TSource> (this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var min = int.MaxValue;
      foreach (var element in source){
        min = Math.Min (selector (element), min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static long Min<TSource> (this IEnumerable<TSource> source, Func<TSource, long> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var min = long.MaxValue;
      foreach (var element in source){
        min = Math.Min (selector (element), min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static double Min<TSource> (this IEnumerable<TSource> source, Func<TSource, double> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var min = double.MaxValue;
      foreach (var element in source){
        min = Math.Min (selector (element), min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static float Min<TSource> (this IEnumerable<TSource> source, Func<TSource, float> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var min = float.MaxValue;
      foreach (var element in source){
        min = Math.Min (selector (element), min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static decimal Min<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      var min = decimal.MaxValue;
      foreach (var element in source){
        min = Math.Min (selector (element), min);
        empty = false;
      }
      if (empty)
        throw new InvalidOperationException ();
      return min;
    }

    public static int? Min<TSource> (this IEnumerable<TSource> source, Func<TSource, int?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      int? min = null;
      foreach (var element in source) {
        int? item = selector (element);

        if (!min.HasValue)
          min = item;
        else if (item < min)
          min = item;
        empty = false;
      }

      if (empty)
        return null;
      return min;
    }

    public static long? Min<TSource> (this IEnumerable<TSource> source, Func<TSource, long?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      long? min = null;
      foreach (var element in source) {
        long? item = selector (element);

        if (!min.HasValue)
          min = item;
        else if (item < min)
          min = item;
        empty = false;
      }

      if (empty)
        return null;
      return min;
    }

    public static float? Min<TSource> (this IEnumerable<TSource> source, Func<TSource, float?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      float? min = null;
      foreach (var element in source) {
        float? item = selector (element);

        if (!min.HasValue)
          min = item;
        else if (item < min)
          min = item;
        empty = false;
      }

      if (empty)
        return null;
      return min;
    }

    public static double? Min<TSource> (this IEnumerable<TSource> source, Func<TSource, double?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      double? min = null;
      foreach (var element in source) {
        double? item = selector (element);

        if (!min.HasValue)
          min = item;
        else if (item < min)
          min = item;
        empty = false;
      }

      if (empty)
        return null;
      return min;
    }

    public static decimal? Min<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal?> selector)
    {
      Check.SourceAndSelector (source, selector);

      bool empty = true;
      decimal? min = null;
      foreach (var element in source) {
        decimal? item = selector (element);

        if (!min.HasValue)
          min = item;
        else if (item < min)
          min = item;
        empty = false;
      }

      if (empty)
        return null;
      return min;
    }

    public static TResult Min<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
      Check.SourceAndSelector (source, selector);

      // TODO: inline
      return source.Select (selector).Min ();
    }

    #endregion

    #region OfType

    public static IEnumerable<TResult> OfType<TResult> (this IEnumerable source)
    {
      Check.Source (source);

      return CreateOfTypeIterator<TResult> (source);
    }

    static IEnumerable<TResult> CreateOfTypeIterator<TResult> (IEnumerable source)
    {
      foreach (object element in source)
        if (element is TResult)
          yield return (TResult) element;
    }

    #endregion

    #region OrderBy

    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
      return OrderBy<TSource, TKey> (source, keySelector, null);
    }

    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        IComparer<TKey> comparer)
    {
      Check.SourceAndKeySelector (source, keySelector);

      return new OrderedSequence<TSource, TKey> (source, keySelector, comparer, SortDirection.Ascending);
    }

    #endregion

    #region OrderByDescending

    public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
      return OrderByDescending<TSource, TKey> (source, keySelector, null);
    }

    public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
    {
      Check.SourceAndKeySelector (source, keySelector);

      return new OrderedSequence<TSource, TKey> (source, keySelector, comparer, SortDirection.Descending);
    }

    #endregion

    #region Range

    public static IEnumerable<int> Range (int start, int count)
    {
      if (count < 0)
        throw new ArgumentOutOfRangeException ("count");

      long upto = ((long) start + count) - 1;

      if (upto > int.MaxValue)
        throw new ArgumentOutOfRangeException ();

      return CreateRangeIterator (start, (int) upto);
    }

    static IEnumerable<int> CreateRangeIterator (int start, int upto)
    {
      for (int i = start; i <= upto; i++)
        yield return i;
    }

    #endregion

    #region Repeat

    public static IEnumerable<TResult> Repeat<TResult> (TResult element, int count)
    {
      if (count < 0)
        throw new ArgumentOutOfRangeException ();

      return CreateRepeatIterator (element, count);
    }

    static IEnumerable<TResult> CreateRepeatIterator<TResult> (TResult element, int count)
    {
      for (int i = 0; i < count; i++)
        yield return element;
    }

    #endregion

    #region Reverse

    public static IEnumerable<TSource> Reverse<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var list = source as IList<TSource>;
      if (list == null)
        list = new List<TSource> (source);

      return CreateReverseIterator (list);
    }

    static IEnumerable<TSource> CreateReverseIterator<TSource> (IList<TSource> source)
    {
      for (int i = source.Count; i > 0; --i)
        yield return source [i - 1];
    }

    #endregion

    #region Select

    public static IEnumerable<TResult> Select<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
      Check.SourceAndSelector (source, selector);

      return CreateSelectIterator (source, selector);
    }

    static IEnumerable<TResult> CreateSelectIterator<TSource, TResult> (IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
      foreach (var element in source)
        yield return selector (element);
    }

    public static IEnumerable<TResult> Select<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
    {
      Check.SourceAndSelector (source, selector);

      return CreateSelectIterator (source, selector);
    }

    static IEnumerable<TResult> CreateSelectIterator<TSource, TResult> (IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
    {
      int counter = 0;
      foreach (TSource element in source) {
        yield return selector (element, counter);
        counter++;
      }
    }

    #endregion

    #region SelectMany

    public static IEnumerable<TResult> SelectMany<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
    {
      Check.SourceAndSelector (source, selector);

      return CreateSelectManyIterator (source, selector);
    }

    static IEnumerable<TResult> CreateSelectManyIterator<TSource, TResult> (IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
    {
      foreach (TSource element in source)
        foreach (TResult item in selector (element))
          yield return item;
    }

    public static IEnumerable<TResult> SelectMany<TSource, TResult> (this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TResult>> selector)
    {
      Check.SourceAndSelector (source, selector);

      return CreateSelectManyIterator (source, selector);
    }

    static IEnumerable<TResult> CreateSelectManyIterator<TSource, TResult> (IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TResult>> selector)
    {
      int counter = 0;
      foreach (TSource element in source) {
        foreach (TResult item in selector (element, counter))
          yield return item;
        counter++;
      }
    }

    public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult> (this IEnumerable<TSource> source,
      Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> selector)
    {
      Check.SourceAndCollectionSelectors (source, collectionSelector, selector);

      return CreateSelectManyIterator (source, collectionSelector, selector);
    }

    static IEnumerable<TResult> CreateSelectManyIterator<TSource, TCollection, TResult> (IEnumerable<TSource> source,
      Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> selector)
    {
      foreach (TSource element in source)
        foreach (TCollection collection in collectionSelector (element))
          yield return selector (element, collection);
    }

    public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult> (this IEnumerable<TSource> source,
      Func<TSource, int, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> selector)
    {
      Check.SourceAndCollectionSelectors (source, collectionSelector, selector);

      return CreateSelectManyIterator (source, collectionSelector, selector);
    }

    static IEnumerable<TResult> CreateSelectManyIterator<TSource, TCollection, TResult> (IEnumerable<TSource> source,
      Func<TSource, int, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> selector)
    {
      int counter = 0;
      foreach (TSource element in source)
        foreach (TCollection collection in collectionSelector (element, counter++))
          yield return selector (element, collection);
    }

    #endregion

    #region Single

    static TSource Single<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate, Fallback fallback)
    {
      var found = false;
      var item = default (TSource);

      foreach (var element in source) {
        if (!predicate (element))
          continue;

        if (found)
          throw new InvalidOperationException ();

        found = true;
        item = element;
      }

      if (!found && fallback == Fallback.Throw)
        throw new InvalidOperationException ();

      return item;
    }

    public static TSource Single<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      return source.Single (PredicateOf<TSource>.Always, Fallback.Throw);
    }

    public static TSource Single<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return source.Single (predicate, Fallback.Throw);
    }

    #endregion

    #region SingleOrDefault

    public static TSource SingleOrDefault<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      return source.Single (PredicateOf<TSource>.Always, Fallback.Default);
    }

    public static TSource SingleOrDefault<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return source.Single (predicate, Fallback.Default);
    }

    #endregion

    #region Skip

    public static IEnumerable<TSource> Skip<TSource> (this IEnumerable<TSource> source, int count)
    {
      Check.Source (source);

      return CreateSkipIterator (source, count);
    }

    static IEnumerable<TSource> CreateSkipIterator<TSource> (IEnumerable<TSource> source, int count)
    {
      var enumerator = source.GetEnumerator ();
      try {
        while (count-- > 0)
          if (!enumerator.MoveNext ())
            yield break;

        while (enumerator.MoveNext ())
          yield return enumerator.Current;

      } finally {
        enumerator.Dispose ();
      }
    }

    #endregion

    #region SkipWhile

    public static IEnumerable<TSource> SkipWhile<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return CreateSkipWhileIterator (source, predicate);
    }

    static IEnumerable<TSource> CreateSkipWhileIterator<TSource> (IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      bool yield = false;

      foreach (TSource element in source) {
        if (yield)
          yield return element;
        else
          if (!predicate (element)) {
            yield return element;
            yield = true;
          }
      }
    }

    public static IEnumerable<TSource> SkipWhile<TSource> (this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return CreateSkipWhileIterator (source, predicate);
    }

    static IEnumerable<TSource> CreateSkipWhileIterator<TSource> (IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
    {
      int counter = 0;
      bool yield = false;

      foreach (TSource element in source) {
        if (yield)
          yield return element;
        else
          if (!predicate (element, counter)) {
            yield return element;
            yield = true;
          }
        counter++;
      }
    }

    #endregion

    #region Sum

    public static int Sum (this IEnumerable<int> source)
    {
      Check.Source (source);
      int total = 0;
      
      foreach (var element in source)
        total = checked (total + element);
      return total;
    }

    public static int? Sum (this IEnumerable<int?> source)
    {
      Check.Source (source);

      int total = 0;
      foreach (var element in source) {
        if (element.HasValue)
          total = checked (total + element.Value);
      }
      return total;
    }

    public static int Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
      Check.SourceAndSelector (source, selector);
      int total = 0;

      foreach (var element in source)
        total = checked (total + selector (element));

      return total;
    }

    public static int? Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, int?> selector)
    {
      Check.SourceAndSelector (source, selector);

      int total = 0;
      foreach (var element in source) {
        var value = selector (element);
        if (value.HasValue)
          total = checked (total + value.Value);
      }
      return total;
    }

    public static long Sum (this IEnumerable<long> source)
    {
      Check.Source (source);

      long total = 0;
      
      foreach (var element in source)
        total = checked (total + element);
      return total;
    }

    public static long? Sum (this IEnumerable<long?> source)
    {
      Check.Source (source);

      long total = 0;
      foreach (var element in source) {
        if (element.HasValue)
          total = checked (total + element.Value);
      }
      return total;
    }

    public static long Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, long> selector)
    {
      Check.SourceAndSelector (source, selector);

      long total = 0;
      foreach (var element in source)
        total = checked (total + selector (element));
      return total;
    }

    public static long? Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, long?> selector)
    {
      Check.SourceAndSelector (source, selector);

      long total = 0;
      foreach (var element in source) {
        var value = selector (element);
        if (value.HasValue)
          total = checked (total + value.Value);
      }
      return total;
    }

    public static double Sum (this IEnumerable<double> source)
    {
      Check.Source (source);

      double total = 0;
      
      foreach (var element in source)
        total = checked (total + element);
      return total;
    }

    public static double? Sum (this IEnumerable<double?> source)
    {
      Check.Source (source);

      double total = 0;
      foreach (var element in source) {
        if (element.HasValue)
          total = checked (total + element.Value);
      }
      return total;
    }

    public static double Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, double> selector)
    {
      Check.SourceAndSelector (source, selector);

      double total = 0;
      
      foreach (var element in source)
        total = checked (total + selector (element));
      return total;
    }

    public static double? Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, double?> selector)
    {
      Check.SourceAndSelector (source, selector);

      double total = 0;
      foreach (var element in source) {
        var value = selector (element);
        if (value.HasValue)
          total = checked (total + value.Value);
      }
      return total;
    }

    public static float Sum (this IEnumerable<float> source)
    {
      Check.Source (source);

      float total = 0;
      
      foreach (var element in source)
        total = checked (total + element);
      return total;
    }

    public static float? Sum (this IEnumerable<float?> source)
    {
      Check.Source (source);

      float total = 0;
      foreach (var element in source) {
        if (element.HasValue)
          total = checked (total + element.Value);
      }
      return total;

    }

    public static float Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, float> selector)
    {
      Check.SourceAndSelector (source, selector);
      float total = 0;
      foreach (var element in source)
        total = checked (total + selector (element));
      return total;
    }

    public static float? Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, float?> selector)
    {
      Check.SourceAndSelector (source, selector);

      float total = 0;
      foreach (var element in source) {
        var value = selector (element);
        if (value.HasValue)
          total = checked (total + value.Value);
      }
      return total;
    }

    public static decimal Sum (this IEnumerable<decimal> source)
    {
      Check.Source (source);
      decimal total = 0;
      
      foreach (var element in source)
        total = checked (total + element);
      return total;
    }

    public static decimal? Sum (this IEnumerable<decimal?> source)
    {
      Check.Source (source);

      decimal total = 0;
      foreach (var element in source) {
        if (element.HasValue)
          total = checked (total + element.Value);
      }
      return total;

    }

    public static decimal Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal> selector)
    {
      Check.SourceAndSelector (source, selector);
      decimal total = 0;
      
      foreach (var element in source)
        total = checked (total + selector (element));
      return total;
    }

    public static decimal? Sum<TSource> (this IEnumerable<TSource> source, Func<TSource, decimal?> selector)
    {
      Check.SourceAndSelector (source, selector);

      decimal total = 0;
      foreach (var element in source) {
        var value = selector (element);
        if (value.HasValue)
          total = checked (total + value.Value);
      }
      return total;
    }

    #endregion

    #region Take

    public static IEnumerable<TSource> Take<TSource> (this IEnumerable<TSource> source, int count)
    {
      Check.Source (source);

      return CreateTakeIterator (source, count);
    }

    static IEnumerable<TSource> CreateTakeIterator<TSource> (IEnumerable<TSource> source, int count)
    {
      if (count <= 0)
        yield break;

      int counter = 0;
      foreach (TSource element in source) {
        yield return element;

        if (++counter == count)
          yield break;
      }
    }

    #endregion

    #region TakeWhile

    public static IEnumerable<TSource> TakeWhile<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return CreateTakeWhileIterator (source, predicate);
    }

    static IEnumerable<TSource> CreateTakeWhileIterator<TSource> (IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      foreach (var element in source) {
        if (!predicate (element))
          yield break;

        yield return element;
      }
    }

    public static IEnumerable<TSource> TakeWhile<TSource> (this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return CreateTakeWhileIterator (source, predicate);
    }

    static IEnumerable<TSource> CreateTakeWhileIterator<TSource> (IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
    {
      int counter = 0;
      foreach (var element in source) {
        if (!predicate (element, counter))
          yield break;

        yield return element;
        counter++;
      }
    }

    #endregion

    #region ThenBy

    public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey> (this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
      return ThenBy<TSource, TKey> (source, keySelector, null);
    }

    public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey> (this IOrderedEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
    {
      Check.SourceAndKeySelector (source, keySelector);

      return source.CreateOrderedEnumerable (keySelector, comparer, false);
    }

    #endregion

    #region ThenByDescending

    public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey> (this IOrderedEnumerable<TSource> source,
      Func<TSource, TKey> keySelector)
    {
      return ThenByDescending<TSource, TKey> (source, keySelector, null);
    }

    public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey> (this IOrderedEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
    {
      Check.SourceAndKeySelector (source, keySelector);

      return source.CreateOrderedEnumerable (keySelector, comparer, true);
    }

    #endregion

    #region ToArray

    public static TSource [] ToArray<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      var collection = source as ICollection<TSource>;
      if (collection != null) {
        var array = new TSource [collection.Count];
        collection.CopyTo (array, 0);
        return array;
      }

      return new List<TSource> (source).ToArray ();
    }

    #endregion

    #region ToDictionary
    public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
    {
      return ToDictionary<TSource, TKey, TElement> (source, keySelector, elementSelector, null);
    }

    public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
    {
      Check.SourceAndKeyElementSelectors (source, keySelector, elementSelector);

      if (comparer == null)
        comparer = EqualityComparer<TKey>.Default;

      var dict = new Dictionary<TKey, TElement> (comparer);
      foreach (var e in source)
        dict.Add (keySelector (e), elementSelector (e));

      return dict;
    }

    public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
      return ToDictionary (source, keySelector, null);
    }

    public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey> (this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
      return ToDictionary<TSource, TKey, TSource> (source, keySelector, Function<TSource>.Identity, comparer);
    }

    #endregion

    #region ToList
    public static List<TSource> ToList<TSource> (this IEnumerable<TSource> source)
    {
      Check.Source (source);

      return new List<TSource> (source);
    }
    #endregion

    #region ToLookup

    public static ILookup<TKey, TSource> ToLookup<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
      return ToLookup<TSource, TKey, TSource> (source, keySelector, Function<TSource>.Identity, null);
    }

    public static ILookup<TKey, TSource> ToLookup<TSource, TKey> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
      return ToLookup<TSource, TKey, TSource> (source, keySelector, element => element, comparer);
    }

    public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
    {
      return ToLookup<TSource, TKey, TElement> (source, keySelector, elementSelector, null);
    }

    public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement> (this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
    {
      Check.SourceAndKeyElementSelectors (source, keySelector, elementSelector);

      List<TElement> nullKeyElements = null;
      
      var dictionary = new Dictionary<TKey, List<TElement>> (comparer ?? EqualityComparer<TKey>.Default);
      foreach (var element in source) {
        var key = keySelector (element);

        List<TElement> list;
        
        if (key == null) {
          if (nullKeyElements == null)
            nullKeyElements = new List<TElement> ();
          
          list = nullKeyElements;
        } else if (!dictionary.TryGetValue (key, out list)) {
          list = new List<TElement> ();
          dictionary.Add (key, list);
        }

        list.Add (elementSelector (element));
      }

      return new Lookup<TKey, TElement> (dictionary, nullKeyElements);
    }

    #endregion

    #region SequenceEqual

    public static bool SequenceEqual<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
      return first.SequenceEqual (second, null);
    }

    public static bool SequenceEqual<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      Check.FirstAndSecond (first, second);

      if (comparer == null)
        comparer = EqualityComparer<TSource>.Default;

      using (IEnumerator<TSource> first_enumerator = first.GetEnumerator (),
        second_enumerator = second.GetEnumerator ()) {

        while (first_enumerator.MoveNext ()) {
          if (!second_enumerator.MoveNext ())
            return false;

          if (!comparer.Equals (first_enumerator.Current, second_enumerator.Current))
            return false;
        }

        return !second_enumerator.MoveNext ();
      }
    }

    #endregion

    #region Union

    public static IEnumerable<TSource> Union<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
    {
      Check.FirstAndSecond (first, second);

      return first.Union (second, null);
    }

    public static IEnumerable<TSource> Union<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      Check.FirstAndSecond (first, second);

      if (comparer == null)
        comparer = EqualityComparer<TSource>.Default;

      return CreateUnionIterator (first, second, comparer);
    }

    static IEnumerable<TSource> CreateUnionIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
      var items = new HashSet<TSource> (comparer);
      foreach (var element in first) {
        if (! items.Contains (element)) {
          items.Add (element);
          yield return element;
        }
      }

      foreach (var element in second) {
        if (! items.Contains (element, comparer)) {
          items.Add (element);
          yield return element;
        }
      }
    }

    #endregion
    
#if NET_4_0    
    #region Zip
    
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult> (this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
      Check.FirstAndSecond (first, second);
      if (resultSelector == null)
        throw new ArgumentNullException ("resultSelector");
        
      return CreateZipIterator (first, second, resultSelector);
    }
    
    static IEnumerable<TResult> CreateZipIterator<TFirst, TSecond, TResult> (IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector)
    {
      using (IEnumerator<TFirst> first_enumerator = first.GetEnumerator ()) {
        using (IEnumerator<TSecond> second_enumerator = second.GetEnumerator ()) {

          while (first_enumerator.MoveNext () && second_enumerator.MoveNext ()) {
            yield return selector (first_enumerator.Current, second_enumerator.Current);
          }
        }
      }
    }
    
    #endregion
#endif    

    #region Where

    public static IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return CreateWhereIterator (source, predicate);
    }

    static IEnumerable<TSource> CreateWhereIterator<TSource> (IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
      foreach (TSource element in source)
        if (predicate (element))
          yield return element;
    }

    public static IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
    {
      Check.SourceAndPredicate (source, predicate);

      return CreateWhereIterator (source, predicate);
    }

    static IEnumerable<TSource> CreateWhereIterator<TSource> (this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
    {
      int counter = 0;
      foreach (TSource element in source) {
        if (predicate (element, counter))
          yield return element;
        counter++;
      }
    }

    #endregion

    internal static ReadOnlyCollection<TSource> ToReadOnlyCollection<TSource> (this IEnumerable<TSource> source)
    {
      if (source == null)
        return ReadOnlyCollectionOf<TSource>.Empty;

      var ro = source as ReadOnlyCollection<TSource>;
      if (ro != null)
        return ro;

      return new ReadOnlyCollection<TSource> (source.ToArray<TSource> ());
    }
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.