#include<vector>
#include<iostream>

#include<boost/lambda/lambda.hpp>
#include<boost/lambda/bind.hpp>

#include<boost/spirit/phoenix.hpp>

#include<numeric> //for accumulate
#include<complex>

using std::cout; 
using std::endl;
using std::complex;
using std::vector;

double sum_norm(double acc, complex<double> elem){
  return acc+std::norm(elem);
}

namespace phoenix{
  struct norm_impl{
    template<typename Arg> struct result{typedef Arg type;};
    template<typename T  > struct result<std::complex<T> >{typedef T type;};
    template <typename Arg>
    typename result<Arg>::type operator()(Arg ar1) const{
      return std::norm(ar1);
    }
  };
  function<norm_impl> norm;
}


int main(){
  //create data
  unsigned l=9;
  vector<complex<double> > v(l);
  for(int i=0; i!=v.size(); ++i) v[i]=std::complex<double>(i,1.2+i);

  // using manual loop over array
  double sum1=0;
  for(int i=0; i!=v.size(); ++i) sum1+=norm(v[i]);
  cout<<"sum1 = "<<sum1<<endl;

  // using manual loop over container
  double sum2=0;
  for(vector<complex<double> >::const_iterator it=v.begin(); it!=v.end(); ++it) sum2+=norm(*it);
  cout<<"sum2 = "<<sum2<<endl;

  //using automatic loop (accumulate) and free function
  double sum3=std::accumulate(v.begin(), v.end(), double(0), sum_norm);
  cout<<"sum3 = "<<sum3<<endl;

  //using accumulate and lambda with pure bind
  using namespace boost::lambda;
  double sum4=std::accumulate(v.begin(), v.end(), double(0),
			      bind(
				   std::plus<double>(),
				   _1,
				   bind(&std::norm<double>, _2)
				   )
			      );
  cout<<"sum4 = "<<sum4<<endl;

  //using accumulate and lambda
  double sum5=std::accumulate(v.begin(), v.end(), double(0), 
			      _1 + bind(&std::norm<double>,_2));
  cout<<"sum5 = "<<sum5<<endl; //  (cout << _1 << " = " << _2 << "\n")("fancy print sum5", sum5);

  //using accumulate and phoenix
  using namespace phoenix;
  double sum6=std::accumulate(v.begin(), v.end(), double(0),
				arg1 + norm(arg2) );
  cout<<"sum6 = "<<sum6<<endl;

  return 0;
}
