//#define __MonoCS__
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace miew.Enumerable
{
	using String = System.String;

	public static class Extensions
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// .NET 4.0 has String.Join(String, IEnumerable&lt;String&gt;)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static String StringJoin<T>(this IEnumerable<T> ie, String sep)
		{
			return String.Join(sep, ie as IEnumerable<String> ?? ie.Select(e => e.ToString()));
		}

		public static HashSet<T> ToHashSet<T>(this IEnumerable<T> ie)
		{
			return new HashSet<T>(ie);
		}

		/// <summary>
		/// Append one element to the end of a sequence.
		/// </summary>
		/// <param name="seq">The original sequence. Can be null.</param>
		/// <param name="element">An element to append.</param>
		public static IEnumerable<T> Append<T>(this IEnumerable<T> seq, T element)
		{
			if (seq != null)
			{
				foreach (T t in seq)
					yield return t;
			}
			yield return element;
		}

		/// <summary>
		/// Prepend one element to the beginning of a sequence.
		/// </summary>
		/// <param name="seq">The original sequence. Can be null.</param>
		/// <param name="element">An element to prepend.</param>
		public static IEnumerable<T> Prepend<T>(this IEnumerable<T> seq, T element)
		{
			yield return element;
			if (seq != null)
			{
				foreach (T t in seq)
					yield return t;
			}
		}

		//struct SingleItemEnumerable<T> : IEnumerable<T>
		//{
		//    T t;
		//    public SingleItemEnumerable(T t)
		//    {
		//        this.t = t;
		//    }

		//    struct _enum : IEnumerator<T>
		//    {
		//        T t;
		//        int ix;
		//        public _enum(T t)
		//        {
		//            this.t = t;
		//            this.ix = -1;
		//        }
		//        public T Current { get { return t; } }
		//        object IEnumerator.Current { get { return t; } }
		//        public bool MoveNext() { return ++ix == 0; }
		//        public void Reset() { ix = -1; }
		//        public void Dispose() { }
		//    };

		//    public IEnumerator<T> GetEnumerator() { return new _enum(t); }

		//    IEnumerator IEnumerable.GetEnumerator() { return new _enum(t); }
		//};

		public static IEnumerable<T> AsSingleton<T>(this T t)
		{
			//return new SingleItemEnumerable<T>(t);
			return new T[] { t };
		}

		public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int n)
		{
			if (source == null)
				throw new ArgumentNullException("source");
			if (n < 0)
				throw new ArgumentOutOfRangeException("must not be negative", "n");
			if (n > 0)
			{
				T[] result = new T[n];
				int i = 0;
				int count = 0;
				foreach (T t in source)
				{
					result[i] = t;
					i = (i + 1) % n;
					count++;
				}
				if (count < n)
				{
					n = count;
					i = 0;
				}
				for (int j = 0; j < n; j++)
					yield return result[(i + j) % n];
			}
		}


		public static Double WeightedAverage<T>(this IEnumerable<T> seq, Func<T, double> value_selector, Func<T, double> weight_selector)
		{
			Double sum = seq.Sum(weight_selector);
			Double avg = 0;
			foreach (T t in seq)
				avg += value_selector(t) * weight_selector(t) / sum;
			return avg;
		}

		public static T Middle<T>(this IEnumerable<T> seq)
		{
			var ie1 = seq.GetEnumerator();
			if (!ie1.MoveNext())
				throw new ArgumentException();
			T current = ie1.Current;

			var ie2 = seq.GetEnumerator();
			ie2.MoveNext();

			while (ie2.MoveNext())
			{
				if (!ie2.MoveNext())
					break;
				ie1.MoveNext();
				current = ie1.Current;
			}
			return current;
		}

		public static bool Any<T>(this IEnumerable<T> seq, Func<T, int, bool> predicate)
		{
			int i = 0;
			foreach (T t in seq)
				if (predicate(t, i++))
					return true;
			return false;
		}

		/// <summary>
		/// Return the (distinct) set union of all elements of type TDst generated by a selector which projects each 
		/// source element of type TSrc from the sequence into a new sequence of zero or more elements of type
		/// TDst
		/// </summary>
		public static HashSet<TDst> UnionMany<TSrc, TDst>(this IEnumerable<TSrc> seq, Func<TSrc, IEnumerable<TDst>> selector)
		{
			HashSet<TDst> hs = new HashSet<TDst>();
			foreach (TSrc t in seq)
				hs.UnionWith(selector(t));
			return hs;
		}

		/// <summary>
		/// Although there's no problem on .NET, the 'GroupBy' extension method is unusably slow on Mono. Below, we 
		/// implement a usable version from scratch
		/// </summary>
		public static IEnumerable<IGrouping<TKey, TSrc>> _GroupBy<TSrc, TKey>(this IEnumerable<TSrc> ie, Func<TSrc, TKey> fn)
		{
#if __MonoCS__
			Dictionary<TKey, _grouping<TKey, TSrc>> d = new Dictionary<TKey, _grouping<TKey, TSrc>>();
			foreach (TSrc src in ie)
			{
				TKey k = fn(src);
				_grouping<TKey, TSrc> l;
				if (!d.TryGetValue(k, out l))
					d.Add(k, l = new _grouping<TKey, TSrc>(k));
				l.Add(src);
			}
			return d.Values;
#else
			return ie.GroupBy(fn);
#endif
		}
		public static IEnumerable<IGrouping<TKey, TSrc>> _GroupBy<TSrc, TKey>(this IEnumerable<TSrc> ie, Func<TSrc, TKey> fn, IEqualityComparer<TKey> c)
		{
#if __MonoCS__
			Dictionary<TKey, _grouping<TKey, TSrc>> d = new Dictionary<TKey, _grouping<TKey, TSrc>>(c);
			foreach (TSrc src in ie)
			{
				TKey k = fn(src);
				_grouping<TKey, TSrc> l;
				if (!d.TryGetValue(k, out l))
					d.Add(k, l = new _grouping<TKey, TSrc>(k));
				l.Add(src);
			}
			return d.Values;
#else
			return ie.GroupBy(fn, c);
#endif
		}
#if __MonoCS__
		class _grouping<TKey, TSrc> : List<TSrc>, IGrouping<TKey, TSrc>
		{
			public TKey _key;
			public _grouping(TKey k) { _key = k; }

			public TKey Key
			{
				get { return _key; }
			}
		}
#endif

		/// <summary>
		/// 
		/// </summary>
		public static IEnumerable<T> Sort<T>(this IEnumerable<T> seq)
			where T : IComparable<T>
		{
			return seq.OrderBy(_t => _t);
		}

		/// <summary>
		/// 
		/// </summary>
		public static IEnumerable<T> OrderByDistinct<T>(this IEnumerable<T> seq)
			where T : IComparable<T>
		{
			IEnumerator<T> iei = seq.OrderBy(_t => _t).GetEnumerator();
			if (!iei.MoveNext())
				yield break;
			T t, t2;
			yield return t = iei.Current;
			while (iei.MoveNext())
			{
				if (!t.Equals(t2 = iei.Current))
					yield return t = t2;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public static IEnumerable<T> OrderByDistinct<T, TKey>(this IEnumerable<T> seq, Func<T, TKey> selector)
			where TKey : IComparable<TKey>
		{
			IEnumerator<T> iei = seq.OrderBy(selector).GetEnumerator();
			if (!iei.MoveNext())
				yield break;
			T t, t2;
			yield return t = iei.Current;
			while (iei.MoveNext())
			{
				if (!t.Equals(t2 = iei.Current))
					yield return t = t2;
			}
		}


		/// <summary>
		/// Reduce a sequence of 'n' elements to 'n-1' by combining adjacent elements
		/// </summary>
		public static IEnumerable<TDst> ReduceAdjacent<TSrc, TDst>(this IEnumerable<TSrc> seq, Func<TSrc, TSrc, TDst> combiner)
		{
			IEnumerator<TSrc> iei = seq.GetEnumerator();
			if (!iei.MoveNext())
				yield break;
			TSrc i_prev = iei.Current;
			while (iei.MoveNext())
				yield return combiner(i_prev, i_prev = iei.Current);
		}

		/// <summary>
		/// Exclude all instances of element <paramref name="element"/> from the sequence <paramref name="seq"/>.
		/// </summary>
		/// <param name="seq">The original sequence.</param>
		/// <param name="element">The element to exclude.</param>
		public static IEnumerable<T> Exclude<T>(this IEnumerable<T> seq, T element)
		{
			foreach (T t in seq)
				if (!t.Equals(element))
					yield return t;
		}

		public static IEnumerable<T> ExceptElementAt<T>(this IEnumerable<T> seq, int ix)
		{
			int i = 0;
			foreach (T t in seq)
			{
				if (i != ix)
					yield return t;
				i++;
			}
		}

		public static IEnumerable<T> OfExactType<T>(this IEnumerable seq)
		{
			foreach (Object t in seq)
				if (t.GetType() == typeof(T))
					yield return (T)t;
		}

		/// <summary>
		/// Return one source element from each range result mapped by the selector function
		/// </summary>
#if true
		public static IEnumerable<TSrc> Distinct<TSrc, TKey>(this IEnumerable<TSrc> seq, Converter<TSrc, TKey> keySelector)
		{
			if (seq == null)
				throw new ArgumentNullException("source");

			return seq._GroupBy(e => keySelector(e)).Select(g => g.First());
		}
#else
		public static IEnumerable<TSrc> Distinct<TSrc, TKey>(this IEnumerable<TSrc> source, Converter<TSrc, TKey> keySelector)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			return source.Distinct<TSrc>(new LambdaComparer<TSrc>((s1, s2) =>
			{
				return keySelector(s1).Equals(keySelector(s2));
			}, h => 0));
		}
#endif

		class distinct_grouping<TKey, TSrc> : HashSet<TSrc>, IGrouping<TKey, TSrc>
		{
			public TKey _key;
			public distinct_grouping(TKey k) { _key = k; }

			public TKey Key
			{
				get { return _key; }
			}
		}

		/// <summary>
		/// Group elements of the sequence according to the key generated by keySelector, where each group contains a distinct
		/// set of elements projected by elementSelector.
		/// </summary>
		public static IEnumerable<IGrouping<TKey, TElement>> GroupByDistinct<TSrc, TKey, TElement>(
			this IEnumerable<TSrc> seq,
			Func<TSrc, TKey> keySelector,
			Func<TSrc, TElement> elementSelector)
		{
			var ie = seq.GetEnumerator();
			if (!ie.MoveNext())
				return System.Linq.Enumerable.Empty<IGrouping<TKey, TElement>>();

			Dictionary<TKey, distinct_grouping<TKey, TElement>> d = new Dictionary<TKey, distinct_grouping<TKey, TElement>>();
			do
			{
				TSrc t = ie.Current;
				TKey k = keySelector(t);
				distinct_grouping<TKey, TElement> l;
				if (!d.TryGetValue(k, out l))
					d.Add(k, l = new distinct_grouping<TKey, TElement>(k));
				l.Add(elementSelector(t));
			}
			while (ie.MoveNext());
			return d.Values;
		}

		public static int CountDistinct<TSrc, TDst>(this IEnumerable<TSrc> seq, Func<TSrc, TDst> selector)
		{
			var ie = seq.GetEnumerator();
			if (!ie.MoveNext())
				return 0;
			HashSet<TDst> hs = new HashSet<TDst>();
			do
				hs.Add(selector(ie.Current));
			while (ie.MoveNext());
			return hs.Count;
		}
		public static int CountDistinct<TSrc>(this IEnumerable<TSrc> seq)
		{
			var ie = seq.GetEnumerator();
			if (!ie.MoveNext())
				return 0;
			HashSet<TSrc> hs = new HashSet<TSrc>();
			do
				hs.Add(ie.Current);
			while (ie.MoveNext());
			return hs.Count;
		}
		public static int CountDistinctValues<K, V>(this IDictionary<K, V> seq)
		{
			var ie = seq.GetEnumerator();
			if (!ie.MoveNext())
				return 0;
			HashSet<V> hs = new HashSet<V>();
			do
				hs.Add(ie.Current.Value);
			while (ie.MoveNext());
			return hs.Count;
		}

		public static bool IsDistinct<T>(this IEnumerable<T> seq)
		{
			var ie = seq.GetEnumerator();
			if (ie.MoveNext())
			{
				HashSet<T> hs = new HashSet<T>();
				do
				{
					T t = ie.Current;
					if (hs.Contains(t))
						return false;
					hs.Add(t);
				}
				while (ie.MoveNext());
			}
			return true;
		}

		/// <summary>
		/// Return a sequence where only the elements from the original sequence which match the predicate are transformed.
		/// </summary>
		/// <param name="seq">The original sequence.</param>
		/// <param name="filter">A predicate for matching elements from the original sequence.</param>
		/// <param name="selector">A transformation which will be applied to matching elements.</param>
		public static IEnumerable<TResult> WhereSelect<TSrc, TResult>(this IEnumerable<TSrc> seq, Predicate<TSrc> filter, Converter<TSrc, TResult> selector)
		{
			foreach (TSrc t in seq)
				if (filter(t))
					yield return selector(t);
		}

		/// <summary>
		/// Returns true if the sequence is empty, that is, if it has no elements. Also returns null if <paramref name="seq"/> is <i>null</i>.
		/// </summary>
		/// <param name="seq">The sequence to examine, or null.</param>
		/// <returns>True if the sequence is empty or null.</returns>
		public static bool None<T>(this IEnumerable<T> seq)
		{
			if (seq == null)
				return true;
			ICollection<T> ic = seq as ICollection<T>;
			if (ic != null)
				return ic.Count == 0;
			return !seq.GetEnumerator().MoveNext();
		}

		/// <summary>
		/// Returns true if the sequence is empty, that is, if it has no elements. Also returns null if <paramref name="seq"/> is <i>null</i>.
		/// </summary>
		/// <param name="seq">The sequence to examine, or null.</param>
		/// <param name="predicate">A filter function which identifies elements to ignore when considering if the list is empty</param>
		/// <returns>True if the sequence is empty or null.</returns>
		public static bool None<T>(this IEnumerable<T> seq, Func<T, bool> predicate)
		{
			return seq == null ? true : !seq.Any(predicate);
		}


		/// <summary>
		/// Returns the element from a sequence that maximizes the function <paramref name="objective"/>.
		/// </summary>
		/// <typeparam name="TArg">A type which implements IComparable&lt;T&gt;</typeparam>
		/// <param name="seq">A sequence of at least one element.</param>
		/// <param name="objective">An objective function to maximize. <paramref name="objective"/> will not be called more than once per element.</param>
		/// <returns>The element in the sequence that maximizes the function. If more than one element equivalently maximize the function, the first such element is returned.</returns>
		/// <remarks>There must be at least one element in the sequence. If there is exactly one element, <paramref name="objective"/> will not be called, so do not expect
		/// its side-effects to occur.</remarks>
		public static TSrc ArgMax<TSrc, TArg>(this IEnumerable<TSrc> seq, Converter<TSrc, TArg> objective) where TArg : IComparable<TArg>
		{
			IEnumerator<TSrc> e = seq.GetEnumerator();
			if (!e.MoveNext())
				throw new InvalidOperationException("Sequence has no elements.");

			TSrc t = e.Current;
			if (e.MoveNext())
			{
				TArg v, max_val = objective(t);
				do
				{
					TSrc t_try;
					if ((v = objective(t_try = e.Current)).CompareTo(max_val) > 0)
					{
						t = t_try;
						max_val = v;
					}
				}
				while (e.MoveNext());
			}
			return t;
		}

		public static bool TryArgMax<TSrc, TArg>(this IEnumerable<TSrc> seq, Converter<TSrc, TArg> objective, out TSrc t) where TArg : IComparable<TArg>
		{
			IEnumerator<TSrc> e = seq.GetEnumerator();
			if (!e.MoveNext())
			{
				t = default(TSrc);
				return false;
			}

			t = e.Current;
			if (e.MoveNext())
			{
				TArg max_val = objective(t);
				do
				{
					TArg v;
					TSrc t_try;
					if ((v = objective(t_try = e.Current)).CompareTo(max_val) > 0)
					{
						t = t_try;
						max_val = v;
					}
				}
				while (e.MoveNext());
			}
			return true;
		}

		/// <summary>
		/// Returns the element from a sequence that minimizes the function <paramref name="objective"/>.
		/// </summary>
		/// <typeparam name="TArg">A type which implements IComparable&lt;T&gt;</typeparam>
		/// <param name="seq">A sequence of at least one element.</param>
		/// <param name="objective">An objective function to minimize. <paramref name="objective"/> will not be called more than once per element.</param>
		/// <returns>The element in the sequence that maximizes the function. If more than one element equivalently minimize the function, the first such element is returned.</returns>
		/// <remarks>There must be at least one element in the sequence. If there is exactly one element, <paramref name="objective"/> will not be called, so do not expect
		/// its side-effects to occur.</remarks>
		public static TSrc ArgMin<TSrc, TArg>(this IEnumerable<TSrc> seq, Converter<TSrc, TArg> objective) where TArg : IComparable<TArg>
		{
			IEnumerator<TSrc> e = seq.GetEnumerator();
			if (!e.MoveNext())
				throw new InvalidOperationException("Sequence has no elements.");

			TSrc t = e.Current;
			if (e.MoveNext())
			{
				TArg v, min_val = objective(t);
				do
				{
					TSrc t_try;
					if ((v = objective(t_try = e.Current)).CompareTo(min_val) < 0)
					{
						t = t_try;
						min_val = v;
					}
				}
				while (e.MoveNext());
			}
			return t;
		}

		/// <summary>
		/// Returns the index of the first element in a sequence that satisfies the predicate.
		/// </summary>
		public static int IndexOfFirst<TSrc>(this IEnumerable<TSrc> seq, Func<TSrc, bool> predicate)
		{
			int i = 0;
			foreach (TSrc t in seq)
			{
				if (predicate(t))
					return i;
				i++;
			}
			return -1;
		}


		/// <summary>
		/// Returns the index of the only element in a sequence that satisfies the predicate, or -1 if there is not exactly one such element.
		/// </summary>
		public static int IndexOfOnly<TSrc>(this IEnumerable<TSrc> seq, Func<TSrc, bool> predicate)
		{
			int i = 0;
			int i_found = -1;
			foreach (TSrc t in seq)
			{
				if (predicate(t))
				{
					if (i_found != -1)
						return -1;
					i_found = i;
				}
				i++;
			}
			return i_found;
		}

		public static bool TryMax(this IEnumerable<int> seq, out int t)
		{
			IEnumerator<int> e = seq.GetEnumerator();
			if (!e.MoveNext())
			{
				t = int.MinValue;
				return false;
			}

			t = e.Current;
			if (!e.MoveNext())
				return true;

			do
			{
				int tx = e.Current;
				if (tx > t)
					t = tx;
			}
			while (e.MoveNext());
			return true;
		}

		public static bool TryMax<TSrc>(this IEnumerable<TSrc> seq, Converter<TSrc, int> objective, out int t)
		{
			IEnumerator<TSrc> e = seq.GetEnumerator();
			if (!e.MoveNext())
			{
				t = int.MinValue;
				return false;
			}
			t = objective(e.Current);
			if (e.MoveNext())
			{
				do
				{
					int tx = objective(e.Current);
					if (tx > t)
						t = tx;
				}
				while (e.MoveNext());
			}
			return true;
		}



		/// <summary>
		/// Returns the index of the element in a sequence that maximizes the function <paramref name="objective"/>.
		/// </summary>
		/// <typeparam name="TArg">A type which implements IComparable&lt;T&gt;</typeparam>
		/// <param name="seq">A sequence of zero or more elements.</param>
		/// <param name="objective">An objective function to maximize. <paramref name="objective"/> will not be called more than once per element.</param>
		/// <returns>The index of the element in the sequence that maximizes the function, or <value>-1</value> if the sequence has no elements. If more than one element 
		/// equivalently maximize the function, the index of the first such element is returned.</returns>
		public static int IndexOfMax<TSrc, TArg>(this IEnumerable<TSrc> seq, Converter<TSrc, TArg> objective) where TArg : IComparable<TArg>
		{
			IEnumerator<TSrc> e = seq.GetEnumerator();
			if (!e.MoveNext())
				return -1;

			int max_ix = 0;
			TSrc t = e.Current;
			if (!e.MoveNext())
				return max_ix;

			TArg tx, max_val = objective(t);
			int i = 1;
			do
			{
				if ((tx = objective(e.Current)).CompareTo(max_val) > 0)
				{
					max_val = tx;
					max_ix = i;
				}
				i++;
			}
			while (e.MoveNext());
			return max_ix;
		}

		/// <summary>
		/// Returns the index of the element in a sequence that minimizes the function <paramref name="objective"/>.
		/// </summary>
		/// <typeparam name="TArg">A type which implements IComparable&lt;T&gt;</typeparam>
		/// <param name="seq">A sequence of zero or more elements.</param>
		/// <param name="objective">An objective function to minimize. <paramref name="objective"/> will not be called more than once per element.</param>
		/// <returns>The index of the element in the sequence that minimize the function, or <value>-1</value> if the sequence has no elements. If more than one element 
		/// equivalently minimize the function, the index of the first such element is returned.</returns>
		public static int IndexOfMin<TSrc, TArg>(this IEnumerable<TSrc> seq, Converter<TSrc, TArg> objective) where TArg : IComparable<TArg>
		{
			IEnumerator<TSrc> e = seq.GetEnumerator();
			if (!e.MoveNext())
				return -1;

			int min_ix = 0;
			TSrc t = e.Current;
			if (!e.MoveNext())
				return min_ix;

			TArg tx, min_val = objective(t);
			int i = 1;
			do
			{
				if ((tx = objective(e.Current)).CompareTo(min_val) < 0)
				{
					min_val = tx;
					min_ix = i;
				}
				i++;
			}
			while (e.MoveNext());
			return min_ix;
		}

		/// <summary>
		/// Pair-up the elements of a vector of elements of type T
		/// </summary>
		public static IEnumerable<KeyValuePair<T, T>> PairOff<T>(this IEnumerable<T> seq)
		{
			IEnumerator<T> e = seq.GetEnumerator();
			while (e.MoveNext())
			{
				T first = e.Current;
				if (e.MoveNext())
					yield return new KeyValuePair<T, T>(first, e.Current);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static IList<T> Shuffle<T>(this IEnumerable<T> source)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			Random rnd = new Random();
			List<T> result = source.ToList();
			int c = result.Count;
			for (int i = 0; i < c; i++)
				miew.Array.Extensions.Swap<T>(result, i, rnd.Next(c));
			return result;
		}

		public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
			this IEnumerable<TSource> seq,
			Func<TSource, TKey> keySelector,
			Func<TKey, TKey, bool> comparer)
		{
			return seq._GroupBy(keySelector, new LambdaComparer<TKey>(comparer));
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Filter for non-default(T) values (i.e. non-null references, non-default values)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static IEnumerable<T> NotDefault<T>(this IEnumerable<T> source)
		{
			return source.Where(e => !Object.Equals(e, default(T)));
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Filter for non-default(T) values (i.e. non-null references, non-default values)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static IEnumerable<TSrc> NotDefault<TSrc, TAny>(this IEnumerable<TSrc> source, Converter<TSrc, TAny> selector)
		{
			return source.Where(e => !Object.Equals(selector(e), default(TAny)));
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static IEnumerable<TDst> SelectNotNull<TSrc, TDst>(this IEnumerable<TSrc> source, Func<TSrc, TDst> selector) where TDst : class
		{
			IEnumerator<TSrc> ie = source.GetEnumerator();
			TDst e;
			while (ie.MoveNext())
				if ((e = selector(ie.Current)) != null)
					yield return e;
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		class LambdaComparer<T> : IEqualityComparer<T>
		{
			private readonly Func<T, T, bool> c;
			private readonly Func<T, int> h;

			public LambdaComparer(Func<T, T, bool> lambdaComparer) :
				this(lambdaComparer, o => o.GetHashCode())
			{
			}

			public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
			{
				if (lambdaComparer == null)
					throw new ArgumentNullException("lambdaComparer");
				if (lambdaHash == null)
					throw new ArgumentNullException("lambdaHash");

				c = lambdaComparer;
				h = lambdaHash;
			}

			public bool Equals(T x, T y)
			{
				return c(x, y);
			}

			public int GetHashCode(T obj)
			{
				return h(obj);
			}
		};
	};
}