#include <iostream>
#include <string>
#include <cstring>
#include <tbb/task_scheduler_init.h>
#include <tbb/concurrent_hash_map.h>
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>

#define N 10

using namespace std;
using namespace tbb;

class hash_func {
	public:
		static size_t hash( const string &s)
		{
			return s.size();
		}

		static bool equal( const string &s1, const string &s2)
		{
			return s1 == s2;
		}
};

typedef concurrent_hash_map<string,int,hash_func> CHM;
/*
 * For default data types like std::string, an explicit hash function
 * is not required. So the above typedef could simply be written as:
 * typedef concurrent_hash_map<string,int> CHM;
 */

void read_words(string *v)
{
	string s;

	for (int i=0; i<N; i++) {
		cin >> s;
		v[i]=s;
	}
}

/*
 * Function object for counting occurrences of strings.
 */
class FObject {
	public:
		CHM &chm;

		FObject( CHM &chm_ ) : chm(chm_) {}

		void operator()( const blocked_range<string *> r ) const
		{
			for ( string *ptr=r.begin(); ptr!=r.end(); ++ptr ) {
				CHM::accessor chm_acc;
				chm.insert( chm_acc, *ptr );
				chm_acc->second += 1;
			}
		}
};

int main()
{
	task_scheduler_init init(-1);

	string words[N];
	CHM chm;
	CHM::accessor chm_acc;
	pair<CHM::const_iterator, CHM::const_iterator> chm_pair;

	read_words( words );

	parallel_for( blocked_range<string *>( words, words+N, 3 ), FObject(chm) );

	/*
	 * equal_range() has less overhead than find() and count()
	 */
	chm_pair = chm.equal_range( words[0] );
	if ( chm_pair.second != chm.end() ) {
		cout << "Found " << (*chm_pair.second).second << " (used .equal_range())" << endl;
	}

	/*
	 * count() has less overhead than find()
	 */
	if ( chm.count(words[0]) ) {
		cout << "Found " << words[0] << " (used .count())" << endl;
	}

	/*
	 * use find() only when really required
	 */
	if ( chm.find(chm_acc,"13") ) {
		cout << "Found 13 (used .find())" << endl;

		/*
		 * "13" can be deleted in two ways:
		 * 1. use accessor
		 * chm.erase(chm_acc);
		 *
		 * 2. release the read-lock from find and erase using the key:
		 * chm_acc.release();
		 * chm.erase("13");
		 *
		 * if accessor is not released, it will lead to deadlock between
		 * the read-only lock of find vs read-write lock of erase.
		 */
		cout << "Deleting 13" << endl;
		chm.erase(chm_acc);

		if ( chm.find(chm_acc,"on") ) {
			cout << "   Found on" << endl;
		}
	}

	chm.erase(words[0]);
	chm.erase(words[1]);

	cout << "Printing all pairs" << endl;
	for (CHM::iterator it = chm.begin(); it != chm.end(); it++) {
		cout << "\t" << it->first << " -> " << it->second << endl;
	}

	return 0;
}

