//vector: 
// - mimics raw c++ array, but dynamically resizable
// - very efficient - no safety processing 
// - operation usually have ready-to-understand names
// - if operation is not efficient - it is not "directly" supported (i.e. - no "nice" name), 
//   but there will be a way ...

#include <vector>
   
std::vector<int> v; // size=0
//initialize (grow)
v.push_back( 1 ); // ++size
v.push_back( 2 ); // ++size
v.push_back( 3 ); // ++size
...
v.push_back( 10 ); // ++size

//access
v[5]; // ok 
v[15]; // possible crash, no bounds checking
v.at( 15 ); // throws
//v.push_front(); // too slow - linear, not implemented
// 0 1 2 3 4 5 6 7
// ^
v.insert( v.begin(), 0 ); // but there is a way

//typical error
std::vector<int> v; // size=0
v.push_back( 1 );
v.push_back( 2 );
v.push_back( 3 ); // indicies 0,1,2 are defined
v[3] = 4; // 4 is outside of valid array (note size is still 3)
// 0 1 2 3 4 5 - indices
// 0 1 2 4      - data
// occup ^ STL end
v.push_back( 5 ); // overwrites 4 at position 3 (++size, size is 4 now)
// 1 2 3 5
//         ^ new end 


// vector access
// read             overwrite existing position            grow (new)
// operator[]                operator[]                    push_back/insert

// 
// size=s
// capacity=c
// +----------------+
// |0 1 2 3 4 5 E   |
// +----------------+
// +----------------+
// |0 1 2 3 4 5 6 7 | E     size = capapcity
// +----------------+
// v.push_back(8); // reallocate - 1) new  2) copy values 3 delete old array
// +--------------------------------+
// |0 1 2 3 4 5 6 7 8 E  free       | 
// +--------------------------------+
// 1 2 4 8 16 32 64 128
// resize( s2 );
// size=s2
// capacity=?? // depends
// 
// size=s
// capacity=c
// reserve( c2 ); // will do nothing if c2 <= c
// size=s
// capacity=c2

//v.reserve ( 16 ); // does not change size
// +--------------------------------+
// |0 1 2 3 4 5 6 7 8 E  free       | 
// +--------------------------------+
// +-----------------------------------------+
// |0 1 2 3 4 5 6 7 8 E  free                | 
// +-----------------------------------------+
//  0                                       15 

// v.resize( 16 ) // changes size
// +--------------------------------+
// |0 1 2 3 4 5 6 7 8 E  free       | 
// +--------------------------------+
// +-----------------------------------------+
// |0 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0| E
// +-----------------------------------------+
//  0                                       15 



// growing by +10 (instead of *2)
if ( v.size() == v.capacity() ) { // out of free space
	v.reserve( v.size() +10 ); // addd 10 un-initialized positions
}
v.push_back( i );

// if growing by addding, n^2 copies
// if growing by multiplying, 2*n copies
// see "vector reallocations cost" on course website (1 down from this link)

// Example 1:
std::vector<int> v;
for ( int i =0; i<1<<20;++i ) {
	v.push_back( i ); // 20 reallocations at 2^i
}

// Example 2:
std::vector<int> v( 1<<20 ); // size= 1 mil (1 mil elements are created by default ctors)
//  equivalent
std::vector<int> v();
v.resize( 1<<20 );
for ( int i =0; i<1<<20;++i ) {
	//v.push_back( i ); // we already have elements
	v[i] = i; // overwrite old values
}

// Example 3:
std::vector<int> v;
v.reserve( 1<<20 );   // no ctors called ! creates uninialized space
for ( int i=0; i<10; ++i ) {
	v.push_back( i ); // uses placement new in the space allocated by reserve
}
// if we need one more 
v.push_back( 11111111 ); // "automatic" reallocation


// another ctor
std::vector<int> v( 10, 1 );



// trimming capacity to size
std::vector<int>(cont1).swap(cont1);
// -----------------------
//     unnamed temp( s=c=cont1.size()=50 )
// 	after swap 
// 	cont1: s=c=50 
// C++11 has shrink_to_fit that probably does the above



//important
// 1 2 3 5 6 7
// ^   ^
// b   e
// range b,e  [b, e)  1,2
// 
// 1 2 3 5 6 7
// ^          ^
// b          e
// v.begin()  v.end()
// whole vector

// write an alg to add doubled value to all odd values in vector<int>
// 1 2 3 4 5
// 1 2 3 4 5 2 6 10
// C++03 style
std::vector<int>::iterator 
	it = v.begin(),
	it_e = v.end();
for ( ; it!=it_e; ++it ) {  // for( int* p=a; p!=p+size; ++p )
	if ( *it %2 == 1 ) {
		v.push_back( *it * 2 );
	}
}
// C++11 style
for ( auto const& el : v ) {
	if ( el%2 == 1 ) {
		v.push_back( el * 2 );
	}
}
//problem - iterator will be invalidated during one of the push_backs and next iteration of the loop will crash
// NEVER mix traversal and modifying (size)

//                 +--------------------------------------------------------------+
//     +-----------+                                                              |
//     |           +--------------------------------------------------------------+
// +---+----+                 ^         
// |        |                 | iterator 
// +--------+                           
// 
// pushback => reallocate
//                        garbage 
// +---+----+                 ^                                                       
// |        |                 | iterator                                              
// +--------+                                                                                                    
//     |           +--------------------------------------------------------------------------------------------+
//     +-----------+                                                                                            |
//                 +--------------------------------------------------------------------------------------------+
                                                                                  
                                     
// separate the traversal and insertions
//traverse, place new elements into a separate container
std::vector<int>::iterator 
	it = v.begin(),   
	it_e = v.end();

std::vector<int> temp;
for ( ; it!=it_e; ++it ) {
	if ( *it %2 == 1 ) {
		temp.push_back( *it * 2 );
	}
}

//add elements
v.insert( v.end(), temp.begin(), temp.end() ); // range insert
// reread iterators after modifying container !!!!
it = v.begin(),
it_e = v.end();
for ( ; it!=it_e; ++it ) {
	std::cout << *it << " ";
}

//write an alg to delete all 2's from vector<int>
// exersize

// owning semantics - all STL containers
std::vector<C> v;
for( int i=0; i<100; ++i ) {
	C c( i ); // client's object
	v.push_back( c ); // copy to container
	c = C(2); // does not change value in vector - STL OWNS data
}


// vector of pointers to objects (possibly base class pointers)
// vector owns pointers - not data. Now client is responsible for deletion
int main() {
	std::vector<C*> v;
	for( int i=0; i<100; ++i ) {
		C* pc = new C( i );
		v.push_back( pc );
	}
	
	std::vector<int>::iterator 
		it = v.begin(),
		it_e = v.end();

	for ( ; it!=it_e; ++it ) {
		std::cout << **it << " ";
	}
	
    // container is responsible for pointers only, so objects have to be deleted manually
	for ( ; it!=it_e; ++it ) {
		delete *it;
	}

}




//assume C::operator<() is defined
std::vector<C*> v;
for( int i=0; i<100; ++i ) {
	C* pc = new C( rand()%1000 );
	v.push_back( pc );
}
std::sort( v.begin(), v.end() ); // will sort addresses


//solution 
bool sort_C( C const* p1, C const* p2 ) {
	return *p1 < *p2;
}
std::sort( v.begin(), v.end(), sort_C );
// there is a better method - later



//print vector in reverse!!!
//
//  ^           ^
//  |           |
//  begin       end

// ugly and error-prone
for ( it = it_e -1; it >= it_b; --it ) { ... }

// correct
//rend        rbegin       
// |          |
// V          V
//  1 2 3 4 5 6
for ( vector<int>::reverse_const_iterator rit = v.rbegin(); 
      rit != v.rend();
	  ++rit )
	  
vector<int> const v; // constant vector
//vector<int>::iterator it = v.begin();  // error, read-write iterator to const memory
// int const i;
// int * p = &i;
	  
for ( vector<int>::const_iterator it = v.begin(); 
      it != v.end();
	  ++it )
{
    std::cout << *it << " ";
}


vector<int>::const_iterator it1;
++it;
// *it = 5;
// like:   int const * p;

// note that
const vector<int>::iterator it1;
//++it1;
*it1 = 5;
// like:   int * const p;






// using iterators - identical syntax for all STL containers
std::vector<std::string>::iterator it2;
for (it2 = cont2.begin(); it2 != cont2.end(); ++it2)
  std::cout << *it << "  ";

std::list<std::string>::iterator it;
for (it = cont1.begin(); it != cont1.end(); ++it)
  std::cout << *it << "  ";
  
  
//std::vector_iterator<int> iter;

typedef std::list<int> MyContainerT;

MyContainerT v;
MyContainerT::iterator iter;



// more ctors
std::vector<int> v1;
std::vector<int> v2( v1 );
//std::vector<double> v3( v1 );
std::vector<double> v3( v1.begin(), v1.end() );


class C {
	public:
    static const int i = 10;
	static int foo() return 111; }
    typedef double MT;
};
C::i
C::foo
C::MT


template <typename T>
void print4(T& v)
{
  //T::iterator iter;
  typename T::iterator iter;
  for (iter = v.begin(); iter != v.end(); ++iter)
    std::cout << *iter << "  ";
  std::cout << std::endl;
}

std::vector< std::vector <int> >
std::vector< std::vector <int>> // MS C++03 and C++11 are OK




struct C_pointer_comparison {
    bool operator() ( C const * p1, C const * p2 ) const {
        return *p1 < *p2;
    }
};

C_pointer_comparison op;
op( p1, p2 );

#include <limits>

// 2-dimensional vector initialized to infinty
int n;
std::cin >> n;
std::vector< std::vector< int> > 
   v( n, std::vector< int>( 
      n, std::numeric_limits<int>::infinity()  // infinity is C++11
	  ) 
	);
	
// c++03
//m = std::numeric_limits<int>::max()
//m+1 is -1 ???
//std::numeric_limits<int>::min()
//// c++11
//std::numeric_limits<int>::infinity()
//m+1 is infinty (not imlemented in GNU 7)
//std::numeric_limits<double>::infinity()
//m+1 is infinty (IS imlemented  in GNU 7)







std::deque<int> d;
d.push_back( 1 );  
d.push_back( 2 );
d.push_back( 3 );
d.push_front( 4 );
d.push_front( 5 );
d.push_front( 6 );
6 5 4 1 2 3
0 1 2 3 4 5
// deque does not invalidate - false

-3 -2 -1

1 2 3 4                       5 6 7 
^

//d.reserve()


std::list<int> lst;

lst.push_back();
lst.push_front();
// lst[i]

1 2 3
lst.insert( lst.end(), 5 );
1 2 3 5
lst.insert( lst.begin(), 0 );
0 1 2 3 5

// lst.reserve() resize()