#include <iostream>
#include <chrono>
#include <random>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <set>

const int Size = 10000;

int rnd(int max) { static auto engine = std::default_random_engine(); return engine() % max; }

// really simple RAII timer utility
struct timer
{
   typedef std::chrono::high_resolution_clock clock;
   clock::time_point start_;

   timer() : start_(clock::now()) {}
   ~timer()
   {
      std::cout 
         << ((double)std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start_).count() / 1000.)
         << "ms" << std::endl;
   }
};

//
// std::vector<> specialisations
//

// insert into a sorted vector<>
void insert(std::vector<int> & c, int val)
{
   // first find the insertion point (naiively)
   auto it = c.begin(); 
   while (it != c.end() && *it <= val) ++it;

   c.insert(it, val);
}

// remove from a sorted vector<>
bool remove(std::vector<int> & c, int val)
{
   // first find the insertion point (naiively)
   auto it = c.begin(); 
   while (it != c.end() && *it < val) ++it;

   if (it == c.end() || *it != val) return false;
   c.erase(it);
   return true;
}

bool isSorted(std::vector<int> & c)
{
   int prev = -1;
   for (auto v : c)
   {
      if (v < prev) return false;
      prev = v;
   }
   return true;
}

//
// std::deque<> specialisations
//

// insert into a sorted deque<>
void insert(std::deque<int> & c, int val)
{
   // first find the insertion point (naiively)
   auto it = c.begin(); 
   while (it != c.end() && *it <= val) ++it;

   c.insert(it, val);
}

// remove from a sorted deque<>
bool remove(std::deque<int> & c, int val)
{
   // first find the insertion point (naiively)
   auto it = c.begin(); 
   while (it != c.end() && *it < val) ++it;

   if (it == c.end() || *it != val) return false;
   c.erase(it);
   return true;
}

bool isSorted(std::deque<int> & c)
{
   int prev = -1;
   for (auto v : c)
   {
      if (v < prev) return false;
      prev = v;
   }
   return true;
}

//
// std::set<> specialisations
//

void insert(std::set<int> & c, int val)
{
   c.insert(val);
}

bool remove(std::set<int> & c, int val)
{
   return 1 == c.erase(val);
}

bool isSorted(std::set<int> &)
{
	// set is always sorted
   return true;
}

//
// test drivers
//

template <typename C>
void test(std::string name, std::vector<int> v, C & c)
{
   {
      std::cout << "testing " << name << "..." << std::endl;
      timer t;

      for (auto val : v) insert(c, val);
      
#if false
	  std::cerr << "WARNING: this line should not be printed in the actual test" << std::endl;
	  // verify that inserts all worked - dont do this in the actual test though!
	  if (c.size() != Size) std::cerr << "ERROR: output is not big enough!" << std::endl;
	  if (!isSorted(c)) std::cout << "ERROR: output is not sorted!" << std::endl;
#endif

      for (auto val : v)
      {
      	auto removed = remove(c, val);
      	if (!removed) std::cerr << "not removed!" << std::endl;
      }
   }
   if (0 != c.size()) std::cerr << "ERROR: elements not removed!" << std::endl;
}

int main()
{
   // assume we are always inserting unique numbers
   // (this should bias towards std::vector because it will ensure the
   // std::set is as large as possible)
   std::cout << "shuffling numbers..." << std::endl;
   std::vector<int> v;
   for (auto i = 0; i < Size; i++) v.push_back(i);
   std::random_shuffle(v.begin(), v.end());
   
   std::cout << "numbers shuffled. first few numbers:";
   for (auto i = 0; i < 10; i++) std::cout << " " << v[i];
   std::cout << std::endl;

   // vector<> test
   {
      std::vector<int> sorted;
      sorted.reserve(Size); // ensure we arent reallocating unnecessarily

      test("vector", v, sorted);
   }

   // deque<> test
   {
      std::deque<int> sorted;
      test("deque", v, sorted);
   }
   
   // set<> test
   {
      std::set<int> sorted;

      test("set", v, sorted);
   }
}
