// ==========================================================================
// $Id: stl_sort_C11.cpp,v 1.2 2015/11/16 16:17:15 jlang Exp $
// CSI2372 example Code for lecture 13 - C++11
// ==========================================================================
// (C)opyright:
//
//   Jochen Lang
//   SITE, University of Ottawa
//   800 King Edward Ave.
//   Ottawa, On., K1N 6N5
//   Canada. 
//   http://www.site.uottawa.ca
// 
// Creator: jlang (Jochen Lang)
// Email:   jlang@site.uottawa.ca
// ==========================================================================
// $Log: stl_sort_C11.cpp,v $
// Revision 1.2  2015/11/16 16:17:15  jlang
// More C++11 initializer lists
//
// Revision 1.1  2013/11/10 02:28:08  jlang
// Added examples on bind and lambdas
//
// ==========================================================================
#include <iostream>
#include <vector>
#include <array>
#include <algorithm>

using std::ostream;
using std::cout;
using std::endl;
using std::vector;
using std::array;
using std::sort;
using std::stable_sort;
using std::for_each;

template <class T, const int NUM> class Point;
template <class T, const int NUM>
ostream& operator<<( ostream& _os, const Point<T,NUM>& _pt );

template <class T, const int NUM>
class Point{
  array<T,NUM> d_components;
public:
  Point();
  Point( array<T,NUM>& _components );

	inline T operator()( int _dim ) const;
	inline T& operator()( int _dim );

	friend  
	ostream& operator<< <T,NUM> ( ostream& _os, const Point<T,NUM>& _pt );
};

template <class T, const int NUM>
	Point<T,NUM>::Point() 
{}

template <class T, const int NUM>
	Point<T,NUM>::Point( array<T,NUM>& _components ) :
		d_components(_components) 
{}
  

template <class T, const int NUM>
T Point<T,NUM>::operator()( int _dim ) const {
	return d_components[_dim];
}

template <class T, const int NUM>
T& Point<T,NUM>::operator()( int _dim ){
	return d_components[_dim];
}

template <class T, const int NUM>
ostream& operator<<( ostream& _os, const Point<T,NUM>& _pt )
{
  _os << "( ";
  for ( int i=0; i<NUM; i++ ) {
    if (i!=0) _os  << ", ";
    _os << _pt.d_components[i];
  }  
  _os << " )" << endl;
  return _os;
}


template <class T>
void printElements( const T& _container ) {
  // C++11 loop over the elements and print using auto and for range
  for ( auto &element : _container ) {
    cout << element;
  }
  cout << endl;
  return;
}


int main() {
	// C++11 Use of std::array with list initializer 
	array<int,2> initA{3,15};
  array<int,2> initB{3,13};
  array<int,2> initC{3,8};
  array<int,2> initD{1,12};
  vector<Point<int,2> > pVec{initA,initB,initC,initD};

  // Print unsorted
  printElements( pVec );
  // C++11 Sort the points using a lambda
  sort( pVec.begin(), pVec.end(), 
		[](const Point<int,2>& _a, const Point<int,2>& _b) {
			return _a(1)<_b(1);
		});
  // Print sorted
  printElements( pVec );
  // C++11 Stable sort on 1st coordinate using a lambda
  stable_sort( pVec.begin(), pVec.end(),
			[](const Point<int,2>& _a, const Point<int,2>& _b) {
			return _a(0)<_b(0);
		});
  // C++11 Print sorted using a lambda and std::for_each (C++98)
  for_each( pVec.begin(), pVec.end(), [](const Point<int,2>& _pt) {
  	cout << _pt;
  });
  cout << endl;

  return 0;
}
