// ----------------------------------------------------------------------------------------------
// Copyright (c) Mårten Rånge.
// ----------------------------------------------------------------------------------------------
// This source code is subject to terms and conditions of the Microsoft Public License. A
// copy of the license can be found in the License.html file at the root of this distribution.
// If you cannot locate the Microsoft Public License, please send an email to
// dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
// by the terms of the Microsoft Public License.
// ----------------------------------------------------------------------------------------------
// You must not remove this notice, or any other, from this software.
// ----------------------------------------------------------------------------------------------

// ----------------------------------------------------------------------------
#ifndef CPPLINQ__HEADER_GUARD
#   define CPPLINQ__HEADER_GUARD
// ----------------------------------------------------------------------------
#include <algorithm>
#include <cassert>
#include <climits>
#include <exception>
#include <functional>
#include <iterator>
#include <list>
#include <map>
#include <numeric>
#include <set>
#include <string>
#include <type_traits>
#include <vector>
#include <deque>
#include <unordered_set>
#// ----------------------------------------------------------------------------
#ifdef _MSC_VER
#   pragma warning (push)
#       pragma warning (disable:4512)   // 'class' : assignment operator could not be generated
// Rationale: cpplinq doesn't rely on assignment operators
#       pragma warning (disable:4702)   // unreachable code
// Rationale: Depending on the predicates and the calling
// code the compiler might detect unreacable code that with
// other predicates are reachable.
#endif
// ----------------------------------------------------------------------------
#ifndef CPPLINQ_ASSERT
#   define CPPLINQ_ASSERT(expr) assert(expr)
#endif
#ifndef CPPLINQ_METHOD
#   define CPPLINQ_METHOD
#endif
#ifndef CPPLINQ_INLINEMETHOD
#   define CPPLINQ_INLINEMETHOD inline
#endif
#ifndef CPPLINQ_NOEXCEPT
#   define CPPLINQ_NOEXCEPT noexcept
#endif
// ----------------------------------------------------------------------------

// TODO:    Struggled with getting slice protection
//          and assignment operator detection for MINGW
//          Look into using delete standard functions
// #define CPPLINQ_DETECT_INVALID_METHODS

// ----------------------------------------------------------------------------
namespace cpplinq
{
	typedef std::size_t size_type;

	// -------------------------------------------------------------------------

	struct base_exception : std::exception
	{
		virtual const char* what() const CPPLINQ_NOEXCEPT
		{
			return "base_exception";
		}
	};

	struct programming_error_exception : base_exception
	{
		virtual const char* what() const CPPLINQ_NOEXCEPT
		{
			return "programming_error_exception";
		}
	};

	struct sequence_empty_exception : base_exception
	{
		virtual const char* what() const CPPLINQ_NOEXCEPT
		{
			return "sequence_empty_exception";
		}
	};

	struct out_of_range_exception : base_exception
	{
		virtual const char* what() const CPPLINQ_NOEXCEPT
		{
			return "out_of_range_exception";
		}
	};

	// -------------------------------------------------------------------------
	// Tedious implementation details of cpplinq
	// -------------------------------------------------------------------------

	namespace detail
	{
		size_type const invalid_size = static_cast<size_type>(-1);

		// -------------------------------------------------------------------------

		template<typename TValue>
		struct cleanup_type
		{
			typedef typename std::remove_const<typename std::remove_reference<TValue>::type>::type type;
		};

		template<typename TRangeBuilder, typename TRange>
		struct get_builtup_type
		{
			static TRangeBuilder get_builder();
			static TRange get_range();

			typedef decltype (get_builder().build(get_range())) type;
		};

		template<typename TPredicate, typename TValue>
		struct get_transformed_type
		{
			static TValue get_value();
			static TPredicate get_predicate();

			typedef decltype (get_predicate()(get_value())) raw_type;
			typedef typename cleanup_type<raw_type>::type type;
		};

		template<typename TArray>
		struct get_array_properties;

		template<typename TValue, int Size>
		struct get_array_properties<TValue[Size]>
		{
			enum
			{
				size = Size,
			};

			typedef typename cleanup_type<TValue>::type value_type;
			typedef value_type const* iterator_type;
		};

		template<typename TValue>
		struct opt
		{
			typedef TValue value_type;

			CPPLINQ_INLINEMETHOD opt() CPPLINQ_NOEXCEPT
				: is_initialized(false)
			{
			}

			CPPLINQ_INLINEMETHOD explicit opt(value_type&& value)
				: is_initialized(true)
			{
				new (&storage) value_type(std::move(value));
			}

			CPPLINQ_INLINEMETHOD explicit opt(value_type const& value)
				: is_initialized(true)
			{
				new (&storage) value_type(value);
			}

			CPPLINQ_INLINEMETHOD ~opt() CPPLINQ_NOEXCEPT
			{
				auto ptr = get_ptr();
				if (ptr)
				{
					ptr->~value_type();
				}
				is_initialized = false;
			}

			CPPLINQ_INLINEMETHOD opt(opt const& v)
				: is_initialized(v.is_initialized)
			{
				if (v.is_initialized)
				{
					copy(&storage, &v.storage);
				}
			}

			CPPLINQ_INLINEMETHOD opt(opt&& v) CPPLINQ_NOEXCEPT
				: is_initialized(v.is_initialized)
			{
				if (v.is_initialized)
				{
					move(&storage, &v.storage);
				}
				v.is_initialized = false;
			}

			CPPLINQ_METHOD void swap(opt& v)
			{
				if (is_initialized && v.is_initialized)
				{
					storage_type tmp;

					move(&tmp, &storage);
					move(&storage, &v.storage);
					move(&v.storage, &tmp);
				}
				else if (is_initialized)
				{
					move(&v.storage, &storage);
					v.is_initialized = true;
					is_initialized = false;
				}
				else if (v.is_initialized)
				{
					move(&storage, &v.storage);
					v.is_initialized = false;
					is_initialized = true;
				}
				else
				{
					// Do nothing
				}
			}

			CPPLINQ_INLINEMETHOD opt& operator=(opt const& v)
			{
				if (this == std::addressof(v))
				{
					return *this;
				}

				opt<value_type> o(v);

				swap(o);

				return *this;
			}

			CPPLINQ_INLINEMETHOD opt& operator=(opt&& v)
			{
				if (this == std::addressof(v))
				{
					return *this;
				}

				swap(v);

				return *this;
			}

			CPPLINQ_INLINEMETHOD opt& operator=(value_type v)
			{
				return *this = opt(std::move(v));
			}

			CPPLINQ_INLINEMETHOD void clear() CPPLINQ_NOEXCEPT
			{
				opt empty;
				swap(empty);
			}

			CPPLINQ_INLINEMETHOD value_type const* get_ptr() const CPPLINQ_NOEXCEPT
			{
				if (is_initialized)
				{
					return reinterpret_cast<value_type const*> (&storage);
				}
				else
				{
					return nullptr;
				}
			}

			CPPLINQ_INLINEMETHOD value_type* get_ptr() CPPLINQ_NOEXCEPT
			{
				if (is_initialized)
				{
					return reinterpret_cast<value_type*> (&storage);
				}
				else
				{
					return nullptr;
				}
			}

			CPPLINQ_INLINEMETHOD value_type const& get() const CPPLINQ_NOEXCEPT
			{
				CPPLINQ_ASSERT(is_initialized);

				return *get_ptr();
			}

			CPPLINQ_INLINEMETHOD value_type& get() CPPLINQ_NOEXCEPT
			{
				CPPLINQ_ASSERT(is_initialized);

				return *get_ptr();
			}

			CPPLINQ_INLINEMETHOD bool has_value() const CPPLINQ_NOEXCEPT
			{
				return is_initialized;
			}

			// TODO: To be replaced with explicit operator bool ()
			typedef bool (opt::* type_safe_bool_type) () const;

			CPPLINQ_INLINEMETHOD operator type_safe_bool_type() const CPPLINQ_NOEXCEPT
			{
				return is_initialized ? &opt::has_value : nullptr;
			}

			CPPLINQ_INLINEMETHOD value_type const& operator*() const CPPLINQ_NOEXCEPT
			{
				return get();
			}

			CPPLINQ_INLINEMETHOD value_type& operator*() CPPLINQ_NOEXCEPT
			{
				return get();
			}

			CPPLINQ_INLINEMETHOD value_type const* operator->() const CPPLINQ_NOEXCEPT
			{
				return get_ptr();
			}

			CPPLINQ_INLINEMETHOD value_type* operator->() CPPLINQ_NOEXCEPT
			{
				return get_ptr();
			}

		private:
			typedef typename std::aligned_storage<sizeof(value_type), std::alignment_of<value_type>::value>::type storage_type;

			storage_type storage;
			bool is_initialized;

			CPPLINQ_INLINEMETHOD static void move(storage_type* to, storage_type* from) CPPLINQ_NOEXCEPT
			{
				auto f = reinterpret_cast<value_type*> (from);
				new (to) value_type(std::move(*f));
				f->~value_type();
			}

			CPPLINQ_INLINEMETHOD static void copy(storage_type* to, storage_type const* from)
			{
				auto f = reinterpret_cast<value_type const*> (from);
				new (to) value_type(*f);
			}
		};

		// -------------------------------------------------------------------------
		// The generic interface
		// -------------------------------------------------------------------------
		// _range classes:
		//      inherit base_range
		//      COPYABLE
		//      MOVEABLE (movesemantics)
		//      typedef                 ...         this_type       ;
		//      typedef                 ...         value_type      ;
		//      typedef                 ...         return_type     ;   // value_type | value_type const &
		//      enum { returns_reference = 0|1 };
		//      return_type front () const
		//      bool next ()
		//      template<typename TRangeBuilder>
		//      typename get_builtup_type<TRangeBuilder, this_type>::type operator>>(TRangeBuilder range_builder) const
		// -------------------------------------------------------------------------
		// _builder classes:
		//      inherit base_builder
		//      COPYABLE
		//      MOVEABLE (movesemantics)
		//      typedef                 ...         this_type       ;
		//      template<typename TRange>
		//      TAggregated build (TRange range) const
		// -------------------------------------------------------------------------

		struct base_range
		{
#ifdef CPPLINQ_DETECT_INVALID_METHODS
		protected:
			// In order to prevent object slicing

			CPPLINQ_INLINEMETHOD base_range() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD base_range(base_range const&) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD base_range(base_range&&) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD ~base_range() CPPLINQ_NOEXCEPT
			{
			}

		private:
			CPPLINQ_INLINEMETHOD base_range& operator= (base_range const&);
			CPPLINQ_INLINEMETHOD base_range& operator= (base_range&&);
#endif
		};

		struct base_builder
		{
#ifdef CPPLINQ_DETECT_INVALID_METHODS
		protected:
			// In order to prevent object slicing

			CPPLINQ_INLINEMETHOD base_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD base_builder(base_builder const&) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD base_builder(base_builder&&) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD ~base_builder() CPPLINQ_NOEXCEPT
			{
			}

		private:
			CPPLINQ_INLINEMETHOD base_builder& operator= (base_builder const&);
			CPPLINQ_INLINEMETHOD base_builder& operator= (base_builder&&);
#endif
		};

		template<typename TValueIterator>
		struct from_range : base_range
		{
			static TValueIterator get_iterator();

			typedef from_range<TValueIterator> this_type;
			typedef TValueIterator iterator_type;

			typedef decltype (*get_iterator()) raw_value_type;
			typedef typename cleanup_type<raw_value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			iterator_type current;
			iterator_type upcoming;
			iterator_type end;


			CPPLINQ_INLINEMETHOD from_range(iterator_type begin, iterator_type end) CPPLINQ_NOEXCEPT
				: current(std::move(begin)),
				upcoming(current),
				end(std::move(end))
			{
			}

			CPPLINQ_INLINEMETHOD from_range(from_range const& v) CPPLINQ_NOEXCEPT
				: current(v.current),
				upcoming(v.upcoming),
				end(v.end)
			{
			}

			CPPLINQ_INLINEMETHOD from_range(from_range&& v) CPPLINQ_NOEXCEPT
				: current(std::move(v.current)),
				upcoming(std::move(v.upcoming)),
				end(std::move(v.end))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(current != upcoming);
				CPPLINQ_ASSERT(current != end);

				return *current;
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				if (upcoming == end)
				{
					return false;
				}

				current = upcoming;

				++upcoming;

				return true;
			}
		};

		// -------------------------------------------------------------------------

		template<typename TContainer>
		struct from_copy_range : base_range
		{
			typedef from_copy_range<TContainer> this_type;

			typedef TContainer container_type;
			typedef typename TContainer::const_iterator iterator_type;
			typedef typename TContainer::value_type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			container_type container;

			iterator_type current;
			iterator_type upcoming;
			iterator_type end;

			CPPLINQ_INLINEMETHOD from_copy_range(container_type&& container)
				: container(std::move(container)),
				current(container.begin()),
				upcoming(container.begin()),
				end(container.end())
			{
			}

			CPPLINQ_INLINEMETHOD from_copy_range(container_type const& container)
				: container(container),
				current(container.begin()),
				upcoming(container.begin()),
				end(container.end())
			{
			}

			CPPLINQ_INLINEMETHOD from_copy_range(from_copy_range const& v)
				: container(v.container),
				current(v.current),
				upcoming(v.upcoming),
				end(v.end)
			{
			}

			CPPLINQ_INLINEMETHOD from_copy_range(from_copy_range&& v) CPPLINQ_NOEXCEPT
				: container(std::move(v.container)),
				current(std::move(v.current)),
				upcoming(std::move(v.upcoming)),
				end(std::move(v.end))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(current != upcoming);
				CPPLINQ_ASSERT(current != end);

				return *current;
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				if (upcoming == end)
				{
					return false;
				}

				current = upcoming;

				++upcoming;

				return true;
			}
		};

		// -------------------------------------------------------------------------

		struct int_range : base_range
		{
			typedef int_range this_type;
			typedef int value_type;
			typedef int return_type;
			enum
			{
				returns_reference = 0,
			};

			int current;
			int end;

			static int get_current(int begin, int end)
			{
				return (begin < end ? begin : end) - 1; // -1 in order to start one-step before the first element
			}

			static int get_end(int begin, int end) // -1 in order to avoid an extra test in next
			{
				return (begin < end ? end : begin) - 1;
			}

			CPPLINQ_INLINEMETHOD int_range(int begin, int end) CPPLINQ_NOEXCEPT
				: current(get_current(begin, end)),
				end(get_end(begin, end))
			{
			}

			CPPLINQ_INLINEMETHOD int_range(int_range const& v) CPPLINQ_NOEXCEPT
				: current(v.current),
				end(v.end)
			{
			}

			CPPLINQ_INLINEMETHOD int_range(int_range && v) CPPLINQ_NOEXCEPT
				: current(std::move(v.current)),
				end(std::move(v.end))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return current;
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				if (current >= end)
				{
					return false;
				}

				++current;

				return true;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TValue>
		struct repeat_range : base_range
		{
			typedef repeat_range<TValue> this_type;
			typedef TValue value_type;
			typedef TValue return_type;
			enum
			{
				returns_reference = 0,
			};

			TValue value;
			size_type remaining;

			CPPLINQ_INLINEMETHOD repeat_range(value_type element, size_type count) CPPLINQ_NOEXCEPT
				: value(std::move(element)),
				remaining(count)
			{
			}

			CPPLINQ_INLINEMETHOD repeat_range(repeat_range const& v) CPPLINQ_NOEXCEPT
				: value(v.value),
				remaining(v.remaining)
			{
			}

			CPPLINQ_INLINEMETHOD repeat_range(repeat_range&& v) CPPLINQ_NOEXCEPT
				: value(std::move(v.value)),
				remaining(std::move(v.remaining))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return value;
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				if (remaining == 0U)
				{
					return false;
				}

				--remaining;

				return true;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TValue>
		struct empty_range : base_range
		{
			typedef empty_range<TValue> this_type;
			typedef TValue value_type;
			typedef TValue return_type;
			enum
			{
				returns_reference = 0,
			};

			CPPLINQ_INLINEMETHOD empty_range() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD empty_range(empty_range const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD empty_range(empty_range&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(false);

				throw programming_error_exception();
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				return false;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TValue>
		struct singleton_range : base_range
		{
			typedef singleton_range<TValue> this_type;
			typedef TValue value_type;
			typedef TValue const& return_type;

			enum
			{
				returns_reference = 1,
			};

			value_type value;
			bool done;

			CPPLINQ_INLINEMETHOD singleton_range(TValue const& value)
				: value(value),
				done(false)
			{
			}

			CPPLINQ_INLINEMETHOD singleton_range(TValue&& value) CPPLINQ_NOEXCEPT
				: value(std::move(value)),
				done(false)
			{
			}

			CPPLINQ_INLINEMETHOD singleton_range(singleton_range const& v) CPPLINQ_NOEXCEPT
				: value(v.value),
				done(v.done)
			{
			}

			CPPLINQ_INLINEMETHOD singleton_range(singleton_range&& v) CPPLINQ_NOEXCEPT
				: value(std::move(v.value)),
				done(std::move(v.done))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const CPPLINQ_NOEXCEPT
			{
				return value;
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				auto d = done;
				done = true;
				return !d;
			}
		};

		// -------------------------------------------------------------------------

		struct sorting_range : base_range
		{
#ifdef CPPLINQ_DETECT_INVALID_METHODS
		protected:
			// In order to prevent object slicing

			CPPLINQ_INLINEMETHOD sorting_range() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD sorting_range(sorting_range const&) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD sorting_range(sorting_range&&) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD ~sorting_range() CPPLINQ_NOEXCEPT
			{
			}
		private:
			CPPLINQ_INLINEMETHOD sorting_range& operator= (sorting_range const&);
			CPPLINQ_INLINEMETHOD sorting_range& operator= (sorting_range&&);
#endif
		};

		template<typename TRange, typename TPredicate>
		struct orderby_range : sorting_range
		{

			typedef orderby_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::return_type forwarding_return_type;
			typedef value_type const& return_type;
			enum
			{
				forward_returns_reference = TRange::returns_reference,
				returns_reference = 1,
			};

			range_type range;
			predicate_type predicate;
			bool sort_ascending;

			size_type current;
			std::vector<value_type> sorted_values;

			CPPLINQ_INLINEMETHOD orderby_range(range_type range, predicate_type predicate, bool sort_ascending) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate)),
				sort_ascending(sort_ascending),
				current(invalid_size)
			{
				static_assert(!std::is_convertible<range_type, sorting_range>::value, "orderby may not follow orderby or thenby");
			}

			CPPLINQ_INLINEMETHOD orderby_range(orderby_range const& v)
				: range(v.range),
				predicate(v.predicate),
				sort_ascending(v.sort_ascending),
				current(v.current),
				sorted_values(v.sorted_values)
			{
			}

			CPPLINQ_INLINEMETHOD orderby_range(orderby_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate)),
				sort_ascending(std::move(v.sort_ascending)),
				current(std::move(v.current)),
				sorted_values(std::move(v.sorted_values))
			{
			}

			CPPLINQ_INLINEMETHOD auto forwarding_front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto forwarding_next()
			{
				return range.next();
			}

			CPPLINQ_INLINEMETHOD auto compare_values(value_type const& l, value_type const& r) const
			{
				if (sort_ascending)
				{
					return predicate(l) < predicate(r);
				}
				else
				{
					return predicate(r) < predicate(l);
				}
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return sorted_values[current];
			}

			CPPLINQ_METHOD auto next()
			{
				if (current == invalid_size)
				{
					sorted_values.clear();

					while (range.next())
					{
						sorted_values.push_back(range.front());
					}

					if (sorted_values.size() == 0)
					{
						return false;
					}

					std::sort(sorted_values.begin(), sorted_values.end(), [this](value_type const& l, value_type const& r)
						{
							return this->compare_values(l, r);
						}
					);

					current = 0U;
					return true;
				}

				if (current < sorted_values.size())
				{
					++current;
				}

				return current < sorted_values.size();
			}
		};

		template<typename TPredicate>
		struct orderby_builder : base_builder
		{
			typedef orderby_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;
			bool sort_ascending;

			CPPLINQ_INLINEMETHOD explicit orderby_builder(predicate_type predicate, bool sort_ascending) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate)),
				sort_ascending(sort_ascending)
			{
			}

			CPPLINQ_INLINEMETHOD orderby_builder(orderby_builder const& v)
				: predicate(v.predicate),
				sort_ascending(v.sort_ascending)
			{
			}

			CPPLINQ_INLINEMETHOD orderby_builder(orderby_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate)),
				sort_ascending(std::move(v.sort_ascending))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return orderby_range<TRange, TPredicate>(std::move(range), predicate, sort_ascending);
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TPredicate>
		struct thenby_range : sorting_range
		{
			typedef thenby_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::forwarding_return_type forwarding_return_type;
			typedef value_type const& return_type;
			enum
			{
				forward_returns_reference = TRange::forward_returns_reference,
				returns_reference = 1,
			};

			range_type range;
			predicate_type predicate;
			bool sort_ascending;

			size_type current;
			std::vector<value_type> sorted_values;

			CPPLINQ_INLINEMETHOD thenby_range(range_type range, predicate_type predicate, bool sort_ascending) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate)),
				sort_ascending(sort_ascending),
				current(invalid_size)
			{
				static_assert(std::is_convertible<range_type, sorting_range>::value, "thenby may only follow orderby or thenby");
			}

			CPPLINQ_INLINEMETHOD thenby_range(thenby_range const& v)
				: range(v.range),
				predicate(v.predicate),
				sort_ascending(v.sort_ascending),
				current(v.current),
				sorted_values(v.sorted_values)
			{
			}

			CPPLINQ_INLINEMETHOD thenby_range(thenby_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate)),
				sort_ascending(std::move(v.sort_ascending)),
				current(std::move(v.current)),
				sorted_values(std::move(v.sorted_values))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto forwarding_front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto forwarding_next()
			{
				return range.next();
			}

			CPPLINQ_INLINEMETHOD auto compare_values(value_type const& l, value_type const& r) const
			{
				auto pless = range.compare_values(l, r);
				if (pless)
				{
					return true;
				}

				auto pgreater = range.compare_values(r, l);
				if (pgreater)
				{
					return false;
				}

				if (sort_ascending)
				{
					return predicate(l) < predicate(r);
				}
				else
				{
					return predicate(r) < predicate(l);
				}
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return sorted_values[current];
			}

			CPPLINQ_METHOD auto next()
			{
				if (current == invalid_size)
				{
					sorted_values.clear();

					while (range.forwarding_next())
					{
						sorted_values.push_back(range.forwarding_front());
					}

					if (sorted_values.size() == 0)
					{
						return false;
					}

					std::sort(sorted_values.begin(), sorted_values.end(), [this](value_type const& l, value_type const& r)
						{
							return this->compare_values(l, r);
						}
					);

					current = 0U;
					return true;
				}

				if (current < sorted_values.size())
				{
					++current;
				}

				return current < sorted_values.size();
			}
		};

		template<typename TPredicate>
		struct thenby_builder : base_builder
		{
			typedef thenby_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;
			bool sort_ascending;

			CPPLINQ_INLINEMETHOD explicit thenby_builder(predicate_type predicate, bool sort_ascending) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate)),
				sort_ascending(sort_ascending)
			{
			}

			CPPLINQ_INLINEMETHOD thenby_builder(thenby_builder const& v)
				: predicate(v.predicate),
				sort_ascending(v.sort_ascending)
			{
			}

			CPPLINQ_INLINEMETHOD thenby_builder(thenby_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate)),
				sort_ascending(std::move(v.sort_ascending))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return thenby_range<TRange, TPredicate>(std::move(range), predicate, sort_ascending);
			}

		};

		// -------------------------------------------------------------------------

		template<typename TRange>
		struct reverse_range : base_range
		{
			typedef reverse_range<TRange> this_type;
			typedef TRange range_type;

			typedef typename TRange::value_type value_type;
			typedef value_type const& return_type;

			typedef std::vector<value_type> stack_type;

			enum
			{
				returns_reference = 1,
			};


			range_type range;
			size_type capacity;
			std::vector<value_type> reversed;
			bool start;

			CPPLINQ_INLINEMETHOD reverse_range(range_type range, size_type capacity) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				capacity(capacity),
				start(true)
			{
			}

			CPPLINQ_INLINEMETHOD reverse_range(reverse_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				capacity(v.capacity),
				reversed(v.reversed),
				start(v.start)
			{
			}

			CPPLINQ_INLINEMETHOD reverse_range(reverse_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				capacity(std::move(v.capacity)),
				reversed(std::move(v.reversed)),
				start(std::move(v.start))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const CPPLINQ_NOEXCEPT
			{
				CPPLINQ_ASSERT(!start);
				CPPLINQ_ASSERT(!reversed.empty());

				return reversed[reversed.size() - 1];
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (start)
				{
					start = false;

					reversed.clear();
					reversed.reserve(capacity);

					while (range.next())
					{
						reversed.push_back(range.front());
					}

					return !reversed.empty();
				}

				if (reversed.empty())
				{
					return false;
				}

				reversed.pop_back();

				return !reversed.empty();
			}
		};

		struct reverse_builder : base_builder
		{
			typedef reverse_builder this_type;

			size_type capacity;

			CPPLINQ_INLINEMETHOD reverse_builder(size_type capacity) CPPLINQ_NOEXCEPT
				: capacity(capacity)
			{
			}

			CPPLINQ_INLINEMETHOD reverse_builder(reverse_builder const& v) CPPLINQ_NOEXCEPT
				: capacity(v.capacity)
			{
			}

			CPPLINQ_INLINEMETHOD reverse_builder(reverse_builder&& v) CPPLINQ_NOEXCEPT
				: capacity(std::move(v.capacity))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return reverse_range<TRange>(std::move(range), capacity);
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TPredicate>
		struct where_range : base_range
		{
			typedef where_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::return_type return_type;
			enum
			{
				returns_reference = TRange::returns_reference,
			};

			range_type range;
			predicate_type predicate;

			CPPLINQ_INLINEMETHOD where_range(range_type range, predicate_type predicate) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD where_range(where_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD where_range(where_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				while (range.next())
				{
					if (predicate(range.front()))
					{
						return true;
					}
				}

				return false;
			}
		};

		template<typename TPredicate>
		struct where_builder : base_builder
		{
			typedef where_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD explicit where_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD where_builder(where_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD where_builder(where_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return where_range<TRange, TPredicate>(std::move(range), predicate);
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange>
		struct take_range : base_range
		{
			typedef take_range<TRange> this_type;
			typedef TRange range_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::return_type return_type;
			enum
			{
				returns_reference = TRange::returns_reference,
			};

			range_type range;
			size_type count;
			size_type current;


			CPPLINQ_INLINEMETHOD take_range(range_type range, size_type count) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				count(std::move(count)),
				current(0)
			{
			}

			CPPLINQ_INLINEMETHOD take_range(take_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				count(v.count),
				current(v.current)
			{
			}

			CPPLINQ_INLINEMETHOD take_range(take_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				count(std::move(v.count)),
				current(std::move(v.current))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (current >= count)
				{
					return false;
				}

				++current;

				return range.next();
			}
		};

		struct take_builder : base_builder
		{
			typedef take_builder this_type;

			size_type count;

			CPPLINQ_INLINEMETHOD explicit take_builder(size_type count) CPPLINQ_NOEXCEPT
				: count(std::move(count))
			{
			}

			CPPLINQ_INLINEMETHOD take_builder(take_builder const& v) CPPLINQ_NOEXCEPT
				: count(v.count)
			{
			}

			CPPLINQ_INLINEMETHOD take_builder(take_builder&& v) CPPLINQ_NOEXCEPT
				: count(std::move(v.count))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return take_range<TRange>(std::move(range), count);
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TPredicate>
		struct take_while_range : base_range
		{
			typedef take_while_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::return_type return_type;
			enum
			{
				returns_reference = TRange::returns_reference,
			};

			range_type range;
			predicate_type predicate;
			bool done;


			CPPLINQ_INLINEMETHOD take_while_range(range_type range, predicate_type predicate) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate)),
				done(false)
			{
			}

			CPPLINQ_INLINEMETHOD take_while_range(take_while_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				predicate(v.predicate),
				done(v.done)
			{
			}

			CPPLINQ_INLINEMETHOD take_while_range(take_while_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate)),
				done(std::move(v.done))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (done)
				{
					return false;
				}

				if (!range.next())
				{
					done = true;
					return false;
				}

				if (!predicate(range.front()))
				{
					done = true;
					return false;
				}

				return true;
			}
		};

		template<typename TPredicate>
		struct take_while_builder : base_builder
		{
			typedef take_while_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD take_while_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD take_while_builder(take_while_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD take_while_builder(take_while_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return take_while_range<TRange, TPredicate>(std::move(range), predicate);
			}

		};

		// -------------------------------------------------------------------------

		template<typename TRange>
		struct skip_range : base_range
		{
			typedef skip_range<TRange> this_type;
			typedef TRange range_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::return_type return_type;
			enum
			{
				returns_reference = TRange::returns_reference,
			};

			range_type range;
			size_type count;
			size_type current;

			CPPLINQ_INLINEMETHOD skip_range(range_type range, size_type count) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				count(std::move(count)),
				current(0)
			{
			}

			CPPLINQ_INLINEMETHOD skip_range(skip_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				count(v.count),
				current(v.current)
			{
			}

			CPPLINQ_INLINEMETHOD skip_range(skip_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				count(std::move(v.count)),
				current(std::move(v.current))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (current == invalid_size)
				{
					return false;
				}

				while (current < count && range.next())
				{
					++current;
				}

				if (current < count)
				{
					current = invalid_size;
					return false;
				}

				return range.next();
			}
		};

		struct skip_builder : base_builder
		{
			typedef skip_builder this_type;

			size_type count;

			CPPLINQ_INLINEMETHOD explicit skip_builder(size_type count) CPPLINQ_NOEXCEPT
				: count(std::move(count))
			{
			}

			CPPLINQ_INLINEMETHOD skip_builder(skip_builder const& v) CPPLINQ_NOEXCEPT
				: count(v.count)
			{
			}

			CPPLINQ_INLINEMETHOD skip_builder(skip_builder&& v) CPPLINQ_NOEXCEPT
				: count(std::move(v.count))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return skip_range<TRange>(std::move(range), count);
			}

		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TPredicate>
		struct skip_while_range : base_range
		{
			typedef skip_while_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			typedef typename TRange::value_type value_type;
			typedef typename TRange::return_type return_type;
			enum
			{
				returns_reference = TRange::returns_reference,
			};

			range_type range;
			predicate_type predicate;
			bool skipping;

			CPPLINQ_INLINEMETHOD skip_while_range(range_type range, predicate_type predicate) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate)),
				skipping(true)
			{
			}

			CPPLINQ_INLINEMETHOD skip_while_range(skip_while_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				predicate(v.predicate),
				skipping(v.skipping)
			{
			}

			CPPLINQ_INLINEMETHOD skip_while_range(skip_while_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate)),
				skipping(std::move(v.skipping))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (!skipping)
				{
					return range.next();
				}

				while (range.next())
				{
					if (!predicate(range.front()))
					{
						skipping = false;
						return true;
					}
				}

				return false;
			}
		};

		template <typename TPredicate>
		struct skip_while_builder : base_builder
		{
			typedef skip_while_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD skip_while_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD skip_while_builder(skip_while_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD skip_while_builder(skip_while_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return skip_while_range<TRange, TPredicate>(std::move(range), predicate);
			}

		};

		// -------------------------------------------------------------------------

		template<typename TRange>
		struct ref_range : base_range
		{
			typedef std::reference_wrapper<typename TRange::value_type const> value_type;
			typedef value_type return_type;
			enum
			{
				returns_reference = 0,
			};

			typedef ref_range<TRange> this_type;
			typedef TRange range_type;

			range_type range;

			CPPLINQ_INLINEMETHOD ref_range(range_type range) CPPLINQ_NOEXCEPT
				: range(std::move(range))
			{
				static_assert(TRange::returns_reference, "ref may only follow a range that returns references");
			}

			CPPLINQ_INLINEMETHOD ref_range(ref_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range)
			{
			}

			CPPLINQ_INLINEMETHOD ref_range(ref_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return value_type(range.front());
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				return range.next();
			}
		};

		struct ref_builder : base_builder
		{
			typedef ref_builder this_type;

			CPPLINQ_INLINEMETHOD ref_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD ref_builder(ref_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD ref_builder(ref_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return ref_range<TRange>(std::move(range));
			}
		};

		// -------------------------------------------------------------------------

		template <typename F, typename... T1s, typename T2>
		static auto choose(F && f, std::tuple<T1s...>&&, T2&&) -> decltype (f(std::declval<T1s>()...));
		template <typename F, typename T1, typename... T2s>
		static auto choose(F && f, T1&&, std::tuple<T2s...>&&) -> decltype (f(std::declval<T2s>()...));

		template<typename TRange, typename TPredicate>
		struct select_range : base_range
		{
			static typename TRange::value_type get_source();
			static TPredicate get_predicate();


			typedef decltype(choose(get_predicate(), std::make_tuple(get_source()), std::make_tuple(get_source(), 0))) raw_value_type;
			typedef typename cleanup_type<raw_value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			typedef select_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			range_type range;
			predicate_type predicate;

			opt<value_type> cache_value;
			size_type current_index;

			CPPLINQ_INLINEMETHOD select_range(range_type range, predicate_type predicate) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate)),
				current_index(0)
			{
			}

			CPPLINQ_INLINEMETHOD select_range(select_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				predicate(v.predicate),
				cache_value(v.cache_value),
				current_index(v.current_index)
			{
			}

			CPPLINQ_INLINEMETHOD select_range(select_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate)),
				cache_value(std::move(v.cache_value)),
				current_index(std::move(v.current_index))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(cache_value);

				return *cache_value;
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (range.next())
				{
					cache_value = evaluate_predicate(predicate);
					return true;
				}

				cache_value.clear();

				return false;
			}

			template<typename TPredicate_>
			CPPLINQ_INLINEMETHOD auto evaluate_predicate(TPredicate_&& pred) -> decltype (pred(range.front()))
			{
				return pred(range.front());
			}

			template<typename TPredicate_, int = 0 /* VisualStudio Workaround (http://stackoverflow.com/a/28204207/3647361) */>
			CPPLINQ_INLINEMETHOD auto evaluate_predicate(TPredicate_ && pred) -> decltype (pred(range.front(), 0))
			{
				return pred(range.front(), current_index++);
			}
		};

		template<typename TPredicate>
		struct select_builder : base_builder
		{
			typedef select_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD explicit select_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD select_builder(select_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD select_builder(select_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return select_range<TRange, TPredicate>(std::move(range), predicate);
			}
		};

		// -------------------------------------------------------------------------

		// Some trickery in order to force the code to compile on VS2012
		template<typename TRange, typename TPredicate>
		struct select_many_range_helper
		{
			static typename TRange::value_type get_source();
			static TPredicate get_predicate();


			typedef decltype(choose(get_predicate(), std::make_tuple(get_source()), std::make_tuple(get_source(), 0))) raw_inner_range_type;
			typedef typename cleanup_type<raw_inner_range_type>::type inner_range_type;

			static inner_range_type get_inner_range();

			typedef decltype (get_inner_range().front()) raw_value_type;
			typedef typename cleanup_type<raw_value_type>::type value_type;

		};

		template<typename TRange, typename TPredicate>
		struct select_many_range : base_range
		{
			typedef select_many_range_helper<TRange, TPredicate> helper_type;

			typedef typename helper_type::inner_range_type inner_range_type;
			typedef typename helper_type::value_type value_type;
			typedef value_type return_type;
			enum
			{
				returns_reference = 0,
			};

			typedef select_many_range<TRange, TPredicate> this_type;
			typedef TRange range_type;
			typedef TPredicate predicate_type;

			range_type range;
			predicate_type predicate;

			opt<inner_range_type> inner_range;
			size_type current_index;


			CPPLINQ_INLINEMETHOD select_many_range(range_type range, predicate_type predicate) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				predicate(std::move(predicate)),
				current_index(0)
			{
			}

			CPPLINQ_INLINEMETHOD select_many_range(select_many_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				predicate(v.predicate),
				inner_range(v.inner_range),
				current_index(v.current_index)
			{
			}

			CPPLINQ_INLINEMETHOD select_many_range(select_many_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				predicate(std::move(v.predicate)),
				inner_range(std::move(v.inner_range)),
				current_index(std::move(v.current_index))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(inner_range);

				return inner_range->front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (inner_range && inner_range->next())
				{
					return true;
				}

				if (range.next())
				{
					inner_range = evaluate_predicate(predicate);
					return inner_range && inner_range->next();
				}

				inner_range.clear();

				return false;
			}

			template<typename TPredicate_>
			CPPLINQ_INLINEMETHOD auto evaluate_predicate(TPredicate_& pred) -> decltype (pred(range.front()))
			{
				return pred(range.front());
			}

			template<typename TPredicate_, int = 0 /* VisualStudio Workaround (http://stackoverflow.com/a/28204207/3647361) */>
			CPPLINQ_INLINEMETHOD auto evaluate_predicate(TPredicate_ & pred) -> decltype (pred(range.front(), 0))
			{
				return pred(range.front(), current_index++);
			}
		};

		template<typename TPredicate>
		struct select_many_builder : base_builder
		{
			typedef select_many_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD explicit select_many_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD select_many_builder(select_many_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD select_many_builder(select_many_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return select_many_range<TRange, TPredicate>(std::move(range), predicate);
			}

		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange, typename TKeySelector, typename TOtherKeySelector, typename TCombiner>
		struct join_range : base_range
		{
			static typename TRange::value_type get_source();
			static typename TOtherRange::value_type get_other_source();
			static TKeySelector get_key_selector();
			static TOtherKeySelector get_other_key_selector();
			static TCombiner get_combiner();

			typedef decltype(get_key_selector()(get_source())) raw_key_type;
			typedef typename cleanup_type<raw_key_type>::type key_type;

			typedef decltype(get_other_key_selector() (get_other_source())) raw_other_key_type;
			typedef typename cleanup_type<raw_other_key_type>::type other_key_type;

			typedef decltype(get_combiner()(get_source(), get_other_source())) raw_value_type;
			typedef typename cleanup_type<raw_value_type>::type value_type;
			typedef value_type return_type;
			enum
			{
				returns_reference = 0,
			};

			typedef join_range<TRange, TOtherRange, TKeySelector, TOtherKeySelector, TCombiner> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;
			typedef TKeySelector key_selector_type;
			typedef TOtherKeySelector other_key_selector_type;
			typedef TCombiner combiner_type;
			typedef std::multimap<other_key_type, typename TOtherRange::value_type> map_type;
			typedef typename map_type::const_iterator map_iterator_type;

			range_type range;
			other_range_type other_range;
			key_selector_type key_selector;
			other_key_selector_type other_key_selector;
			combiner_type combiner;

			bool start;
			map_type map;
			map_iterator_type current;

			CPPLINQ_INLINEMETHOD join_range(range_type range, other_range_type other_range, key_selector_type key_selector, other_key_selector_type other_key_selector, combiner_type combiner) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range)),
				key_selector(std::move(key_selector)),
				other_key_selector(std::move(other_key_selector)),
				combiner(std::move(combiner)),
				start(true)
			{
			}

			CPPLINQ_INLINEMETHOD join_range(join_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range),
				key_selector(v.key_selector),
				other_key_selector(v.other_key_selector),
				combiner(v.combiner),
				start(v.start),
				map(v.map),
				current(v.current)
			{
			}

			CPPLINQ_INLINEMETHOD join_range(join_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range)),
				key_selector(std::move(v.key_selector)),
				other_key_selector(std::move(v.other_key_selector)),
				combiner(std::move(v.combiner)),
				start(std::move(v.start)),
				map(std::move(v.map)),
				current(std::move(v.current))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(current != map.end());

				return combiner(range.front(), current->second);
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (start)
				{
					start = false;
					while (other_range.next())
					{
						auto other_value = other_range.front();
						auto other_key = other_key_selector(other_value);
						map.insert(typename map_type::value_type(std::move(other_key), std::move(other_value)));
					}

					current = map.end();
					if (map.size() == 0U)
					{
						return false;
					}
				}

				if (current != map.end())
				{
					auto previous = current;
					++current;
					if (current != map.end() && !(previous->first < current->first))
					{
						return true;
					}
				}

				while (range.next())
				{
					auto value = range.front();
					auto key = key_selector(value);

					current = map.find(key);
					if (current != map.end())
					{
						return true;
					}
				}

				return false;
			}
		};

		template<typename TOtherRange, typename TKeySelector, typename TOtherKeySelector, typename TCombiner>
		struct join_builder : base_builder
		{
			typedef join_builder<TOtherRange, TKeySelector, TOtherKeySelector, TCombiner> this_type;

			typedef TOtherRange other_range_type;
			typedef TKeySelector key_selector_type;
			typedef TOtherKeySelector other_key_selector_type;
			typedef TCombiner combiner_type;

			other_range_type other_range;
			key_selector_type key_selector;
			other_key_selector_type other_key_selector;
			combiner_type combiner;

			CPPLINQ_INLINEMETHOD join_builder(other_range_type other_range, key_selector_type key_selector, other_key_selector_type other_key_selector, combiner_type combiner) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range)),
				key_selector(std::move(key_selector)),
				other_key_selector(std::move(other_key_selector)),
				combiner(std::move(combiner))
			{
			}

			CPPLINQ_INLINEMETHOD join_builder(join_builder const& v)
				: other_range(v.other_range),
				key_selector(v.key_selector),
				other_key_selector(v.other_key_selector),
				combiner(v.combiner)
			{
			}

			CPPLINQ_INLINEMETHOD join_builder(join_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range)),
				key_selector(std::move(v.key_selector)),
				other_key_selector(std::move(v.other_key_selector)),
				combiner(std::move(v.combiner))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return join_range<TRange, TOtherRange, TKeySelector, TOtherKeySelector, TCombiner>(std::move(range), other_range, key_selector, other_key_selector, combiner);
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange>
		struct distinct_range : base_range
		{
			typedef distinct_range<TRange> this_type;
			typedef TRange range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			typedef std::unordered_set<value_type> set_type;
			typedef typename set_type::const_iterator set_iterator_type;

			range_type range;
			set_type set;
			set_iterator_type current;

			CPPLINQ_INLINEMETHOD distinct_range(range_type range) CPPLINQ_NOEXCEPT
				: range(std::move(range))
			{
			}

			CPPLINQ_INLINEMETHOD distinct_range(distinct_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				set(v.set),
				current(v.current)
			{
			}

			CPPLINQ_INLINEMETHOD distinct_range(distinct_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				set(std::move(v.set)),
				current(std::move(v.current))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return *current;
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				while (range.next())
				{
					auto result = set.insert(range.front());
					if (result.second)
					{
						current = result.first;
						return true;
					}
				}

				return false;
			}
		};

		struct distinct_builder : base_builder
		{
			typedef distinct_builder this_type;

			CPPLINQ_INLINEMETHOD distinct_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD distinct_builder(distinct_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD distinct_builder(distinct_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return distinct_range<TRange>(std::move(range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TSelector>
		struct distinct_key_selector_range : base_range
		{
			typedef distinct_key_selector_range<TRange, TSelector> this_type;
			typedef TRange range_type;
			typedef TSelector key_selector_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			typedef typename get_transformed_type<key_selector_type, typename TRange::value_type>::type key_type;
			typedef std::set<key_type> set_type;

			range_type range;
			key_selector_type key_selector;
			set_type set;

			CPPLINQ_INLINEMETHOD distinct_key_selector_range(range_type range, key_selector_type key_selector) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				key_selector(std::move(key_selector))
			{
			}

			CPPLINQ_INLINEMETHOD distinct_key_selector_range(distinct_key_selector_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				key_selector(v.key_selector),
				set(v.set)
			{
			}

			CPPLINQ_INLINEMETHOD distinct_key_selector_range(distinct_key_selector_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				key_selector(std::move(v.key_selector)),
				set(std::move(v.set))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return range.front();
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				while (range.next())
				{
					auto result = set.insert(key_selector(range.front()));
					if (result.second)
					{
						return true;
					}
				}

				return false;
			}
		};

		template <typename TSelector>
		struct distinct_key_selector_builder : base_builder
		{
			typedef distinct_key_selector_builder<TSelector> this_type;
			typedef TSelector key_selector_type;

			key_selector_type key_selector;

			CPPLINQ_INLINEMETHOD distinct_key_selector_builder(key_selector_type key_selector) CPPLINQ_NOEXCEPT
				: key_selector(std::move(key_selector))
			{
			}

			CPPLINQ_INLINEMETHOD distinct_key_selector_builder(distinct_key_selector_builder const& v) CPPLINQ_NOEXCEPT
				: key_selector(v.key_selector)
			{
			}

			CPPLINQ_INLINEMETHOD distinct_key_selector_builder(distinct_key_selector_builder&& v) CPPLINQ_NOEXCEPT
				: key_selector(std::move(v.key_selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return distinct_key_selector_range<TRange, TSelector>(std::move(range), key_selector);
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange>
		struct union_range : base_range
		{
			typedef union_range<TRange, TOtherRange> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			typedef std::set<value_type> set_type;
			typedef typename set_type::const_iterator set_iterator_type;


			range_type range;
			other_range_type other_range;
			set_type set;
			set_iterator_type current;

			CPPLINQ_INLINEMETHOD union_range(range_type range, other_range_type other_range) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD union_range(union_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range),
				set(v.set),
				current(v.current)
			{
			}

			CPPLINQ_INLINEMETHOD union_range(union_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range)),
				set(std::move(v.set)),
				current(std::move(v.current))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return *current;
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				while (range.next())
				{
					auto result = set.insert(range.front());
					if (result.second)
					{
						current = result.first;
						return true;
					}
				}

				while (other_range.next())
				{
					auto result = set.insert(other_range.front());
					if (result.second)
					{
						current = result.first;
						return true;
					}
				}

				return false;
			}
		};

		template <typename TOtherRange>
		struct union_builder : base_builder
		{
			typedef union_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD union_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD union_builder(union_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD union_builder(union_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return union_range<TRange, TOtherRange>(std::move(range), std::move(other_range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange>
		struct intersect_range : base_range
		{
			typedef intersect_range<TRange, TOtherRange> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			typedef std::set<value_type> set_type;
			typedef typename set_type::const_iterator set_iterator_type;


			range_type range;
			other_range_type other_range;
			set_type set;
			set_iterator_type current;
			bool start;

			CPPLINQ_INLINEMETHOD intersect_range(range_type range, other_range_type other_range) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range)),
				start(true)
			{
			}

			CPPLINQ_INLINEMETHOD intersect_range(intersect_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range),
				set(v.set),
				current(v.current),
				start(v.start)
			{
			}

			CPPLINQ_INLINEMETHOD intersect_range(intersect_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range)),
				set(std::move(v.set)),
				current(std::move(v.current)),
				start(std::move(v.start))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(!start);
				return *current;
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (start)
				{
					start = false;

					while (other_range.next())
					{
						set.insert(other_range.front());
					}

					while (range.next())
					{
						current = set.find(range.front());
						if (current != set.end())
						{
							return true;
						}
					}

					set.clear();

					return false;
				}

				if (set.empty())
				{
					return false;
				}

				set.erase(current);

				while (range.next())
				{
					current = set.find(range.front());
					if (current != set.end())
					{
						return true;
					}
				}

				return false;
			}
		};

		template <typename TOtherRange>
		struct intersect_builder : base_builder
		{
			typedef intersect_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD intersect_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD intersect_builder(intersect_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD intersect_builder(intersect_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return intersect_range<TRange, TOtherRange>(std::move(range), std::move(other_range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange>
		struct except_range : base_range
		{
			typedef except_range<TRange, TOtherRange> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef value_type const& return_type;
			enum
			{
				returns_reference = 1,
			};

			typedef std::unordered_set<value_type> set_type;
			typedef typename set_type::const_iterator set_iterator_type;

			range_type range;
			other_range_type other_range;
			set_type set;
			set_iterator_type current;
			bool start;

			CPPLINQ_INLINEMETHOD except_range(range_type range, other_range_type other_range) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range)),
				start(true)
			{
			}

			CPPLINQ_INLINEMETHOD except_range(except_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range),
				set(v.set),
				current(v.current),
				start(v.start)
			{
			}

			CPPLINQ_INLINEMETHOD except_range(except_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range)),
				set(std::move(v.set)),
				current(std::move(v.current)),
				start(std::move(v.start))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return *current;
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (start)
				{
					start = false;
					while (other_range.next())
					{
						set.insert(other_range.front());
					}
				}

				while (range.next())
				{
					auto result = set.insert(range.front());
					if (result.second)
					{
						current = result.first;
						return true;
					}
				}

				return false;
			}
		};

		template <typename TOtherRange>
		struct except_builder : base_builder
		{
			typedef union_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD except_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD except_builder(except_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD except_builder(except_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return except_range<TRange, TOtherRange>(std::move(range), std::move(other_range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange>
		struct concat_range : base_range
		{
			typedef concat_range<TRange, TOtherRange> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef typename cleanup_type<typename TOtherRange::value_type>::type other_value_type;
			typedef value_type return_type;

			enum
			{
				returns_reference = 0,
			};

			enum state
			{
				state_initial,
				state_iterating_range,
				state_iterating_other_range,
				state_end,
			};

			range_type range;
			other_range_type other_range;
			state state;

			CPPLINQ_INLINEMETHOD concat_range(range_type range, other_range_type other_range) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range)),
				state(state_initial)
			{
			}

			CPPLINQ_INLINEMETHOD concat_range(concat_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range),
				state(v.state)
			{
			}

			CPPLINQ_INLINEMETHOD concat_range(concat_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range)),
				state(std::move(v.state))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				switch (state)
				{
				case state_initial:
				case state_end:
				default:
					CPPLINQ_ASSERT(false); // Intentionally falls through
				case state_iterating_range:
					return range.front();
				case state_iterating_other_range:
					return other_range.front();
				};
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				switch (state)
				{
				case state_initial:
					if (range.next())
					{
						state = state_iterating_range;
						return true;
					}

					if (other_range.next())
					{
						state = state_iterating_other_range;
						return true;
					}

					state = state_end;
					return false;
				case state_iterating_range:
					if (range.next())
					{
						return true;
					}

					if (other_range.next())
					{
						state = state_iterating_other_range;
						return true;
					}

					state = state_end;
					return false;
				case state_iterating_other_range:
					if (other_range.next())
					{
						return true;
					}

					state = state_end;
					return false;
				case state_end:
				default:
					return false;
				}
			}
		};

		template <typename TOtherRange>
		struct concat_builder : base_builder
		{
			typedef concat_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD concat_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD concat_builder(concat_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD concat_builder(concat_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return concat_range<TRange, TOtherRange>(std::move(range), std::move(other_range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange>
		struct start_with_range : base_range
		{
			typedef start_with_range<TRange, TOtherRange> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type value_type;
			typedef typename cleanup_type<typename TOtherRange::value_type>::type other_value_type;
			typedef value_type return_type;

			enum
			{
				returns_reference = 0,
			};

			enum state
			{
				state_initial,
				state_iterating_other_range,
				state_iterating_range,
				state_end,
			};

			range_type range;
			other_range_type other_range;
			state state;

			CPPLINQ_INLINEMETHOD start_with_range(range_type range, other_range_type other_range) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range)),
				state(state_initial)
			{
			}

			CPPLINQ_INLINEMETHOD start_with_range(start_with_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range),
				state(v.state)
			{
			}

			CPPLINQ_INLINEMETHOD start_with_range(start_with_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range)),
				state(std::move(v.state))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				switch (state)
				{
				case state_initial:
				case state_end:
				default:
					CPPLINQ_ASSERT(false); // Intentionally falls through
				case state_iterating_range:
					return range.front();
				case state_iterating_other_range:
					return other_range.front();
				};
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				switch (state)
				{
				case state_initial:
					if (other_range.next())
					{
						state = state_iterating_other_range;
						return true;
					}

					if (range.next())
					{
						state = state_iterating_range;
						return true;
					}

					state = state_end;
					return false;
				case state_iterating_other_range:
					if (other_range.next())
					{
						return true;
					}

					if (range.next())
					{
						state = state_iterating_range;
						return true;
					}

					state = state_end;
					return false;
				case state_iterating_range:
					if (range.next())
					{
						return true;
					}

					state = state_end;
					return false;
				case state_end:
				default:
					return false;
				}
			}
		};

		template <typename TOtherRange>
		struct start_with_builder : base_builder
		{
			typedef start_with_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD start_with_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD start_with_builder(start_with_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD start_with_builder(start_with_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return start_with_range<TRange, TOtherRange>(std::move(range), std::move(other_range));
			}
		};

		// -------------------------------------------------------------------------

		namespace experimental
		{
			// -------------------------------------------------------------------------

			// TODO: Verify that container range aggregator has the right semantics

			template<typename TRange>
			struct container_iterator
			{
				typedef std::forward_iterator_tag iterator_category;
				typedef typename TRange::value_type value_type;
				typedef typename TRange::return_type return_type;
				enum
				{
					returns_reference = TRange::returns_reference,
				};

				typedef std::ptrdiff_t difference_type;
				typedef value_type* pointer;
				typedef value_type& reference;

				typedef container_iterator<TRange> this_type;
				typedef TRange range_type;

				bool has_value;
				opt<range_type> range;

				CPPLINQ_INLINEMETHOD container_iterator() CPPLINQ_NOEXCEPT
					: has_value(false)
				{
				}

				CPPLINQ_INLINEMETHOD container_iterator(range_type r) CPPLINQ_NOEXCEPT
					: range(std::move(r))
				{
					has_value = range && range->next();
				}

				CPPLINQ_INLINEMETHOD container_iterator(container_iterator const& v) CPPLINQ_NOEXCEPT
					: has_value(v.has_value),
					range(v.range)
				{
				}

				CPPLINQ_INLINEMETHOD container_iterator(container_iterator&& v) CPPLINQ_NOEXCEPT
					: has_value(std::move(v.has_value)),
					range(std::move(v.range))
				{
				}

				CPPLINQ_INLINEMETHOD auto operator*() const
				{
					CPPLINQ_ASSERT(has_value);
					CPPLINQ_ASSERT(range);

					return range->front();
				}

				CPPLINQ_INLINEMETHOD auto operator->() const
				{
					static_assert(returns_reference, "operator-> requires a range that returns a reference, typically select causes ranges to return values not references");

					return &range->front();
				}

				CPPLINQ_INLINEMETHOD auto operator++()
				{
					if (has_value && range)
					{
						has_value = range->next();
					}

					return *this;
				}

				CPPLINQ_INLINEMETHOD auto operator==(this_type const& v) const CPPLINQ_NOEXCEPT
				{
					if (!has_value && !v.has_value)
					{
						return true;
					}
					else if (has_value && v.has_value && range.get_ptr() == v.range.get_ptr())
					{
						return true;
					}
					else
					{
						return false;
					}
				}

				CPPLINQ_INLINEMETHOD auto operator!=(this_type const& v) const CPPLINQ_NOEXCEPT
				{
					return !(*this == v);
				}
			};

			template<typename TRange>
			struct container
			{
				typedef container<TRange> this_type;
				typedef TRange range_type;
				typedef typename TRange::value_type value_type;
				typedef typename TRange::return_type return_type;
				enum
				{
					returns_reference = TRange::returns_reference,
				};

				range_type range;

				CPPLINQ_INLINEMETHOD explicit container(TRange range)
					: range(std::move(range))
				{
				}

				CPPLINQ_INLINEMETHOD container(container const& v) CPPLINQ_NOEXCEPT
					: range(v.range)
				{
				}

				CPPLINQ_INLINEMETHOD container(container&& v) CPPLINQ_NOEXCEPT
					: range(std::move(v.range))
				{
				}

				CPPLINQ_INLINEMETHOD auto begin() CPPLINQ_NOEXCEPT
				{
					return container_iterator<TRange>(range);
				}

				CPPLINQ_INLINEMETHOD auto end() CPPLINQ_NOEXCEPT
				{
					return container_iterator<TRange>();
				}
			};

			struct container_builder : base_builder
			{
				typedef container_builder this_type;

				CPPLINQ_INLINEMETHOD container_builder() CPPLINQ_NOEXCEPT
				{
				}

				CPPLINQ_INLINEMETHOD container_builder(container_builder const& v) CPPLINQ_NOEXCEPT
				{
				}

				CPPLINQ_INLINEMETHOD container_builder(container_builder&& v) CPPLINQ_NOEXCEPT
				{
				}

				template<typename TRange>
				CPPLINQ_METHOD auto build(TRange range) const
				{
					return container<TRange>(std::move(range));
				}
			};
		}

		// -------------------------------------------------------------------------

		struct to_vector_builder : base_builder
		{
			typedef to_vector_builder this_type;

			size_type capacity;

			CPPLINQ_INLINEMETHOD explicit to_vector_builder(size_type capacity = 16U) CPPLINQ_NOEXCEPT
				: capacity(capacity)
			{
			}

			CPPLINQ_INLINEMETHOD to_vector_builder(to_vector_builder const& v) CPPLINQ_NOEXCEPT
				: capacity(v.capacity)
			{
			}

			CPPLINQ_INLINEMETHOD to_vector_builder(to_vector_builder&& v) CPPLINQ_NOEXCEPT
				: capacity(std::move(v.capacity))
			{
			}

			template<typename TRange>
			CPPLINQ_METHOD auto build(TRange range) const
			{
				std::vector<typename TRange::value_type> result;
				result.reserve(capacity);

				while (range.next())
				{
					result.push_back(range.front());
				}

				return result;
			}
		};

		struct to_list_builder : base_builder
		{
			typedef to_list_builder this_type;

			CPPLINQ_INLINEMETHOD explicit to_list_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD to_list_builder(to_list_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD to_list_builder(to_list_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_METHOD auto build(TRange range) const
			{
				std::list<typename TRange::value_type> result;

				while (range.next())
				{
					result.push_back(range.front());
				}

				return result;
			}
		};

		// -------------------------------------------------------------------------

		template<typename TKeySelector>
		struct to_map_builder : base_builder
		{
			static TKeySelector get_key_selector();

			typedef to_map_builder<TKeySelector> this_type;
			typedef TKeySelector key_selector_type;

			key_selector_type key_selector;

			CPPLINQ_INLINEMETHOD explicit to_map_builder(key_selector_type key_selector) CPPLINQ_NOEXCEPT
				: key_selector(std::move(key_selector))
			{
			}

			CPPLINQ_INLINEMETHOD to_map_builder(to_map_builder const& v)
				: key_selector(v.key_selector)
			{
			}

			CPPLINQ_INLINEMETHOD to_map_builder(to_map_builder&& v) CPPLINQ_NOEXCEPT
				: key_selector(std::move(v.key_selector))
			{
			}

			template<typename TRange>
			CPPLINQ_METHOD auto build(TRange range) const
			{
				typedef std::map<
					typename get_transformed_type<key_selector_type, typename TRange::value_type>::type,
					typename TRange::value_type
				> result_type;

				result_type result;

				while (range.next())
				{
					auto v = range.front();
					auto k = key_selector(v);

					result.insert(typename result_type::value_type(std::move(k), std::move(v)));
				}

				return result;
			}
		};

		// -------------------------------------------------------------------------

		struct to_deque_builder : base_builder
		{
			typedef to_deque_builder this_type;

			CPPLINQ_INLINEMETHOD explicit to_deque_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD to_deque_builder(to_deque_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD to_deque_builder(to_deque_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_METHOD auto build(TRange range) const
			{
				std::deque<typename TRange::value_type> result;

				while (range.next())
				{
					result.push_back(range.front());
				}

				return result;
			}

		};

		// -------------------------------------------------------------------------

		template<typename TKey, typename TValue>
		struct lookup
		{
			typedef TKey key_type;
			typedef TValue value_type;

			typedef std::vector<std::pair<key_type, size_type>> keys_type;
			typedef std::vector<value_type> values_type;

			typedef typename keys_type::const_iterator keys_iterator_type;
			typedef typename values_type::const_iterator values_iterator_type;

			CPPLINQ_METHOD void init(keys_type& k, values_type& v)
			{
				if (v.size() == 0)
				{
					return;
				}

				std::sort(
					k.begin()
					, k.end()
					, [](typename keys_type::value_type const& l, typename keys_type::value_type const& r)
					{
						return l.first < r.first;
					}
				);

				keys.reserve(k.size());
				values.reserve(v.size());

				auto iter = k.begin();
				auto end = k.end();

				auto index = 0U;

				if (iter != end)
				{
					values.push_back(std::move(v[iter->second]));
					keys.push_back(typename keys_type::value_type(iter->first, index));
				}

				auto previous = iter;
				++iter;
				++index;

				while (iter != end)
				{
					values.push_back(v[iter->second]);

					if (previous->first < iter->first)
					{
						keys.push_back(typename keys_type::value_type(iter->first, index));
					}

					previous = iter;
					++iter;
					++index;
				}
			}

			template<typename TRange, typename TKeySelector>
			CPPLINQ_METHOD lookup(size_type capacity, TRange range, TKeySelector key_selector)
			{
				keys_type k;
				values_type v;
				k.reserve(capacity);
				v.reserve(capacity);

				auto index = 0U;
				while (range.next())
				{
					auto value = range.front();
					auto key = key_selector(value);
					v.push_back(std::move(value));
					k.push_back(typename keys_type::value_type(std::move(key), index));
					++index;
				}

				init(k, v);
			}

			template<typename TRange, typename TKeySelector, typename TValueSelector>
			CPPLINQ_METHOD lookup(size_type capacity, TRange range, TKeySelector key_selector, TValueSelector value_selector)
			{
				keys_type k;
				values_type v;
				k.reserve(capacity);
				v.reserve(capacity);

				auto index = 0U;
				while (range.next())
				{
					auto value = range.front();
					auto key = key_selector(value);
					v.push_back(value_selector(value));
					k.push_back(typename keys_type::value_type(std::move(key), index));
					++index;
				}

				init(k, v);
			}

			CPPLINQ_INLINEMETHOD lookup(lookup const& v)
				: values(v.values),
				keys(v.keys)
			{
			}

			CPPLINQ_INLINEMETHOD lookup(lookup&& v) CPPLINQ_NOEXCEPT
				: values(std::move(v.values)),
				keys(std::move(v.keys))
			{
			}

			CPPLINQ_INLINEMETHOD void swap(lookup& v) CPPLINQ_NOEXCEPT
			{
				values.swap(v.values);
				keys.swap(v.keys);
			}

			CPPLINQ_INLINEMETHOD lookup& operator=(lookup const& v)
			{
				if (this == std::addressof(v))
				{
					return *this;
				}

				lookup tmp(v);

				swap(tmp);

				return *this;
			}

			CPPLINQ_INLINEMETHOD lookup& operator=(lookup&& v) CPPLINQ_NOEXCEPT
			{
				if (this == std::addressof(v))
				{
					return *this;
				}

				swap(v);

				return *this;
			}

			struct lookup_range : base_range
			{
				typedef lookup_range this_type;

				enum
				{
					returns_reference = 1,
				};

				typedef TValue value_type;
				typedef value_type const& return_type;

				enum state
				{
					state_initial,
					state_iterating,
					state_end,
				};

				values_type const* values;
				size_type iter;
				size_type end;
				state state;

				CPPLINQ_INLINEMETHOD lookup_range(values_type const* values, size_type iter, size_type end) CPPLINQ_NOEXCEPT
					: values(values),
					iter(iter),
					end(end),
					state(state_initial)
				{
					CPPLINQ_ASSERT(values);
				}

				CPPLINQ_INLINEMETHOD lookup_range(lookup_range const& v) CPPLINQ_NOEXCEPT
					: values(v.values),
					iter(v.iter),
					end(v.end),
					state(v.state)
				{
				}

				CPPLINQ_INLINEMETHOD lookup_range(lookup_range&& v) CPPLINQ_NOEXCEPT
					: values(std::move(v.values)),
					iter(std::move(v.iter)),
					end(std::move(v.end)),
					state(std::move(v.state))
				{
				}

				template<typename TRangeBuilder>
				CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
				{
					return range_builder.build(*this);
				}

				CPPLINQ_INLINEMETHOD auto front() const CPPLINQ_NOEXCEPT
				{
					CPPLINQ_ASSERT(state == state_iterating);
					CPPLINQ_ASSERT(iter < end);

					return (*values)[iter];
				}

				CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
				{
					switch (state)
					{
					case state_initial:
					{
						auto has_elements = iter < end;
						state = has_elements ? state_iterating : state_end;
						return has_elements;
					}
					break;
					case state_iterating:
					{
						++iter;

						auto has_elements = iter < end;
						state = has_elements ? state_iterating : state_end;
						return has_elements;
					}
					break;
					case state_end:
					default:
						return false;
					}
				}

			};

			CPPLINQ_METHOD auto operator[](key_type const& key) const CPPLINQ_NOEXCEPT
			{
				if (values.empty())
				{
					return lookup_range(std::addressof(values), 0U, 0U);
				}

				auto find = std::lower_bound(keys.begin(), keys.end(), typename keys_type::value_type(key, 0U),
					[](typename keys_type::value_type const& l, typename keys_type::value_type const& r)
					{
						return l.first < r.first;
					});

				if (find == keys.end())
				{
					return lookup_range(std::addressof(values), 0U, 0U);
				}

				auto next = find + 1;
				if (next == keys.end())
				{
					return lookup_range(std::addressof(values), find->second, values.size());
				}

				return lookup_range(std::addressof(values), find->second, next->second);
			}

			CPPLINQ_INLINEMETHOD auto size_of_keys() const CPPLINQ_NOEXCEPT
			{
				return keys.size();
			}

			CPPLINQ_INLINEMETHOD auto size_of_values() const CPPLINQ_NOEXCEPT
			{
				return values.size();
			}

			CPPLINQ_INLINEMETHOD auto range_of_keys() const CPPLINQ_NOEXCEPT
			{
				return from_range<keys_iterator_type>(keys.begin(), keys.end());
			}

			CPPLINQ_INLINEMETHOD auto range_of_values() const CPPLINQ_NOEXCEPT
			{
				return from_range<values_iterator_type>(values.begin(), values.end());
			}

		private:
			values_type values;
			keys_type keys;
		};

		template<typename TKeySelector>
		struct to_lookup_builder : base_builder
		{
			typedef to_lookup_builder<TKeySelector> this_type;
			typedef TKeySelector key_selector_type;

			key_selector_type key_selector;

			CPPLINQ_INLINEMETHOD explicit to_lookup_builder(key_selector_type key_selector) CPPLINQ_NOEXCEPT
				: key_selector(std::move(key_selector))
			{
			}

			CPPLINQ_INLINEMETHOD to_lookup_builder(to_lookup_builder const& v)
				: key_selector(v.key_selector)
			{
			}

			CPPLINQ_INLINEMETHOD to_lookup_builder(to_lookup_builder&& v) CPPLINQ_NOEXCEPT
				: key_selector(std::move(v.key_selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				typedef lookup<
					typename get_transformed_type<key_selector_type, typename TRange::value_type>::type,
					typename TRange::value_type
				> result_type;

				result_type result(16U, range, key_selector);

				return result;
			}

		};

		template<typename TKeySelector, typename TValueSelector>
		struct to_lookup_value_selector_builder : base_builder
		{
			typedef to_lookup_value_selector_builder<TKeySelector, TValueSelector> this_type;
			typedef TKeySelector key_selector_type;
			typedef TValueSelector value_selector_type;

			key_selector_type key_selector;
			value_selector_type value_selector;

			CPPLINQ_INLINEMETHOD explicit to_lookup_value_selector_builder(key_selector_type key_selector, value_selector_type value_selector) CPPLINQ_NOEXCEPT
				: key_selector(std::move(key_selector)),
				value_selector(std::move(value_selector))
			{
			}

			CPPLINQ_INLINEMETHOD to_lookup_value_selector_builder(to_lookup_value_selector_builder const& v)
				: key_selector(v.key_selector),
				value_selector(v.value_selector)
			{
			}

			CPPLINQ_INLINEMETHOD to_lookup_value_selector_builder(to_lookup_value_selector_builder&& v) CPPLINQ_NOEXCEPT
				: key_selector(std::move(v.key_selector)),
				value_selector(std::move(v.value_selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				typedef lookup<
					typename get_transformed_type<key_selector_type, typename TRange::value_type>::type
					, typename get_transformed_type<value_selector_type, typename TRange::value_type>::type
				> result_type;

				result_type result(16U, range, key_selector, value_selector);

				return result;
			}
		};

		// -------------------------------------------------------------------------

		template<typename TPredicate>
		struct for_each_builder : base_builder
		{
			typedef for_each_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD explicit for_each_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD for_each_builder(for_each_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD for_each_builder(for_each_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD void build(TRange range) const
			{
				while (range.next())
				{
					predicate(range.front());
				}
			}
		};

		// -------------------------------------------------------------------------

		template<typename TPredicate>
		struct first_predicate_builder : base_builder
		{
			typedef first_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD first_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD first_predicate_builder(first_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD first_predicate_builder(first_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range)
			{
				while (range.next())
				{
					if (predicate(range.front()))
					{
						return range.front();
					}
				}

				throw sequence_empty_exception();
			}
		};

		// -------------------------------------------------------------------------

		struct first_builder : base_builder
		{
			typedef first_builder this_type;

			CPPLINQ_INLINEMETHOD first_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD first_builder(first_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD first_builder(first_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range)
			{
				if (range.next())
				{
					return range.front();
				}

				throw sequence_empty_exception();
			}
		};

		// -------------------------------------------------------------------------

		template<typename TPredicate>
		struct first_or_default_predicate_builder : base_builder
		{
			typedef first_or_default_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD first_or_default_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD first_or_default_predicate_builder(first_or_default_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD first_or_default_predicate_builder(first_or_default_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				while (range.next())
				{
					if (predicate(range.front()))
					{
						return range.front();
					}
				}

				return typename TRange::value_type();
			}
		};

		struct first_or_default_builder : base_builder
		{
			typedef first_or_default_builder this_type;

			CPPLINQ_INLINEMETHOD first_or_default_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD first_or_default_builder(first_or_default_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD first_or_default_builder(first_or_default_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				if (range.next())
				{
					return range.front();
				}

				return typename TRange::value_type();
			}
		};

		// -------------------------------------------------------------------------

		template<typename TPredicate>
		struct last_predicate_builder : base_builder
		{
			typedef last_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD last_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD last_predicate_builder(last_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD last_predicate_builder(last_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range)
			{
				opt<typename TRange::value_type> current;

				while (range.next())
				{
					if (predicate(range.front()))
					{
						current = range.front();
					}
				}

				if (current.has_value())
				{
					return current.get();
				}
				else
				{
					throw sequence_empty_exception();
				}
			}
		};

		// -------------------------------------------------------------------------

		struct last_builder : base_builder
		{
			typedef last_builder this_type;

			CPPLINQ_INLINEMETHOD last_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD last_builder(last_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD last_builder(last_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range)
			{
				opt<typename TRange::value_type> current;

				while (range.next())
				{
					current = range.front();
				}

				if (current.has_value())
				{
					return current.get();
				}
				else
				{
					throw sequence_empty_exception();
				}
			}
		};

		// -------------------------------------------------------------------------

		template<typename TPredicate>
		struct last_or_default_predicate_builder : base_builder
		{
			typedef last_or_default_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD last_or_default_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD last_or_default_predicate_builder(last_or_default_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD last_or_default_predicate_builder(last_or_default_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto current = typename TRange::value_type();

				while (range.next())
				{
					if (predicate(range.front()))
					{
						current = range.front();
					}
				}

				return current;
			}
		};

		struct last_or_default_builder : base_builder
		{
			typedef last_or_default_builder this_type;

			CPPLINQ_INLINEMETHOD last_or_default_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD last_or_default_builder(last_or_default_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD last_or_default_builder(last_or_default_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto current = typename TRange::value_type();

				while (range.next())
				{
					current = range.front();
				}

				return current;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TPredicate>
		struct count_predicate_builder : base_builder
		{
			typedef count_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD count_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD count_predicate_builder(count_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD count_predicate_builder(count_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				size_type count = 0U;
				while (range.next())
				{
					if (predicate(range.front()))
					{
						++count;
					}
				}
				return count;
			}
		};

		struct count_builder : base_builder
		{
			typedef count_builder this_type;

			CPPLINQ_INLINEMETHOD count_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD count_builder(count_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD count_builder(count_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				size_type count = 0U;
				while (range.next())
				{
					++count;
				}
				return count;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TSelector>
		struct sum_selector_builder : base_builder
		{
			typedef sum_selector_builder<TSelector> this_type;
			typedef TSelector selector_type;

			selector_type selector;

			CPPLINQ_INLINEMETHOD sum_selector_builder(selector_type selector) CPPLINQ_NOEXCEPT
				: selector(std::move(selector))
			{
			}

			CPPLINQ_INLINEMETHOD sum_selector_builder(sum_selector_builder const& v) CPPLINQ_NOEXCEPT
				: selector(v.selector)
			{
			}

			CPPLINQ_INLINEMETHOD sum_selector_builder(sum_selector_builder&& v) CPPLINQ_NOEXCEPT
				: selector(std::move(v.selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				typedef typename get_transformed_type<selector_type, typename TRange::value_type>::type value_type;

				auto sum = value_type();
				while (range.next())
				{
					sum += selector(range.front());
				}
				return sum;
			}
		};

		struct sum_builder : base_builder
		{
			typedef sum_builder this_type;

			CPPLINQ_INLINEMETHOD sum_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD sum_builder(sum_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD sum_builder(sum_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto sum = typename TRange::value_type();
				while (range.next())
				{
					sum += range.front();
				}
				return sum;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TSelector>
		struct max_selector_builder : base_builder
		{
			typedef max_selector_builder<TSelector> this_type;
			typedef TSelector selector_type;

			selector_type selector;

			CPPLINQ_INLINEMETHOD max_selector_builder(selector_type selector) CPPLINQ_NOEXCEPT
				: selector(std::move(selector))
			{
			}

			CPPLINQ_INLINEMETHOD max_selector_builder(max_selector_builder const& v) CPPLINQ_NOEXCEPT
				: selector(v.selector)
			{
			}

			CPPLINQ_INLINEMETHOD max_selector_builder(max_selector_builder&& v) CPPLINQ_NOEXCEPT
				: selector(std::move(v.selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				typedef typename get_transformed_type<selector_type, typename TRange::value_type>::type value_type;

				auto current = std::numeric_limits<value_type>::lowest();
				while (range.next())
				{
					auto v = selector(range.front());
					if (current < v)
					{
						current = std::move(v);
					}
				}

				return current;
			}
		};

		struct max_builder : base_builder
		{
			typedef max_builder this_type;

			CPPLINQ_INLINEMETHOD max_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD max_builder(max_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD max_builder(max_builder&& v) CPPLINQ_NOEXCEPT
			{
			}


			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto current = std::numeric_limits<typename TRange::value_type>::lowest();
				while (range.next())
				{
					auto v = range.front();
					if (current < v)
					{
						current = std::move(v);
					}
				}

				return current;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TSelector>
		struct min_selector_builder : base_builder
		{
			typedef min_selector_builder<TSelector> this_type;
			typedef TSelector selector_type;

			selector_type selector;

			CPPLINQ_INLINEMETHOD min_selector_builder(selector_type selector) CPPLINQ_NOEXCEPT
				: selector(std::move(selector))
			{
			}

			CPPLINQ_INLINEMETHOD min_selector_builder(min_selector_builder const& v) CPPLINQ_NOEXCEPT
				: selector(v.selector)
			{
			}

			CPPLINQ_INLINEMETHOD min_selector_builder(min_selector_builder&& v) CPPLINQ_NOEXCEPT
				: selector(std::move(v.selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				typedef typename get_transformed_type<selector_type, typename TRange::value_type>::type value_type;

				auto current = std::numeric_limits<value_type>::max();
				while (range.next())
				{
					auto v = selector(range.front());
					if (v < current)
					{
						current = std::move(v);
					}
				}

				return current;
			}
		};

		struct min_builder : base_builder
		{
			typedef min_builder this_type;

			CPPLINQ_INLINEMETHOD min_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD min_builder(min_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD min_builder(min_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto current = std::numeric_limits<typename TRange::value_type>::max();
				while (range.next())
				{
					auto v = range.front();
					if (v < current)
					{
						current = std::move(v);
					}
				}

				return current;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TSelector>
		struct avg_selector_builder : base_builder
		{
			typedef avg_selector_builder<TSelector> this_type;
			typedef TSelector selector_type;

			selector_type selector;

			CPPLINQ_INLINEMETHOD avg_selector_builder(selector_type selector) CPPLINQ_NOEXCEPT
				: selector(std::move(selector))
			{
			}

			CPPLINQ_INLINEMETHOD avg_selector_builder(avg_selector_builder const& v) CPPLINQ_NOEXCEPT
				: selector(v.selector)
			{
			}

			CPPLINQ_INLINEMETHOD avg_selector_builder(avg_selector_builder&& v) CPPLINQ_NOEXCEPT
				: selector(std::move(v.selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				typedef typename get_transformed_type<selector_type, typename TRange::value_type>::type value_type;

				auto sum = value_type();
				int count = 0;
				while (range.next())
				{
					sum += selector(range.front());
					++count;
				}

				if (count == 0)
				{
					return sum;
				}

				return sum / count;
			}

		};

		struct avg_builder : base_builder
		{
			typedef avg_builder this_type;

			CPPLINQ_INLINEMETHOD avg_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD avg_builder(avg_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD avg_builder(avg_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto sum = typename TRange::value_type();
				int count = 0;
				while (range.next())
				{
					sum += range.front();
					++count;
				}

				if (count == 0)
				{
					return sum;
				}

				return sum / count;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TAccumulator>
		struct aggregate_without_seed_builder : base_builder
		{
			typedef aggregate_without_seed_builder<TAccumulator> this_type;
			typedef TAccumulator accumulator_type;

			accumulator_type accumulator;

			CPPLINQ_INLINEMETHOD aggregate_without_seed_builder(accumulator_type accumulator) CPPLINQ_NOEXCEPT
				: accumulator(std::move(accumulator))
			{
			}

			CPPLINQ_INLINEMETHOD aggregate_without_seed_builder(aggregate_without_seed_builder const& v) CPPLINQ_NOEXCEPT
				: accumulator(v.accumulator)
			{
			}

			CPPLINQ_INLINEMETHOD aggregate_without_seed_builder(aggregate_without_seed_builder&& v) CPPLINQ_NOEXCEPT
				: accumulator(std::move(v.accumulator))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range)
			{
				if (!range.next())
				{
					throw sequence_empty_exception();
				}

				auto sum = range.front();
				while (range.next())
				{
					sum = accumulator(sum, range.front());
				}
				return std::move(sum);
			}
		};

		// -------------------------------------------------------------------------

		template <typename TAccumulate, typename TAccumulator>
		struct aggregate_builder : base_builder
		{
			typedef aggregate_builder<TAccumulate, TAccumulator> this_type;
			typedef TAccumulator accumulator_type;
			typedef TAccumulate seed_type;

			seed_type seed;
			accumulator_type accumulator;

			CPPLINQ_INLINEMETHOD aggregate_builder(seed_type seed, accumulator_type accumulator) CPPLINQ_NOEXCEPT
				: seed(std::move(seed)),
				accumulator(std::move(accumulator))
			{
			}

			CPPLINQ_INLINEMETHOD aggregate_builder(aggregate_builder const& v) CPPLINQ_NOEXCEPT
				: seed(v.seed),
				accumulator(v.accumulator)
			{
			}

			CPPLINQ_INLINEMETHOD aggregate_builder(aggregate_builder&& v) CPPLINQ_NOEXCEPT
				: seed(std::move(v.seed)),
				accumulator(std::move(v.accumulator))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto sum = seed;
				while (range.next())
				{
					sum = accumulator(sum, range.front());
				}
				return sum;
			}
		};

		template <typename TAccumulate, typename TAccumulator, typename TSelector>
		struct aggregate_result_selector_builder : base_builder
		{
			typedef aggregate_result_selector_builder<TAccumulate, TAccumulator, TSelector> this_type;
			typedef TAccumulator accumulator_type;
			typedef TAccumulate seed_type;
			typedef TSelector result_selector_type;

			seed_type seed;
			accumulator_type accumulator;
			result_selector_type result_selector;

			CPPLINQ_INLINEMETHOD aggregate_result_selector_builder(seed_type seed, accumulator_type accumulator, result_selector_type result_selector) CPPLINQ_NOEXCEPT
				: seed(std::move(seed)),
				accumulator(std::move(accumulator)),
				result_selector(std::move(result_selector))
			{
			}

			CPPLINQ_INLINEMETHOD aggregate_result_selector_builder(aggregate_result_selector_builder const& v) CPPLINQ_NOEXCEPT
				: seed(v.seed),
				accumulator(v.accumulator),
				result_selector(v.result_selector)
			{
			}

			CPPLINQ_INLINEMETHOD aggregate_result_selector_builder(aggregate_result_selector_builder&& v) CPPLINQ_NOEXCEPT
				: seed(std::move(v.seed)),
				accumulator(std::move(v.accumulator)),
				result_selector(std::move(v.result_selector))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const -> decltype (result_selector(seed))
			{
				auto sum = seed;
				while (range.next())
				{
					sum = accumulator(sum, range.front());
				}

				return result_selector(sum);
			}
		};

		// -------------------------------------------------------------------------

		template <typename TOtherRange, typename TComparer>
		struct sequence_equal_predicate_builder : base_builder
		{
			typedef sequence_equal_predicate_builder<TOtherRange, TComparer> this_type;
			typedef TOtherRange other_range_type;
			typedef TComparer comparer_type;

			other_range_type other_range;
			comparer_type comparer;

			CPPLINQ_INLINEMETHOD sequence_equal_predicate_builder(TOtherRange other_range, comparer_type comparer) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range)),
				comparer(std::move(comparer))
			{
			}

			CPPLINQ_INLINEMETHOD sequence_equal_predicate_builder(sequence_equal_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range),
				comparer(v.comparer)
			{
			}

			CPPLINQ_INLINEMETHOD sequence_equal_predicate_builder(sequence_equal_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range)),
				comparer(std::move(v.comparer))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto copy = other_range;
				for (;;)
				{
					bool next1 = range.next();
					bool next2 = copy.next();

					// sequences are not of same length
					if (next1 != next2)
					{
						return false;
					}

					// both sequences are over, next1 = next2 = false
					if (!next1)
					{
						return true;
					}

					if (!comparer(range.front(), copy.front()))
					{
						return false;
					}
				}
			}
		};

		template <typename TOtherRange>
		struct sequence_equal_builder : base_builder
		{
			typedef sequence_equal_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD sequence_equal_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD sequence_equal_builder(sequence_equal_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD sequence_equal_builder(sequence_equal_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto copy = other_range;
				for (;;)
				{
					bool next1 = range.next();
					bool next2 = copy.next();

					// sequences are not of same length
					if (next1 != next2)
					{
						return false;
					}

					// both sequences are over, next1 = next2 = false
					if (!next1)
					{
						return true;
					}

					if (range.front() != copy.front())
					{
						return false;
					}
				}
			}
		};

		// -------------------------------------------------------------------------

		template<typename TCharType>
		struct concatenate_builder : base_builder
		{
			typedef concatenate_builder<TCharType> this_type;

			std::basic_string<TCharType> separator;
			size_type capacity;

			CPPLINQ_INLINEMETHOD concatenate_builder(std::basic_string<TCharType> separator, size_type capacity) CPPLINQ_NOEXCEPT
				: separator(std::move(separator)),
				capacity(capacity)
			{
			}

			CPPLINQ_INLINEMETHOD concatenate_builder(concatenate_builder const& v) CPPLINQ_NOEXCEPT
				: separator(v.separator),
				capacity(v.capacity)
			{
			}

			CPPLINQ_INLINEMETHOD concatenate_builder(concatenate_builder&& v) CPPLINQ_NOEXCEPT
				: separator(std::move(v.separator)),
				capacity(std::move(v.capacity))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				auto first = true;
				std::vector<TCharType> buffer;

				buffer.reserve(capacity);

				while (range.next())
				{
					if (first)
					{
						first = false;
					}
					else
					{
						buffer.insert(buffer.end(), separator.begin(), separator.end());
					}

					auto v = range.front();

					buffer.insert(buffer.end(), v.begin(), v.end());
				}

				return std::basic_string<TCharType>(buffer.begin(), buffer.end());
			}
		};

		// -------------------------------------------------------------------------

		template <typename TPredicate>
		struct any_predicate_builder : base_builder
		{
			typedef any_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD any_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD any_predicate_builder(any_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD any_predicate_builder(any_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				bool any = false;
				while (range.next() && !any)
				{
					any = predicate(range.front());
				}
				return any;
			}
		};

		struct any_builder : base_builder
		{
			typedef any_builder this_type;

			CPPLINQ_INLINEMETHOD any_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD any_builder(any_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD any_builder(any_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return range.next();
			}
		};

		// -------------------------------------------------------------------------

		template <typename TPredicate>
		struct all_predicate_builder : base_builder
		{
			typedef all_predicate_builder<TPredicate> this_type;
			typedef TPredicate predicate_type;

			predicate_type predicate;

			CPPLINQ_INLINEMETHOD all_predicate_builder(predicate_type predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD all_predicate_builder(all_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD all_predicate_builder(all_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				while (range.next())
				{
					if (!predicate(range.front()))
					{
						return false;
					}
				}

				return true;
			}
		};

		// -------------------------------------------------------------------------

		template <typename TValue>
		struct contains_builder : base_builder
		{
			typedef contains_builder<TValue> this_type;
			typedef TValue value_type;

			value_type value;

			CPPLINQ_INLINEMETHOD contains_builder(value_type value) CPPLINQ_NOEXCEPT
				: value(std::move(value))
			{
			}

			CPPLINQ_INLINEMETHOD contains_builder(contains_builder const& v) CPPLINQ_NOEXCEPT
				: value(v.value)
			{
			}

			CPPLINQ_INLINEMETHOD contains_builder(contains_builder&& v) CPPLINQ_NOEXCEPT
				: value(std::move(v.value))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				while (range.next())
				{
					if (range.front() == value)
					{
						return true;
					}
				}

				return false;
			}
		};

		template <typename TValue, typename TPredicate>
		struct contains_predicate_builder : base_builder
		{
			typedef contains_predicate_builder<TValue, TPredicate> this_type;
			typedef TValue value_type;
			typedef TPredicate predicate_type;

			value_type value;
			predicate_type predicate;

			CPPLINQ_INLINEMETHOD contains_predicate_builder(value_type value, predicate_type predicate) CPPLINQ_NOEXCEPT
				: value(std::move(value)),
				predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD contains_predicate_builder(contains_predicate_builder const& v) CPPLINQ_NOEXCEPT
				: value(v.value),
				predicate(v.predicate)
			{
			}

			CPPLINQ_INLINEMETHOD contains_predicate_builder(contains_predicate_builder&& v) CPPLINQ_NOEXCEPT
				: value(std::move(v.value)),
				predicate(std::move(v.predicate))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				while (range.next())
				{
					if (predicate(range.front(), value))
					{
						return true;
					}
				}

				return false;
			}
		};

		// -------------------------------------------------------------------------

		struct element_at_or_default_builder : base_builder
		{
			typedef element_at_or_default_builder this_type;

			size_type index;

			CPPLINQ_INLINEMETHOD element_at_or_default_builder(size_type index) CPPLINQ_NOEXCEPT
				: index(std::move(index))
			{
			}

			CPPLINQ_INLINEMETHOD element_at_or_default_builder(element_at_or_default_builder const& v) CPPLINQ_NOEXCEPT
				: index(v.index)
			{
			}

			CPPLINQ_INLINEMETHOD element_at_or_default_builder(element_at_or_default_builder&& v) CPPLINQ_NOEXCEPT
				: index(std::move(v.index))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				size_type current = 0U;

				while (range.next())
				{
					if (current < index)
					{
						++current;
					}
					else
					{
						return range.front();
					}
				}

				return typename TRange::value_type();
			}
		};

		// -------------------------------------------------------------------------

		struct element_at_builder : base_builder
		{
			typedef element_at_builder this_type;

			size_type index;

			CPPLINQ_INLINEMETHOD element_at_builder(size_type index) CPPLINQ_NOEXCEPT
				: index(std::move(index))
			{
			}

			CPPLINQ_INLINEMETHOD element_at_builder(element_at_builder const& v) CPPLINQ_NOEXCEPT
				: index(v.index)
			{
			}

			CPPLINQ_INLINEMETHOD element_at_builder(element_at_builder&& v) CPPLINQ_NOEXCEPT
				: index(std::move(v.index))
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range)
			{
				size_type current = 0U;

				while (range.next())
				{
					if (current < index)
					{
						++current;
					}
					else
					{
						return range.front();
					}
				}

				throw out_of_range_exception();
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange>
		struct pairwise_range : base_range
		{
			typedef pairwise_range<TRange> this_type;
			typedef TRange range_type;

			typedef typename TRange::value_type element_type;
			typedef std::pair<element_type, element_type> value_type;
			typedef value_type return_type;

			enum
			{
				returns_reference = 0,
			};

			range_type range;
			opt<element_type> previous;
			opt<element_type> current;

			CPPLINQ_INLINEMETHOD pairwise_range(range_type range) CPPLINQ_NOEXCEPT
				: range(std::move(range))
			{
			}

			CPPLINQ_INLINEMETHOD pairwise_range(pairwise_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				previous(v.previous),
				current(v.current)
			{
			}

			CPPLINQ_INLINEMETHOD pairwise_range(pairwise_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				previous(std::move(v.previous)),
				current(std::move(v.current))
			{
			}

			template<typename TPairwiseBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TPairwiseBuilder pairwise_builder) const
			{
				return pairwise_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(previous.has_value());
				CPPLINQ_ASSERT(current.has_value());

				return std::make_pair(previous.get(), current.get());
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				if (!previous.has_value())
				{
					if (range.next())
					{
						current = range.front();
					}
					else
					{
						return false;
					}
				}

				previous.swap(current);

				if (range.next())
				{
					current = range.front();
					return true;
				}

				previous.clear();
				current.clear();

				return false;
			}
		};

		struct pairwise_builder : base_builder
		{
			typedef pairwise_builder this_type;

			CPPLINQ_INLINEMETHOD pairwise_builder() CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD pairwise_builder(pairwise_builder const& v) CPPLINQ_NOEXCEPT
			{
			}

			CPPLINQ_INLINEMETHOD pairwise_builder(pairwise_builder&& v) CPPLINQ_NOEXCEPT
			{
			}

			template<typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return pairwise_range<TRange>(std::move(range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TRange, typename TOtherRange>
		struct zip_with_range : base_range
		{
			typedef zip_with_range<TRange, TOtherRange> this_type;
			typedef TRange range_type;
			typedef TOtherRange other_range_type;

			typedef typename cleanup_type<typename TRange::value_type>::type left_element_type;
			typedef typename cleanup_type<typename TOtherRange::value_type>::type right_element_type;
			typedef std::pair<left_element_type, right_element_type> value_type;
			typedef value_type return_type;
			enum
			{
				returns_reference = 0,
			};

			range_type range;
			other_range_type other_range;

			CPPLINQ_INLINEMETHOD zip_with_range(range_type range, other_range_type other_range) CPPLINQ_NOEXCEPT
				: range(std::move(range)),
				other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD zip_with_range(zip_with_range const& v) CPPLINQ_NOEXCEPT
				: range(v.range),
				other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD zip_with_range(zip_with_range&& v) CPPLINQ_NOEXCEPT
				: range(std::move(v.range)),
				other_range(std::move(v.other_range))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				return std::make_pair(range.front(), other_range.front());
			}

			CPPLINQ_INLINEMETHOD auto next()
			{
				return range.next() && other_range.next();
			}
		};

		template <typename TOtherRange>
		struct zip_with_builder : base_builder
		{
			typedef zip_with_builder<TOtherRange> this_type;
			typedef TOtherRange other_range_type;

			other_range_type other_range;

			CPPLINQ_INLINEMETHOD zip_with_builder(TOtherRange other_range) CPPLINQ_NOEXCEPT
				: other_range(std::move(other_range))
			{
			}

			CPPLINQ_INLINEMETHOD zip_with_builder(zip_with_builder const& v) CPPLINQ_NOEXCEPT
				: other_range(v.other_range)
			{
			}

			CPPLINQ_INLINEMETHOD zip_with_builder(zip_with_builder&& v) CPPLINQ_NOEXCEPT
				: other_range(std::move(v.other_range))
			{
			}

			template <typename TRange>
			CPPLINQ_INLINEMETHOD auto build(TRange range) const
			{
				return zip_with_range<TRange, TOtherRange>(std::move(range), std::move(other_range));
			}
		};

		// -------------------------------------------------------------------------

		template<typename TPredicate>
		struct generate_range : base_range
		{
			static TPredicate get_predicate();

			typedef decltype (get_predicate()()) raw_opt_value_type;
			typedef typename cleanup_type<raw_opt_value_type>::type opt_value_type;

			typedef decltype (*(get_predicate()())) raw_value_type;
			typedef typename cleanup_type<raw_value_type>::type value_type;

			typedef generate_range<TPredicate> this_type;
			typedef TPredicate predicate_type;
			typedef value_type const& return_type;

			enum
			{
				returns_reference = 1,
			};

			TPredicate predicate;
			opt_value_type current_value;

			CPPLINQ_INLINEMETHOD generate_range(TPredicate predicate) CPPLINQ_NOEXCEPT
				: predicate(std::move(predicate))
			{
			}

			CPPLINQ_INLINEMETHOD generate_range(generate_range const& v) CPPLINQ_NOEXCEPT
				: predicate(v.predicate),
				current_value(v.current_value)
			{
			}

			CPPLINQ_INLINEMETHOD generate_range(generate_range&& v) CPPLINQ_NOEXCEPT
				: predicate(std::move(v.predicate)),
				current_value(std::move(v.current_value))
			{
			}

			template<typename TRangeBuilder>
			CPPLINQ_INLINEMETHOD auto operator>>(TRangeBuilder range_builder) const
			{
				return range_builder.build(*this);
			}

			CPPLINQ_INLINEMETHOD auto front() const
			{
				CPPLINQ_ASSERT(current_value);

				return *current_value;
			}

			CPPLINQ_INLINEMETHOD auto next() CPPLINQ_NOEXCEPT
			{
				current_value = predicate();

				return current_value;
			}
		};

		// -------------------------------------------------------------------------

	} // namespace detail

	// -------------------------------------------------------------------------
	// The interface of cpplinq
	// -------------------------------------------------------------------------

	// Range sources

	template<typename TValueIterator>
	CPPLINQ_INLINEMETHOD auto from_iterators(TValueIterator begin, TValueIterator end) CPPLINQ_NOEXCEPT
	{
		return detail::from_range<TValueIterator>(std::move(begin), std::move(end));
	}

	template<typename TContainer>
	CPPLINQ_INLINEMETHOD auto from(TContainer const& container) CPPLINQ_NOEXCEPT
	{
		return from_iterators(container.begin(), container.end());
	}

	template<typename TValueArray>
	CPPLINQ_INLINEMETHOD auto from_array(TValueArray& a) CPPLINQ_NOEXCEPT
	{
		typedef detail::get_array_properties<TValueArray> array_properties;
		typedef typename array_properties::iterator_type iterator_type;

		iterator_type begin = a;
		iterator_type end = begin + array_properties::size;

		return from_iterators(std::move(begin), std::move(end));
	}

	template<typename TContainer>
	CPPLINQ_INLINEMETHOD auto from_copy(TContainer&& container)
	{
		typedef typename detail::cleanup_type<TContainer>::type container_type;

		return detail::from_copy_range<container_type>(std::forward<TContainer>(container));
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto generate(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::generate_range<TPredicate>(std::move(predicate));
	}

	// Restriction operators

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto where(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::where_builder<TPredicate>(std::move(predicate));
	}

	// Projection operators

	CPPLINQ_INLINEMETHOD auto ref() CPPLINQ_NOEXCEPT
	{
		return detail::ref_builder();
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto select(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::select_builder<TPredicate>(std::move(predicate));
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto select_many(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::select_many_builder<TPredicate>(std::move(predicate));
	}

	template<typename TOtherRange, typename TKeySelector, typename TOtherKeySelector, typename TCombiner>
	CPPLINQ_INLINEMETHOD auto join(TOtherRange other_range, TKeySelector key_selector, TOtherKeySelector other_key_selector, TCombiner combiner) CPPLINQ_NOEXCEPT
	{
		return detail::join_builder<TOtherRange, TKeySelector, TOtherKeySelector, TCombiner>
			(std::move(other_range), std::move(key_selector), std::move(other_key_selector), std::move(combiner));
	}

	// Concatenation operators

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto concat(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::concat_builder<TOtherRange>(std::move(other_range));
	}

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto start_with(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::start_with_builder<TOtherRange>(std::move(other_range));
	}

	// Partitioning operators

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto take_while(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::take_while_builder<TPredicate>(std::move(predicate));
	}

	CPPLINQ_INLINEMETHOD auto take(size_type count) CPPLINQ_NOEXCEPT
	{
		return detail::take_builder(count);
	}

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto skip_while(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::skip_while_builder<TPredicate>(predicate);
	}

	CPPLINQ_INLINEMETHOD auto skip(size_type count) CPPLINQ_NOEXCEPT
	{
		return detail::skip_builder(count);
	}

	// Ordering operators

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto orderby(TPredicate predicate, bool sort_ascending = true) CPPLINQ_NOEXCEPT
	{
		return detail::orderby_builder<TPredicate>(std::move(predicate), sort_ascending);
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto orderby_ascending(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::orderby_builder<TPredicate>(std::move(predicate), true);
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto orderby_descending(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::orderby_builder<TPredicate>(std::move(predicate), false);
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto thenby(TPredicate predicate, bool sort_ascending = true) CPPLINQ_NOEXCEPT
	{
		return detail::thenby_builder<TPredicate>(std::move(predicate), sort_ascending);
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto thenby_ascending(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::thenby_builder<TPredicate>(std::move(predicate), true);
	}

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto thenby_descending(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::thenby_builder<TPredicate>(std::move(predicate), false);
	}

	CPPLINQ_INLINEMETHOD auto reverse(size_type capacity = 16U) CPPLINQ_NOEXCEPT
	{
		return detail::reverse_builder(capacity);
	}

	// Conversion operators

	namespace experimental
	{
		CPPLINQ_INLINEMETHOD auto container() CPPLINQ_NOEXCEPT
		{
			return detail::experimental::container_builder();
		}
	}

	template<typename TValue>
	CPPLINQ_INLINEMETHOD auto to_opt(TValue&& v)
	{
		return detail::opt<typename detail::cleanup_type<TValue>::type>(std::forward<TValue>(v));
	}

	template<typename TValue>
	CPPLINQ_INLINEMETHOD auto to_opt()
	{
		return detail::opt<TValue>();
	}

	CPPLINQ_INLINEMETHOD auto to_vector(size_type capacity = 16U) CPPLINQ_NOEXCEPT
	{
		return detail::to_vector_builder(capacity);
	}

	CPPLINQ_INLINEMETHOD auto to_deque() CPPLINQ_NOEXCEPT
	{
		return detail::to_deque_builder();
	}

	CPPLINQ_INLINEMETHOD auto to_list() CPPLINQ_NOEXCEPT
	{
		return detail::to_list_builder();
	}

	template<typename TKeySelector>
	CPPLINQ_INLINEMETHOD auto to_map(TKeySelector key_selector) CPPLINQ_NOEXCEPT
	{
		return detail::to_map_builder<TKeySelector>(std::move(key_selector));
	}

	template<typename TKeySelector>
	CPPLINQ_INLINEMETHOD auto to_lookup(TKeySelector key_selector) CPPLINQ_NOEXCEPT
	{
		return detail::to_lookup_builder<TKeySelector>(std::move(key_selector));
	}

	template<typename TKeySelector, typename TValueSelector>
	CPPLINQ_INLINEMETHOD auto to_lookup(TKeySelector key_selector, TValueSelector value_selector) CPPLINQ_NOEXCEPT
	{
		return detail::to_lookup_value_selector_builder<TKeySelector, TValueSelector>(std::move(key_selector), std::move(value_selector));
	}

	// Equality operators

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto sequence_equal(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::sequence_equal_builder<TOtherRange>(std::move(other_range));
	}

	template <typename TOtherRange, typename TComparer>
	CPPLINQ_INLINEMETHOD auto sequence_equal(TOtherRange other_range, TComparer comparer) CPPLINQ_NOEXCEPT
	{
		return detail::sequence_equal_predicate_builder<TOtherRange, TComparer>(std::move(other_range), std::move(comparer));
	}

	// Element operators

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto first(TPredicate predicate)
	{
		return detail::first_predicate_builder<TPredicate>(std::move(predicate));
	}

	CPPLINQ_INLINEMETHOD auto first()
	{
		return detail::first_builder();
	}

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto first_or_default(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::first_or_default_predicate_builder<TPredicate>(predicate);
	}

	CPPLINQ_INLINEMETHOD auto first_or_default() CPPLINQ_NOEXCEPT
	{
		return detail::first_or_default_builder();
	}

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto last_or_default(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::last_or_default_predicate_builder<TPredicate>(std::move(predicate));
	}

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto last(TPredicate predicate)
	{
		return detail::last_predicate_builder<TPredicate>(std::move(predicate));
	}

	CPPLINQ_INLINEMETHOD auto last()
	{
		return detail::last_builder();
	}

	CPPLINQ_INLINEMETHOD auto last_or_default() CPPLINQ_NOEXCEPT
	{
		return detail::last_or_default_builder();
	}

	CPPLINQ_INLINEMETHOD auto element_at(size_type index) CPPLINQ_NOEXCEPT
	{
		return detail::element_at_builder(index);
	}

	CPPLINQ_INLINEMETHOD auto element_at_or_default(size_type index) CPPLINQ_NOEXCEPT
	{
		return detail::element_at_or_default_builder(index);
	}

	// Generation operators

	CPPLINQ_INLINEMETHOD auto range(int start, int count) CPPLINQ_NOEXCEPT
	{
		auto c = count > 0 ? count : 0;
		auto end = (INT_MAX - c) > start ? (start + c) : INT_MAX;
		return detail::int_range(start, end);
	}

	template <typename TValue>
	CPPLINQ_INLINEMETHOD auto repeat(TValue element, int count) CPPLINQ_NOEXCEPT
	{
		auto c = count > 0 ? count : 0;
		return detail::repeat_range<TValue>(element, c);
	}

	template <typename TValue>
	CPPLINQ_INLINEMETHOD auto empty() CPPLINQ_NOEXCEPT
	{
		return detail::empty_range<TValue>();
	}

	template<typename TValue>
	CPPLINQ_INLINEMETHOD auto singleton(TValue && value) CPPLINQ_NOEXCEPT
	{
		return detail::singleton_range<typename detail::cleanup_type<TValue>::type>(std::forward<TValue>(value));
	}

	// Quantifiers

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto any(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::any_predicate_builder<TPredicate>(std::move(predicate));
	}

	CPPLINQ_INLINEMETHOD auto any() CPPLINQ_NOEXCEPT
	{
		return detail::any_builder();
	}

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto all(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::all_predicate_builder<TPredicate>(std::move(predicate));
	}

	template <typename TValue>
	CPPLINQ_INLINEMETHOD auto contains(TValue value) CPPLINQ_NOEXCEPT
	{
		return detail::contains_builder<TValue>(value);
	}

	template <typename TValue, typename TPredicate>
	CPPLINQ_INLINEMETHOD auto contains(TValue value, TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::contains_predicate_builder<TValue, TPredicate>(value, std::move(predicate));
	}

	// Aggregate operators

	template <typename TPredicate>
	CPPLINQ_INLINEMETHOD auto count(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::count_predicate_builder<TPredicate>(std::move(predicate));
	}

	CPPLINQ_INLINEMETHOD auto count() CPPLINQ_NOEXCEPT
	{
		return detail::count_builder();
	}

	template<typename TSelector>
	CPPLINQ_INLINEMETHOD auto sum(TSelector selector) CPPLINQ_NOEXCEPT
	{
		return detail::sum_selector_builder<TSelector>(std::move(selector));
	}

	CPPLINQ_INLINEMETHOD auto sum() CPPLINQ_NOEXCEPT
	{
		return detail::sum_builder();
	}

	template<typename TSelector>
	CPPLINQ_INLINEMETHOD auto max(TSelector selector) CPPLINQ_NOEXCEPT
	{
		return detail::max_selector_builder<TSelector>(std::move(selector));
	}

	CPPLINQ_INLINEMETHOD auto max() CPPLINQ_NOEXCEPT
	{
		return detail::max_builder();
	}

	template<typename TSelector>
	CPPLINQ_INLINEMETHOD auto min(TSelector selector) CPPLINQ_NOEXCEPT
	{
		return detail::min_selector_builder<TSelector>(std::move(selector));
	}

	CPPLINQ_INLINEMETHOD auto min() CPPLINQ_NOEXCEPT
	{
		return detail::min_builder();
	}

	template<typename TSelector>
	CPPLINQ_INLINEMETHOD auto avg(TSelector selector) CPPLINQ_NOEXCEPT
	{
		return detail::avg_selector_builder<TSelector>(std::move(selector));
	}

	CPPLINQ_INLINEMETHOD auto avg() CPPLINQ_NOEXCEPT
	{
		return detail::avg_builder();
	}

	template <typename TAccumulator>
	CPPLINQ_INLINEMETHOD auto aggregate(TAccumulator accumulator) CPPLINQ_NOEXCEPT
	{
		return detail::aggregate_without_seed_builder<TAccumulator>(accumulator);
	}

	template <typename TAccumulate, typename TAccumulator>
	CPPLINQ_INLINEMETHOD auto aggregate(TAccumulate seed, TAccumulator accumulator) CPPLINQ_NOEXCEPT
	{
		return detail::aggregate_builder<TAccumulate, TAccumulator>(seed, accumulator);
	}

	template <typename TAccumulate, typename TAccumulator, typename TSelector>
	CPPLINQ_INLINEMETHOD auto aggregate(TAccumulate seed, TAccumulator accumulator, TSelector result_selector) CPPLINQ_NOEXCEPT
	{
		return detail::aggregate_result_selector_builder<TAccumulate, TAccumulator, TSelector>(seed, accumulator, result_selector);
	}

	// set operators
	CPPLINQ_INLINEMETHOD auto distinct() CPPLINQ_NOEXCEPT
	{
		return detail::distinct_builder();
	}

	template <typename TSelector>
	CPPLINQ_INLINEMETHOD auto distinct(TSelector key_selector) CPPLINQ_NOEXCEPT
	{
		return detail::distinct_key_selector_builder<TSelector>(key_selector);
	}

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto union_with(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::union_builder<TOtherRange>(std::move(other_range));
	}

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto intersect_with(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::intersect_builder<TOtherRange>(std::move(other_range));
	}

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto except(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::except_builder<TOtherRange>(std::move(other_range));
	}

	// other operators

	template<typename TPredicate>
	CPPLINQ_INLINEMETHOD auto for_each(TPredicate predicate) CPPLINQ_NOEXCEPT
	{
		return detail::for_each_builder<TPredicate>(std::move(predicate));
	}

	CPPLINQ_INLINEMETHOD auto concatenate(std::string separator, size_type capacity = 16U) CPPLINQ_NOEXCEPT
	{
		return detail::concatenate_builder<char>(std::move(separator), capacity);
	}

	CPPLINQ_INLINEMETHOD auto concatenate(std::wstring separator, size_type capacity = 16U) CPPLINQ_NOEXCEPT
	{
		return detail::concatenate_builder<wchar_t>(std::move(separator), capacity);
	}

	CPPLINQ_INLINEMETHOD auto pairwise() CPPLINQ_NOEXCEPT
	{
		return detail::pairwise_builder();
	}

	template <typename TOtherRange>
	CPPLINQ_INLINEMETHOD auto zip_with(TOtherRange other_range) CPPLINQ_NOEXCEPT
	{
		return detail::zip_with_builder<TOtherRange>(std::move(other_range));
	}

	// -------------------------------------------------------------------------

}
// ----------------------------------------------------------------------------
#ifdef _MSC_VER
#   pragma warning (pop)
#endif
// ----------------------------------------------------------------------------
#endif // CPPLINQ__HEADER_GUARD
// ----------------------------------------------------------------------------