/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2009, 2010 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$ 
 */

#include "StlAdvancedFeatures.h"

static void usage();

class StlAdvancedFeaturesExample
{
public:
	typedef map<int, int> m_int_t;
	typedef ptint tpint;
	~StlAdvancedFeaturesExample();
	StlAdvancedFeaturesExample(void *param1);
	void run()
	{
		arbitrary_object_storage();
		storing_std_strings();
		secondary_containers();
		char_star_string_storage();
		arbitray_sequence_storage();
		bulk_retrieval_read();
		primitive();
		queue_stack();
	}
private:

	// Store primitive types of data into dbstl containers.
	void primitive();

	// Use std::queue and std::stack as adapters, and dbstl::db_vector 
	// as container, to make a queue and a stack.
	void queue_stack();

	// Use two ways to store an object of arbitrary length. The object
	// contains some varying length members, char* string for example.
	void arbitrary_object_storage();

	// Store std::string types of strings.
	void storing_std_strings();

	// Open a secondary db H and associate it with an exisitng db handle
	// which is being used by a container C1, then use H to create another
	// container C2, verify we can get C1's data via C2.
	// This technique works for all types of db and containers.
	void secondary_containers();

	// Direct storage of char* strings.
	void char_star_string_storage();

	// Storage of arbitrary element type of sequence.
	void arbitray_sequence_storage();

	// Reading with bulk retrieval flag.
	void bulk_retrieval_read();


	int flags, setflags, explicit_txn, test_autocommit, n;
	DBTYPE dbtype;
	dm_int_t::difference_type oddcnt;
	Db *db3, *dmdb1, *dmdb2, *dmmdb1, *dmmdb2, *dmsdb1, 
	    *dmsdb2, *dmmsdb1, *dmmsdb2, *dbstrv, *pquedb, *quedb;
	Db *dbp3;
	Db *dmdb6;
	Db *dbp3sec;
	Db *dmmdb4, *dbstrmap;
	Db *dmstringdb;
	Db *dbprim;
	DbEnv *penv;
	u_int32_t dboflags;

	map<int, int> m1;
	multimap<int, int> mm1;
};

StlAdvancedFeaturesExample::~StlAdvancedFeaturesExample()
{
}

StlAdvancedFeaturesExample::StlAdvancedFeaturesExample(void *param1)
{
	check_expr(param1 != NULL);
	TestParam *param = (TestParam*)param1;
	TestParam *ptp = param;

	flags = 0, setflags = 0, explicit_txn = 1, test_autocommit = 0;
	dbtype = DB_BTREE;
	penv = param->dbenv;
	dmdb1 = dmdb2 = dmmdb1 = dmmdb2 = dmsdb1 = dmsdb2 = dmmsdb1 = 
	    dmmsdb2 = dbstrv = NULL;
	flags = param->flags;
	dbtype = param->dbtype;
	setflags = param->setflags;
	test_autocommit = param->test_autocommit;
	explicit_txn = param->explicit_txn;
	dboflags = ptp->dboflags;
	n = 10;

	dmdb1 = dbstl::open_db(penv, "db_map.db", 
	    dbtype, DB_CREATE | ptp->dboflags, 0);
	dmdb2 = dbstl::open_db(penv, "db_map2.db", 
	    dbtype, DB_CREATE | ptp->dboflags, 0);
	dmdb6 = dbstl::open_db(penv, "db_map6.db", 
	    dbtype, DB_CREATE | ptp->dboflags, 0);

	dmmdb1 = dbstl::open_db(penv, 
	    "db_multimap.db", dbtype, DB_CREATE | ptp->dboflags, DB_DUP);
	dmmdb2 = dbstl::open_db(penv, 
	    "db_multimap2.db", dbtype, DB_CREATE | ptp->dboflags, DB_DUP);
	
	dmsdb1 = dbstl::open_db(penv, "db_set.db", 
	    dbtype, DB_CREATE | ptp->dboflags, 0);
	dmsdb2 = dbstl::open_db(penv, "db_set2.db", 
	    dbtype, DB_CREATE | ptp->dboflags, 0);
	
	dmmsdb1 = dbstl::open_db(penv, 
	    "db_multiset.db", dbtype, DB_CREATE | ptp->dboflags, DB_DUP);
	dmmsdb2 = dbstl::open_db(penv, 
	    "db_multiset2.db", dbtype, DB_CREATE | ptp->dboflags, DB_DUP);

	dbstrv = dbstl::open_db(penv, "dbstr.db", 
	    DB_RECNO, DB_CREATE | ptp->dboflags, DB_RENUMBER);
	dbp3sec = dbstl::open_db(penv, "db_map_sec.db", 
	    dbtype, DB_CREATE | ptp->dboflags, DB_DUP);

	dmmdb4 = dbstl::open_db(penv, 
	    "db_multimap4.db", dbtype, DB_CREATE | dboflags, DB_DUPSORT);
	dbstrmap = dbstl::open_db(penv, "dbstrmap.db", 
	    DB_BTREE, DB_CREATE, 0);

	dmstringdb = dbstl::open_db(penv, "db_map_stringdb.db", 
	    dbtype, DB_CREATE | dboflags, 0);
	db3 = dbstl::open_db(penv, "db3.db", 
	    DB_RECNO, DB_CREATE | ptp->dboflags, DB_RENUMBER);

	// NO DB_RENUMBER needed 
	quedb = dbstl::open_db(penv, "dbquedb.db", 
	    DB_RECNO, DB_CREATE | ptp->dboflags | DB_THREAD, 0);

	pquedb = dbstl::open_db(penv, "dbpquedb.db", 
	    DB_RECNO, DB_CREATE | ptp->dboflags | DB_THREAD, DB_RENUMBER);
	dbprim = dbstl::open_db(penv, "dbprim.db",
	    DB_RECNO, DB_CREATE | ptp->dboflags | DB_THREAD, DB_RENUMBER);

	dbp3 = dbstl::open_db(penv, "dbp3.db", 
	    dbtype, DB_CREATE | ptp->dboflags, 0);
	
}

void StlAdvancedFeaturesExample::arbitrary_object_storage()
{
	int i;

	if (explicit_txn)
		begin_txn(0, penv);
	// varying length objects test
	cout<<"\nArbitary object storage using Dbt..\n";
	
	rand_str_dbt smsdbt;
	DbstlDbt dbt, dbtmsg;
	string msgstr;
	SMSMsg *smsmsgs[10];

	dbtmsg.set_flags(DB_DBT_USERMEM);
	dbt.set_data(DbstlMalloc(256));
	dbt.set_flags(DB_DBT_USERMEM);
	dbt.set_ulen(256);
	db_map<int, DbstlDbt> msgmap(dbp3, penv);
	for (i = 0; i < 10; i++) {
		smsdbt(dbt, msgstr, 10, 200);
		SMSMsg *pmsg = SMSMsg::make_sms_msg(time(NULL), 
		    (char *)dbt.get_data(), i);
		smsmsgs[i] = SMSMsg::make_sms_msg(time(NULL), 
		    (char *)dbt.get_data(), i);
		dbtmsg.set_data(pmsg);
		dbtmsg.set_ulen((u_int32_t)(pmsg->mysize));
		dbtmsg.set_size((u_int32_t)(pmsg->mysize));
		dbtmsg.set_flags(DB_DBT_USERMEM);
		msgmap.insert(make_pair(i, dbtmsg));
		free(pmsg);
		memset(&dbtmsg, 0, sizeof(dbtmsg));
	}
	dbtmsg.set_data(NULL);

	SMSMsg *psmsmsg;
	for (i = 0; i < 10; i++) {
		db_map<int, DbstlDbt>::data_type_wrap msgref = msgmap[i];
		psmsmsg = (SMSMsg *)msgref.get_data();
		check_expr(memcmp(smsmsgs[i], psmsmsg, 
		    smsmsgs[i]->mysize) == 0);
	}

	i = 0;
	for (db_map<int, DbstlDbt>::iterator msgitr = 
	    msgmap.begin(ReadModifyWriteOption::
	    read_modify_write()); msgitr != msgmap.end(); ++msgitr, i++) {
		db_map<int, DbstlDbt>::reference smsmsg = *msgitr;
		(((SMSMsg*)(smsmsg.second.get_data())))->when = time(NULL);
		smsmsg.second._DB_STL_StoreElement();
		
	}

	for (i = 0; i < 10; i++) 
		free(smsmsgs[i]);
	
	msgmap.clear();


	cout<<"\nArbitary object(sparse, varying length) storage support using registered callbacks.\n";
	db_map<int, SMSMsg2> msgmap2(dbp3, penv);
	SMSMsg2 smsmsgs2[10];
	DbstlElemTraits<SMSMsg2>::instance()->set_copy_function(SMSMsgCopy);
	DbstlElemTraits<SMSMsg2>::instance()->set_size_function(SMSMsgSize);
	DbstlElemTraits<SMSMsg2>::instance()->set_restore_function(SMSMsgRestore);
	// use new technique to store varying length and inconsecutive objs
	for (i = 0; i < 10; i++) {
		smsdbt(dbt, msgstr, 10, 200);
		SMSMsg2 msg2(time(NULL), msgstr.c_str(), i);
		smsmsgs2[i] = msg2;
		
		msgmap2.insert(make_pair(i, msg2));
	 
	}
	
	// check that retrieved data is identical to stored data
	SMSMsg2 tmpmsg2;
	for (i = 0; i < 10; i++) {
		tmpmsg2 = msgmap2[i];
		check_expr(smsmsgs2[i] == tmpmsg2);
	}
	for (db_map<int, SMSMsg2>::iterator msgitr = 
	    msgmap2.begin(ReadModifyWriteOption::
	    read_modify_write()); msgitr != msgmap2.end(); msgitr++) {
		    db_map<int, SMSMsg2>::reference smsmsg = *msgitr;
		smsmsg.second.when = time(NULL);
		smsmsg.second._DB_STL_StoreElement();
		
	}
	msgmap2.clear();
	if (explicit_txn)
		commit_txn(penv);
} // arbitrary_object_storage

// std::string persistent test.
void StlAdvancedFeaturesExample::storing_std_strings()
{	
	string kstring = "hello world", *sstring = new string("hi there");
	if (explicit_txn)
		begin_txn(0, penv);

	db_map<string, string> pmap(dmstringdb, NULL);

	pmap[kstring] = *sstring + "!";
	*sstring = pmap[kstring];
	map<string, string> spmap;
	spmap.insert(make_pair(kstring, *sstring));
	cout<<"sstring append ! is : "<<pmap[kstring]
	    <<" ; *sstring is : "<<*sstring;
	delete sstring;
	for (db_map<string, string>::iterator ii = pmap.begin();
	    ii != pmap.end();
	    ++ii) {
		cout << (*ii).first << ": " << (*ii).second << endl;
	} 
	close_db(dmstringdb);
	
	dmstringdb = dbstl::open_db(penv, "db_map_stringdb.db", 
	    dbtype, DB_CREATE | dboflags, 0);
	db_map<string, string> pmap2(dmstringdb, NULL);
	for (db_map<string, string>::iterator ii = pmap2.begin();
	    ii != pmap2.end(); ++ii) {
		cout << (*ii).first << ": " << (*ii).second << endl;
		// assert key/data pair set equal
		check_expr((spmap.count(ii->first) == 1) && 
		    (spmap[ii->first] == ii->second));
	} 
	if (explicit_txn)
		commit_txn(penv);

	db_vector<string> strvctor(10);
	vector<string> sstrvctor(10);
	for (int i = 0; i < 10; i++) {
		strvctor[i] = "abc";
		sstrvctor[i] = strvctor[i];
	}
	check_expr(is_equal(strvctor, sstrvctor));
}

void StlAdvancedFeaturesExample::secondary_containers()
{
	int i;

	if (explicit_txn)
		begin_txn(0, penv);
	// test secondary db
	cout<<"\ndb container backed by secondary database.";
	
	dbp3->associate(dbstl::current_txn(penv), dbp3sec, 
	    get_dest_secdb_callback, DB_CREATE);
	typedef db_multimap<int, BaseMsg>  sec_mmap_t;
	sec_mmap_t secmmap(dbp3sec, penv);// index "to" field
	db_map<int, BaseMsg> basemsgs(dbp3, penv);
	basemsgs.clear();
	BaseMsg tmpmsg;
	multiset<BaseMsg> bsmsgs, bsmsgs2;
	multiset<BaseMsg>::iterator bsitr1, bsitr2;
	// populate primary and sec db
	for (i = 0; i < 10; i++) {
		tmpmsg.when = time(NULL);
		tmpmsg.to = 100 - i % 3;// sec index multiple
		tmpmsg.from = i + 20;
		bsmsgs.insert( tmpmsg);
		basemsgs.insert(make_pair(i, tmpmsg));

	}
	check_expr(basemsgs.size() == 10);
	// check retrieved data is identical to those fed in
	sec_mmap_t::iterator itrsec;
	for (itrsec = secmmap.begin(
	    ReadModifyWriteOption::no_read_modify_write(), true);
	    itrsec != secmmap.end(); itrsec++) {
		bsmsgs2.insert(itrsec->second);
	}
	for (bsitr1 = bsmsgs.begin(), bsitr2 = bsmsgs2.begin(); 
	    bsitr1 != bsmsgs.end() && bsitr2 != bsmsgs2.end(); 
	    bsitr1++, bsitr2++) {
		check_expr(*bsitr1 == *bsitr2);
	}
	check_expr(bsitr1 == bsmsgs.end() && bsitr2 == bsmsgs2.end());

	// search using sec index, check the retrieved data is expected
	// and exists in bsmsgs
	check_expr(secmmap.size() == 10);
	pair<sec_mmap_t::iterator, sec_mmap_t::iterator> secrg = 
	    secmmap.equal_range(98);

	for (itrsec = secrg.first; itrsec != secrg.second; itrsec++) {
		check_expr(itrsec->second.to == 98 && 
		    bsmsgs.count(itrsec->second) > 0);
	}
	// delete via sec db
	size_t nersd = secmmap.erase(98);
	check_expr(10 - nersd == basemsgs.size());
	secrg = secmmap.equal_range(98);
	check_expr(secrg.first == secrg.second);

	if (explicit_txn)
		dbstl::commit_txn(penv);
	 
} // secondary_containers

void StlAdvancedFeaturesExample::char_star_string_storage()
{
	int i;
	// Varying length data element storage/retrieval
	cout<<"\nchar*/wchar_t* string storage support...\n";
	
	if (explicit_txn)
		dbstl::begin_txn(0, penv);
	// Use Dbt to wrap any object and store them. This is rarely needed, 
	// so this piece of code is only for test purpose.
	db_vector<DbstlDbt> strv(dbstrv, penv);
	vector<string> strsv;
	vector<DbstlDbt> strvdbts;
	strv.clear();
	
	int strlenmax = 256, strlenmin = 64;
	string str;
	DbstlDbt dbt;
	rand_str_dbt rand_str_maker;
	dbt.set_flags(DB_DBT_USERMEM);
	dbt.set_data(DbstlMalloc(strlenmax + 10));
	dbt.set_ulen(strlenmax + 10);

	for (int jj = 0; jj < 10; jj++) {
		rand_str_maker(dbt, str, strlenmin, strlenmax);
		strsv.push_back(str);
		strv.push_back(dbt);
	}
	
	cout<<"\nstrings:\n";
	for (i = 0; i < 10; i++) {
		db_vector<DbstlDbt>::value_type_wrap elemref = strv[i];
		strvdbts.push_back(elemref);
		printf("\n%s\n%s",  (char*)(strvdbts[i].get_data()), 
		    strsv[i].c_str());
		check_expr(strcmp((char*)(elemref.get_data()), 
		    strsv[i].c_str()) == 0);
		check_expr(strcmp((char*)(strvdbts[i].get_data()), 
		    strsv[i].c_str()) == 0);
	}
	strv.clear();
	
	if (explicit_txn) {
		dbstl::commit_txn(penv);
		dbstl::begin_txn(0, penv);
	}

	// Use ordinary way to store strings.
	TCHAR cstr1[32], cstr2[32], cstr3[32];
	strcpy(cstr1, "abc");
	strcpy(cstr2, "defcd");
	strcpy(cstr3, "edggsefcd");

	typedef db_map<int, TCHAR*, ElementHolder<TCHAR*> > strmap_t;
	strmap_t strmap(dmdb6, penv);
	strmap.clear();
	strmap.insert(make_pair(1, cstr1));
	strmap.insert(make_pair(2, cstr2));
	strmap.insert(make_pair(3, cstr3));
	cout<<"\n strings in strmap:\n";
	for (strmap_t::const_iterator citr = strmap.begin(); 
	    citr != strmap.end(); citr++)
		cout<<(*citr).second<<'\t';
	cout<<strmap[1]<<strmap[2]<<strmap[3];
	TCHAR cstr4[32], cstr5[32], cstr6[32];
	_tcscpy(cstr4, strmap[1]);
	_tcscpy(cstr5, strmap[2]);
	_tcscpy(cstr6, strmap[3]);
	_tprintf(_T("%s, %s, %s"), cstr4, cstr5, cstr6);
	strmap_t::value_type_wrap::second_type vts = strmap[1];
	using_charstr(vts);
	vts._DB_STL_StoreElement();
	_tcscpy(cstr4, _T("hello world"));
	vts = cstr4;
	vts._DB_STL_StoreElement();
	cout<<"\n\nstrmap[1]: "<<strmap[1];
	check_expr(_tcscmp(strmap[1], cstr4) == 0);
	vts[0] = _T('H');// it is wrong to do it this way
	vts._DB_STL_StoreElement();
	check_expr(_tcscmp(strmap[1], _T("Hello world")) == 0);
	TCHAR *strbase = vts._DB_STL_value();
	strbase[6] = _T('W');
	vts._DB_STL_StoreElement();
	check_expr(_tcscmp(strmap[1], _T("Hello World")) == 0);
	strmap.clear();

	typedef db_map<const char *, const char *, 
	    ElementHolder<const char *> > cstrpairs_t;
	cstrpairs_t strpairs(dmdb6, penv);
	strpairs["abc"] = "def";
	strpairs["ghi"] = "jkl";
	strpairs["mno"] = "pqrs";
	strpairs["tuv"] = "wxyz";
	cstrpairs_t::const_iterator ciitr;
	cstrpairs_t::iterator iitr;
	for (ciitr = strpairs.begin(), iitr = strpairs.begin(); 
	    iitr != strpairs.end(); ++iitr, ++ciitr) {
		cout<<"\n"<<iitr->first<<"\t"<<iitr->second;
		cout<<"\n"<<ciitr->first<<"\t"<<ciitr->second;
		check_expr(strcmp(ciitr->first, iitr->first) == 0 && 
		    strcmp(ciitr->second, iitr->second) == 0);
	}

	typedef db_map<char *, char *, ElementHolder<char *> > strpairs_t;
	typedef std::map<string, string> sstrpairs_t;
	sstrpairs_t sstrpairs2;
	strpairs_t strpairs2;
	rand_str_dbt randstr;
	
	for (i = 0; i < 100; i++) {
		string rdstr, rdstr2;
		randstr(dbt, rdstr);
		randstr(dbt, rdstr2);
		strpairs2[(char *)rdstr.c_str()] = (char *)rdstr2.c_str();
		sstrpairs2[rdstr] = rdstr2;
	}
	strpairs_t::iterator itr;
	strpairs_t::const_iterator citr;
	
	for (itr = strpairs2.begin(); itr != strpairs2.end(); ++itr) {
		check_expr(strcmp(strpairs2[itr->first], itr->second) == 0);
		check_expr(string(itr->second) == 
		    sstrpairs2[string(itr->first)]);
		strpairs_t::value_type_wrap::second_type&secref = itr->second;
		std::reverse((char *)secref, (char *)secref + strlen(secref));
		secref._DB_STL_StoreElement();
		std::reverse(sstrpairs2[itr->first].begin(), 
		    sstrpairs2[itr->first].end());
	}

	check_expr(strpairs2.size() == sstrpairs2.size());
	for (citr = strpairs2.begin(
	    ReadModifyWriteOption::no_read_modify_write(), 
	    true, BulkRetrievalOption::bulk_retrieval()); 
	    citr != strpairs2.end(); ++citr) {
		check_expr(strcmp(strpairs2[citr->first], citr->second) == 0);
		check_expr(string(citr->second) == 
		    sstrpairs2[string(citr->first)]);
	}

	
	if (explicit_txn) 
		dbstl::commit_txn(penv);

	db_vector<const char *, ElementHolder<const char *> > csvct(10);
	vector<const char *> scsvct(10);
	const char *pconststr = "abc";
	for (i = 0; i < 10; i++) {
		scsvct[i] = pconststr;
		csvct[i] = pconststr;
		csvct[i] = scsvct[i];
		// scsvct[i] = csvct[i]; assignment won't work because scsvct 
		// only stores pointer but do not copy the sequence, thus it 
		// will refer to an invalid pointer when i changes.
	}
	for (i = 0; i < 10; i++) {
		check_expr(strcmp(csvct[i], scsvct[i]) == 0);
		cout<<endl<<(const char *)(csvct[i]);
	}

	db_vector<const wchar_t *, ElementHolder<const wchar_t *> > wcsvct(10);
	vector<const wchar_t *> wscsvct(10);
	const wchar_t *pconstwstr = L"abc";
	for (i = 0; i < 10; i++) {
		wscsvct[i] = pconstwstr;
		wcsvct[i] = pconstwstr;
		wcsvct[i] = wscsvct[i];
		// scsvct[i] = csvct[i]; assignment won't work because scsvct 
		// only stores pointer but do not copy the sequence, thus it 
		// will refer to an invalid pointer when i changes.
	}
	for (i = 0; i < 10; i++) {
		check_expr(wcscmp(wcsvct[i], wscsvct[i]) == 0);

	}
	
} // char_star_string_storage

void StlAdvancedFeaturesExample::arbitray_sequence_storage()
{
	int i, j;

	if (explicit_txn) 
		dbstl::begin_txn(0, penv);
	// storing arbitary sequence test .  
	cout<<endl<<"Arbitary type of sequence storage support.\n";
	RGBB *rgbs[10], *prgb1, *prgb2;
	typedef db_map<int, RGBB *, ElementHolder<RGBB *> > rgbmap_t;
	rgbmap_t rgbsmap(dmdb6, penv);
	
	map<int, RGBB *> srgbsmap;

	DbstlElemTraits<RGBB>::instance()->set_sequence_len_function(rgblen);
	DbstlElemTraits<RGBB>::instance()->set_sequence_copy_function(rgbcpy);
	// populate srgbsmap and rgbsmap
	for (i = 0; i < 10; i++) {
		n = abs(rand()) % 10 + 2;
		rgbs[i] = new RGBB[n];
		memset(&rgbs[i][n - 1], 0, sizeof(RGBB));//make last element 0
		for (j = 0; j < n - 1; j++) {
			rgbs[i][j].r_ = i + 128;
			rgbs[i][j].g_ = 256 - i;
			rgbs[i][j].b_ = 128 - i;
			rgbs[i][j].bright_ = 256 / (i + 1);
			
		}
		rgbsmap.insert(make_pair(i, rgbs[i]));
		srgbsmap.insert(make_pair(i, rgbs[i]));
	}

	// retrieve and assert equal, then modify and store
	for (i = 0; i < 10; i++) {
		rgbmap_t::value_type_wrap::second_type rgbelem = rgbsmap[i];
		prgb1 = rgbelem;
		check_expr(memcmp(prgb1, prgb2 = srgbsmap[i], 
		    (n = (int)rgblen(srgbsmap[i])) * sizeof(RGBB)) == 0);
		for (j = 0; j < n - 1; j++) {
			prgb1[j].r_ = 256 - prgb1[j].r_;
			prgb1[j].g_ = 256 - prgb1[j].g_;
			prgb1[j].b_ = 256 - prgb1[j].b_;
			prgb2[j].r_ = 256 - prgb2[j].r_;
			prgb2[j].g_ = 256 - prgb2[j].g_;
			prgb2[j].b_ = 256 - prgb2[j].b_;
		}
		rgbelem._DB_STL_StoreElement();
	}

	// retrieve again and assert equal
	for (i = 0; i < 10; i++) {
		rgbmap_t::value_type_wrap::second_type rgbelem = rgbsmap[i];
		// Can't use rgbsmap[i] here because container::operator[] is 
		// an temporary value.;
		prgb1 = rgbelem;
		check_expr(memcmp(prgb1, prgb2 = srgbsmap[i], 
		    sizeof(RGBB) * rgblen(srgbsmap[i])) == 0);
	}
	
	rgbmap_t::iterator rmitr;
	map<int, RGBB *>::iterator srmitr;

	for (rmitr = rgbsmap.begin();
	    rmitr != rgbsmap.end(); ++rmitr) {
		rgbmap_t::value_type_wrap::second_type 
		    rgbelem2 = (*rmitr).second;
		prgb1 = (*rmitr).second;
		srmitr = srgbsmap.find(rmitr->first);
		rmitr.refresh();
	}

	for (i = 0; i < 10; i++)
		delete []rgbs[i];
	if (explicit_txn)
		dbstl::commit_txn(penv);
} // arbitray_sequence_storage

void StlAdvancedFeaturesExample::bulk_retrieval_read()
{

	int i;
	
	typedef db_map<int, sms_t> smsmap_t;
	smsmap_t smsmap(dmdb6, penv);
	map<int, sms_t> ssmsmap;
	if (explicit_txn) 
		dbstl::begin_txn(0, penv);
		
	cout<<"\nBulk retrieval support:\n";
	sms_t smsmsg;
	time_t now;
	smsmap.clear();
	for (i = 0; i < 2008; i++) {
		smsmsg.from = 1000 + i;
		smsmsg.to = 10000 - i;
		smsmsg.sz = sizeof(smsmsg);
		time(&now);
		smsmsg.when = now;
		ssmsmap.insert(make_pair(i, smsmsg));
		smsmap.insert(make_pair(i, smsmsg));
	}

	// bulk retrieval test. 
	map<int, sms_t>::iterator ssmsitr = ssmsmap.begin();
	i = 0;
	const smsmap_t &rosmsmap = smsmap;
	smsmap_t::const_iterator smsitr;
	for (smsitr = rosmsmap.begin(
	    BulkRetrievalOption::bulk_retrieval());
	    smsitr != smsmap.end(); i++) {
		// The order may be different, so if the two key set are 
		// identical, it is right.
		check_expr((ssmsmap.count(smsitr->first) == 1)); 
		check_expr((smsitr->second == ssmsmap[smsitr->first]));
		if (i % 2)
			smsitr++;
		else 
			++smsitr; // Exercise both pre/post increment.
		if (i % 100 == 0)
			smsitr.set_bulk_buffer(
			    (u_int32_t)(smsitr.get_bulk_bufsize() * 1.1));
	}

	smsmap.clear();
	ssmsmap.clear();

	// Using db_vector. when moving its iterator sequentially to end(),
	// bulk retrieval works, if moving randomly, it dose not function
	// for db_vector iterators. Also, note that we can create a read only
	// iterator when using db_vector<>::iterator rather than 
	// db_vector<>::const_iterator.
	db_vector<sms_t> vctsms;
	db_vector<sms_t>::iterator itrv;
	vector<sms_t>::iterator sitrv;
	vector<sms_t> svctsms;
	for (i = 0; i < 2008; i++) {
		smsmsg.from = 1000 + i;
		smsmsg.to = 10000 - i;
		smsmsg.sz = sizeof(smsmsg);
		time(&now);
		smsmsg.when = now;
		vctsms.push_back(smsmsg);
		svctsms.push_back(smsmsg);
	}

	for (itrv = vctsms.begin(ReadModifyWriteOption::no_read_modify_write(), 
	    true, BulkRetrievalOption::bulk_retrieval(64 * 1024)), 
	    sitrv = svctsms.begin(), i = 0; itrv != vctsms.end();
	    ++itrv, ++sitrv, ++i) {
		check_expr(*itrv == *sitrv);
		if (i % 100 == 0)
			itrv.set_bulk_buffer(
			    (u_int32_t)(itrv.get_bulk_bufsize() * 1.1));
	}

	if (explicit_txn)
		dbstl::commit_txn(penv);

	
} //bulk_retrieval_read

void StlAdvancedFeaturesExample::primitive()
{

	int i;

	if ( explicit_txn) 
		dbstl::begin_txn(0, penv);
	

	db_vector<int, ElementHolder<int> > ivi(dbprim, penv);
	vector<int> spvi4;
	fill(ivi, spvi4);
	check_expr(is_equal(ivi, spvi4));
	ivi.clear(false);

	db_vector<int, ElementHolder<int> > ivi2(dbprim, penv);
	vector<int> spvi5;
	fill(ivi2, spvi5);
	check_expr(is_equal(ivi2, spvi5));
	size_t vsz = ivi2.size();
	for (i = 0; i < (int)vsz - 1; i++) {
		ivi2[i] += 3;
		ivi2[i]--;
		ivi2[i] <<= 2;
		ivi2[i] = (~ivi2[i] | ivi2[i] & ivi2[i + 1] ^ 
		    (2 * (-ivi2[i + 1]) + ivi2[i]) * 3) / 
		    (ivi2[i] * ivi2[i + 1] + 1);

		spvi5[i] += 3;
		spvi5[i]--;
		spvi5[i] <<= 2;
		spvi5[i] = (~spvi5[i] | spvi5[i] & spvi5[i + 1] ^ 
		    (2 * (-spvi5[i + 1]) + spvi5[i]) * 3) / 
		    (spvi5[i] * spvi5[i + 1] + 1);
	}
	check_expr(is_equal(ivi2, spvi5));
	ivi2.clear(false);

	typedef db_vector<double, ElementHolder<double> > dbl_vct_t;
	dbl_vct_t dvi(dbprim, penv);
	vector<double> dsvi;
	for (i = 0; i < 10; i++) {
		dvi.push_back(i * 3.14);
		dsvi.push_back(i * 3.14);
	}
	check_expr(is_equal(dvi, dsvi));

	dbl_vct_t::iterator ditr;
	vector<double>::iterator sditr;
	for (ditr = dvi.begin(), sditr = dsvi.begin(); 
	    ditr != dvi.end(); ++ditr, ++sditr){
		*ditr *= 2;
		*sditr *= 2;
	}

	check_expr(is_equal(dvi, dsvi));

	for (i = 0; i < 9; i++) {
		dvi[i] /= (-dvi[i] / 3 + 2 * dvi[i + 1]) / (1 + dvi[i]) + 1;
		dsvi[i] /= (-dsvi[i] / 3 + 2 * dsvi[i + 1]) / (1 + dsvi[i]) + 1;
	}

	cout<<"\ndsvi after math operations: \n";
	for (i = 0; i <= 9; i++)
		cout<<dsvi[i]<<"  ";
	for (i = 0; i < 10; i++) {
		cout<<i<<"\t";
		check_expr((int)(dvi[i] * 100000) == (int)(dsvi[i] * 100000));
	}

	if ( explicit_txn) 
		dbstl::commit_txn(penv);
} // primitive

void StlAdvancedFeaturesExample::queue_stack()
{
	int i;

	if ( explicit_txn) 
		dbstl::begin_txn(0, penv);
	// test whether db_vector works with std::queue, 
	// std::priority_queue and std::stack
	cout<<"\ndb_vector working with std::queue\n";
	
	// std::queue test
	intvec_t quev(quedb, penv);
	quev.clear();
	std::queue<ptint, intvec_t> intq(quev);
	std::queue<ptint> sintq;
	check_expr(intq.empty());
	check_expr(intq.size() == 0);
	for (i = 0; i < 100; i++) {
		intq.push(ptint(i));
		sintq.push(i);
		check_expr(intq.front() == 0);
		check_expr(intq.back() == i);
	}
	check_expr(intq.size() == 100);
	for (i = 0; i < 100; i++) {
		check_expr(intq.front() == i);
		check_expr(intq.back() == 99);
		check_expr(intq.front() == sintq.front());
		check_expr(sintq.back() == intq.back());
		sintq.pop();
		intq.pop();
	}
	check_expr(intq.size() == 0);
	check_expr(intq.empty());
	quev.clear();

	// std::priority_queue test
	cout<<"\ndb_vector working with std::priority_queue\n";

	std::vector<ptint> squev;
	intvec_t pquev(pquedb, penv);
	pquev.clear();
	std::priority_queue<ptint, intvec_t, ptintless_ft> 
	    intpq(ptintless, pquev);
	std::priority_queue<ptint, vector<ptint>, ptintless_ft> 
	    sintpq(ptintless, squev);

	check_expr(intpq.empty());
	check_expr(intpq.size() == 0);
	ptint tmppq, tmppq1;
	set<ptint> ptintset;
	for (i = 0; i < 100; i++) {
		for (;;) {// avoid duplicate
			tmppq = rand();
			if (ptintset.count(tmppq) == 0) {
				intpq.push(tmppq);		
				sintpq.push(tmppq);
				ptintset.insert(tmppq);
				break;
			} 
		}

	}
	check_expr(intpq.empty() == false);
	check_expr(intpq.size() == 100);
	for (i = 0; i < 100; i++) {
		tmppq = intpq.top();
		tmppq1 = sintpq.top();
		if (i == 98 && tmppq != tmppq1) {
			tmppq = intpq.top();
		}
		if (i < 98)
			check_expr(tmppq == tmppq1);
		if (i == 97)
			intpq.pop();
		else
			intpq.pop();
		sintpq.pop();
	}
	check_expr(intpq.empty());
	check_expr(intpq.size() == 0);


	// std::stack test
	cout<<"\ndb_vector working with std::stack\n";
	std::stack<ptint, intvec_t> intstk(quev);
	std::stack<ptint> sintstk;
	check_expr(intstk.size() == 0);
	check_expr(intstk.empty());
	for (i = 0; i < 100; i++) {
		intstk.push(ptint(i));
		sintstk.push(ptint(i));
		check_expr(intstk.top() == i);
		check_expr(intstk.size() == (size_t)i + 1);
	}
	
	for (i = 99; i >= 0; i--) {
		check_expr(intstk.top() == ptint(i));
		check_expr(intstk.top() == sintstk.top());
		sintstk.pop();
		intstk.pop();
		check_expr(intstk.size() == (size_t)i);
	}
	check_expr(intstk.size() == 0);
	check_expr(intstk.empty());
	
	// Vector with no handles specified. 
	ptint_vector simple_vct(10);
	vector<ptint> ssvct(10);
	for (i = 0; i < 10; i++) {
		simple_vct[i] = ptint(i);
		ssvct[i] = ptint(i);
	}
	check_expr(is_equal(simple_vct, ssvct));

	if ( explicit_txn)
		dbstl::commit_txn(penv);
	
	return;
} // queue_stack

DbEnv *g_env;
int g_test_start_txn;

int main(int argc, char *argv[])
{
	int c, ret;
	char *envhome = NULL, *mode = NULL;
	int flags = DB_THREAD, setflags = 0, explicit_txn = 0;
	int test_autocommit = 1, totalinsert = 100, verbose = 0;
	DBTYPE dbtype = DB_BTREE;
	int shortest = 50, longest = 200;
	u_int32_t cachesize = 8 * 1024 * 1024;
	DbEnv *penv = NULL;

	TestParam *ptp = new TestParam;

	while ((c = getopt(argc, argv, "T:c:hH:k:l:m:n:r:s:t:v")) != EOF) {
		switch (c) {
			case 'T':
				totalinsert = atoi(optarg);
				break;
			case 'c':
				cachesize = atoi(optarg);
				break;
			case 'h':
				usage();
				return 0;
				break;
			case 'H':
				envhome = strdup(optarg);
				break;
			case 'k':
				shortest = atoi(optarg);
				break;
			case 'l':
				longest = atoi(optarg);
				break;
			case 'm':
				mode = optarg;
				break;
			case 's': // db type for associative containers
				if (*optarg == 'h') // hash db type
					dbtype = DB_HASH;
				else if (*optarg == 'b')
					dbtype = DB_BTREE;
				else
					usage();
				break;
			case 't':
				explicit_txn = 1;
				if (*optarg == 'a') 
					setflags = DB_AUTO_COMMIT;
				else if (*optarg == 'e') // explicit txn
					test_autocommit = 0;
				else 
					usage();
				break;
			case 'v':
				verbose = 1;
				break;
			default:
				usage();
				break;
		}
	}

	if (mode == NULL) 
		flags |= 0;
	else if (*mode == 'c') //cds
		flags |= DB_INIT_CDB;
	else if (*mode == 't')
		flags |= DB_INIT_TXN | DB_RECOVER 
		    | DB_INIT_LOG | DB_INIT_LOCK;
	else
		flags |= 0;

	ptp->explicit_txn = explicit_txn;
	ptp->flags = flags;
	ptp->dbtype = dbtype;
	ptp->setflags = setflags;
	ptp->test_autocommit = test_autocommit;
	ptp->dboflags = DB_THREAD;

	// Call this method before any use of dbstl.
	dbstl_startup();
	penv = new DbEnv(DB_CXX_NO_EXCEPTIONS);
	BDBOP(penv->set_flags(setflags, 1), ret);
	BDBOP(penv->set_cachesize(0, cachesize, 1), ret);

	// Methods of containers returning a reference costs 
	// locker/object/lock slots.
	penv->set_lk_max_lockers(10000); 
	penv->set_lk_max_objects(10000);
	penv->set_lk_max_locks(10000);

	penv->set_flags(DB_TXN_NOSYNC, 1);

	BDBOP(penv->open(envhome, 
	    flags | DB_CREATE | DB_INIT_MPOOL, 0777), ret);
	register_db_env(penv);
	ptp->dbenv = penv;
	g_env = penv;
	g_test_start_txn = test_autocommit * explicit_txn;

	// Create the example and run.
	StlAdvancedFeaturesExample example(ptp);
	example.run();

	// Clean up and exit.
	delete ptp;
	dbstl_exit();// Call this method before the process exits.
	if (penv) 
		delete penv;
}

void usage()
{
	cout<<
		"\nUsage: StlAdvancedFeaturesExample\n\
[-c number		cache size]\n\
[-h			print this help message then exit]\n\
[-H dir			Specify dir as environment home]\n\
[-k number		shortest string inserted]\n\
[-l number		longest string inserted]\n\
[-m ds/cds/tds		use ds/cds/tds product]\n\
[-s b/h			use btree/hash type of DB for assocative \
containers] \n\
[-t a/e			for tds, use autocommit/explicit transaction in \
the test] \n\
[-v			verbose mode, output more info in multithread \
test]\n\
";
}

void using_charstr(TCHAR*str)
{
	cout<<_T("using str read only with non-const parameter type:")<<str;
	str[0] = _T('U');
	str[1] = _T('R');
}

int get_dest_secdb_callback(Db * /* secondary */, const Dbt * /* key */, 
    const Dbt *data, Dbt *result)
{
	SMSMsg *p = (SMSMsg*)data->get_data();

	result->set_data(&(p->to));
	result->set_size(sizeof(p->to));
	return 0;
}

void SMSMsgRestore(SMSMsg2& dest, const void *srcdata)
{
	char *p = dest.msg;

	memcpy(&dest, srcdata, sizeof(dest));

	dest.msg = (char *)DbstlReAlloc(p, dest.szmsg);
	strcpy(dest.msg, (char*)srcdata + sizeof(dest));
}

u_int32_t SMSMsgSize(const SMSMsg2& elem)
{
	return (u_int32_t)(sizeof(elem) + strlen(elem.msg) + 1);
}

void SMSMsgCopy(void *dest, const SMSMsg2&elem)
{
	memcpy(dest, &elem, sizeof(elem));
	strcpy((char*)dest + sizeof(elem), elem.msg);
}

u_int32_t rgblen(const RGBB *seq)
{
	size_t s = 0;
	
	const RGBB *p = seq, rgb0;
	for (s = 0, p = seq; memcmp(p, &rgb0, sizeof(rgb0)) != 0; p++, s++);
	// this size includes the all-0 last element used like '\0' 
	// for char* strings 
	return (u_int32_t)(s + 1);
}

// The seqs sequence of RGBB objects may not reside in a consecutive chunk of 
// memory but the seqd points to a consecutive chunk of mem large enough to 
// hold all objects from seqs.
void rgbcpy(RGBB *seqd, const RGBB *seqs, size_t num)
{
	const RGBB *p = seqs;
	RGBB rgb0;
	RGBB *q = seqd;

	memset((void *)&rgb0, 0, sizeof(rgb0));
	for (p = seqs, q = seqd; memcmp(p, &rgb0, sizeof(rgb0)) != 0 && 
	    num > 0; num--, p++, q++)
		memcpy((void *)q, p, sizeof(RGBB));
	memcpy((void *)q, p, sizeof(RGBB));// append trailing end token.
}
