#pragma once
#include <algorithm>
#include <array>
#include <functional>
#include <vector>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
using boost::phoenix::arg_names::arg1;
using boost::phoenix::arg_names::arg2;
// Voodoo magic to get the friend function to work
// Thanks Cheers and hth. - Alf
template<class Enumerable>
class Container;
template<class Enumerable>
Container<Enumerable> From(Enumerable& source);
// End of Voodoo magic
template<typename Enumerable>
class Container
{
public:
friend Container From<Enumerable>(Enumerable& source);
template<typename T>
operator T()
{
T t;
std::copy(items_.begin(), items_.end(), std::back_inserter(t));
return t;
}
template<class Predicate>
Container Where(Predicate p)
{
Container filteredContainer;
std::copy_if(items_.begin(), items_.end(), std::back_inserter(filteredContainer.items_), p);
return filteredContainer;
}
Container Take(size_t n)
{
// Add assert on n
Container subContainer;
std::copy(items_.begin(), items_.begin() + n-1, std::back_inserter(subContainer.items_));
return subContainer;
}
template<class Predicate>
Container TakeWhile(Predicate p)
{
Container subContainer;
auto it = items_.begin();
auto end = items_.end();
// Increment it only and use range copy instead of multiple push_back
while(p(*it) && it != end)
{
subContainer.items_.push_back(*it);
it++;
}
return subContainer;
}
Container Skip(size_t n)
{
// Add assert on n
Container subContainer;
std::copy(items_.begin() + n-1, items_.end(), std::back_inserter(subContainer.items_));
return subContainer;
}
template<class Predicate>
Container SkipWhile(Predicate p)
{
Container subContainer;
auto it = items_.begin();
auto end = items_.end();
while(p(*it) && it != end)
{
it++;
}
std::copy(it, end, std::back_inserter(subContainer);
return subContainer;
}
size_t Count()
{
return items_.size();
}
template<class Predicate>
size_t Count(Predicate p)
{
return std::count_if(items_.begin(), items_.end(), p);
}
template<class Function>
void ForEach(Function f)
{
std::for_each(items_.begin(), items_.end(), f);
}
typename Enumerable::value_type Sum()
{
auto it = items_.begin();
auto end = items_.end();
Enumerable::value_type sum = *(it++);
while(it != end)
{
sum += *it;
it++;
}
return sum;
}
typename Enumerable::value_type Min()
{
auto it = items_.begin();
auto end = items_.end();
Enumerable::value_type min = *(it++);
while(it != end)
{
if((*it) < min)
{
min = *it;
}
it++;
}
return min;
}
typename Enumerable::value_type Max()
{
auto it = items_.begin();
auto end = items_.end();
Enumerable::value_type max = *(it++);
while(it != end)
{
if((*it) > max)
{
max = *it;
}
it++;
}
return max;
}
typename Enumerable::value_type Average()
{
return Sum()/Count();
}
template<class Function>
typename Enumerable::value_type Aggregate(Function f)
{
auto it = items_.begin();
auto end = items_.end();
Enumerable::value_type aggregate = *(it++);
while(it != end)
{
aggregate = f(aggregate, *(it++));
}
return aggregate;
}
typename Enumerable::value_type ElementAt(unsigned int index)
{
// Use boost::optional?
return items_[index];
}
private:
Container(): items_() {}
Container(Enumerable& source): items_()
{
std::copy(std::begin(source), std::end(source), std::back_inserter(items_));
}
std::vector<typename Enumerable::value_type> items_;
};
template<typename Enumerable>
Container<Enumerable> From(Enumerable& source)
{
return Container<Enumerable>(source);
}