/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2009 Oracle.  All rights reserved.
 *
 * $Id$ 
 */

#include "test.h"
// This function tests all member functions in the four assocative 
// containers and their iterators. The containers are db_map,
// db_multimap, db_set, db_multiset . They are equivalent to std::map, 
// hash_map, std::multimap, hash_multimap, std::set, hash_set, 
// std::multiset, hash_multiset respectively---passing btree db handles,
// they are equivalent to std assocative container; passing hash db handles
// they are hash associative containers
//
// This function tests the classes also in pair with std/hash counterparts,
// but the result of some operations are checked not by comparing contents
// after operation, but by self-check, often use another member function of
// the same class to verify. Also, only the four container types in std is
// used, the hash containers are not standarized, so not used, although 
// hash db type can be passed in, so the db_containers can be of hash type
//
// Most of the algorithms in std are targeting at a sequential range, while
// ranges in associative containers are not, so only some read only 
// algorithms are applied to the db_map container.
//
// This function also tests some advanced features, like using containers
// with secondary db, bulk retrieval, varying length records storage/load,
// storing native char*/wchar_t* strings, etc
// 

static int hcmp_def(Db *pdb, const Dbt *dbt1, const Dbt *dbt2)
{
	u_int32_t sz1 = dbt1->get_size(), sz2 = dbt2->get_size();

	int ret = memcmp(dbt1->get_data(), dbt2->get_data(), sz1 < sz2 ? sz1 : sz2);
	ret = (ret == 0) ? (sz1 - sz2) : ret;

	return ret;
}

class TestAssoc
{
public:
	typedef map<int, int> m_int_t;
	typedef ptint tpint;
	~TestAssoc();
	TestAssoc(void *param1);
	void start_test()
	{
		tb.begin("db_map");
		test_map_member_functions();
		test_map_std_functions();
		test_hot_dbclose();
		test_arbitrary_object_storage();
		test_storing_std_strings();
		test_secondary_containers();
		tb.end();

		tb.begin("db_multimap");
		test_mmap_member_functions();
		tb.end();

		tb.begin("db_set");
		test_set_member_functions();
		tb.end();

		tb.begin("db_multiset");
		test_mset_member_functions();
		tb.end();

		tb.begin("Special functionalities of all dbstl containers.");
		test_char_star_string_storage();
		test_fixed_len_obj_storage();
		test_arbitray_sequence_storage();
		test_bulk_retrieval_read();
		test_nested_txns();
		test_etc();
		tb.end();

	
	}
private:
	// Test all member functions of db_map<>
	void test_map_member_functions();

	// Use std functions to manipulate db_map containers.
	void test_map_std_functions();

	// Close a live container's db handle then open it again and 
	// reassign to the same container, verify the container works.
	void test_hot_dbclose();

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

	void test_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 test_secondary_containers();

	// Test all member functions of db_multimap<>.
	void test_mmap_member_functions();

	// Test all member functions of db_set<>.
	void test_set_member_functions();

	// Test all member functions of db_multiset<>.
	void test_mset_member_functions();

	// Test direct storage of char* strings.
	void test_char_star_string_storage();

	// Testing storage of fixed length objects.
	void test_fixed_len_obj_storage();

	// Testing storage of arbitrary element type of sequence.
	void test_arbitray_sequence_storage();

	// Testing reading with bulk retrieval flag.
	void test_bulk_retrieval_read();

	// Testing nested transaction implementation.
	void test_nested_txns();

	// Testing miscellaneous functions.
	void test_etc();

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

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

TestAssoc::~TestAssoc()
{
	dbstl::close_db_cursors(this->dmsdb2);
	dbstl::close_db_cursors(this->dmdb1);
	dbstl::close_db_cursors(this->dmdb2);
	dbstl::close_db_cursors(this->dmdb6);
	dbstl::close_db_cursors(this->dmmdb1);
}

TestAssoc::TestAssoc(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);
	dbp3 = NULL;
	
}

void TestAssoc::test_map_member_functions()
{
	int i;

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	
	dm_int_t dm1(dmdb1, penv);
	const dm_int_t& cnstdm1 = dm1;
	dm_int_t simple_map;
	map<ptint, ptint> ssimple_map;
	map<ptint, ptint>::iterator sitr, sitr1;

	for (i = 0; i < n; i++) {
		dm1[i] = ptint(i);
		ssimple_map[i] = ptint(i);
	}

	for (i = 0; i < n; i++) {
		dm_int_t::const_iterator citr, citr1;
		dm_int_t::iterator itr;

		citr = dm1.find(i);
		check_expr(citr->second == dm1.find(i)->second);
		itr = citr;
		check_expr(*citr == *itr);
		itr->second = i * 2 + 1;
		citr1 = itr;
		check_expr(*citr == *itr);
		check_expr(*citr == *citr1);
		check_expr(citr->second == dm1.find(i)->second);
	}

	for (i = 0; i < n; i++) {
		dm_int_t::const_iterator citr = dm1.find(i);
		check_expr(citr->second == dm1.find(i)->second);
		dm_int_t::iterator itr = citr;
		check_expr(*citr == *itr);
		itr->second = i * 2 + 1;
		dm_int_t::const_iterator citr1 = itr;
		check_expr(*citr == *itr);
		check_expr(*citr == *citr1);
		check_expr(citr->second == dm1.find(i)->second);
	}

	for (i = 0; i < n; i++) {
		dm_int_t::iterator ncitr, ncitr1;
		dm_int_t::const_iterator citr, citr1;

		ncitr = cnstdm1.find(i);
		check_expr(ncitr->second == cnstdm1.find(i)->second);
		citr = ncitr;
		check_expr(*citr == *ncitr);
		//*ncitr = i * 2 + 1;
		citr1 = ncitr;
		ncitr1 = citr1;
		check_expr(*citr == *ncitr);
		check_expr(*citr == *ncitr1);
		check_expr(*citr == *citr1);
		check_expr(citr->second == cnstdm1.find(i)->second);
	}

	for (i = 0; i < n; i++) {

		dm_int_t::iterator ncitr = cnstdm1.find(i);
		check_expr(ncitr->second == cnstdm1.find(i)->second);
		dm_int_t::const_iterator citr = ncitr;
		check_expr(*citr == *ncitr);
		//*itr = i * 2 + 1;
		dm_int_t::const_iterator citr1 = ncitr;
		dm_int_t::iterator ncitr1 = citr1;

		check_expr(*citr == *ncitr);
		check_expr(*citr == *ncitr1);
		check_expr(*citr == *citr1);
		check_expr(citr->second == cnstdm1.find(i)->second);
	}

	if (dm1.is_hash() == false)
	{
		dm1.clear();
		ssimple_map.clear();
		for (i = 0; i < n; i++) {
			dm1[i] = ptint(i);
			ssimple_map[i] = ptint(i);
		}
		dm_int_t::iterator ncitr = dm1.end();
		dm_int_t::iterator ncrend = --dm1.begin();
		map<ptint, ptint>::iterator sitr = ssimple_map.end();
		for (sitr--, --ncitr; ncitr != ncrend; ncitr--, sitr--) {
			ncitr.refresh(true); // not needed because by default we are using direct db get, only for test purpose.
			check_expr(*sitr == *ncitr);
		}
		ncitr.close_cursor();

		const dm_int_t&cnstdm1 = dm1;
		dm_int_t::const_iterator citr = cnstdm1.end();
		dm_int_t::const_iterator crend = --cnstdm1.begin();
		map<ptint, ptint>::iterator sitr2;
		map<ptint, ptint>::reverse_iterator rsitr;
		
		sitr = ssimple_map.end();
		for (sitr--, --citr; citr!= crend; sitr--, citr--) {
			citr.refresh(true); // not needed because by default we are using direct db get, only for test purpose.
			check_expr(*sitr == *citr);
		}

		for (sitr2 = --ssimple_map.begin(), citr = crend; citr != cnstdm1.end();) {
			citr++;
			sitr2++;
			check_expr(*sitr2 == *citr);
		}

		simple_map.insert(simple_map.begin(), *ssimple_map.begin());
		check_expr(*simple_map.begin() == *ssimple_map.begin());
		crend = --cnstdm1.begin();
		--(--crend);
		++crend;
		check_expr(*crend == *cnstdm1.begin());
		citr.close_cursor();

		simple_map.clear();
		simple_map.insert(cnstdm1.begin(), cnstdm1.end());
		rsitr = ssimple_map.rbegin();
		for (dm_int_t::reverse_iterator itrr1 = simple_map.rbegin(); itrr1 != simple_map.rend(); ++itrr1, ++rsitr)
			check_expr(*itrr1 == *rsitr);

		const dm_int_t &csimple_map = simple_map;
		rsitr = ssimple_map.rbegin();
		for (dm_int_t::const_reverse_iterator citrr1 = csimple_map.rbegin(); citrr1 != csimple_map.rend(); ++citrr1, ++rsitr)
			check_expr((citrr1->first == rsitr->first) && (citrr1->second == rsitr->second));

		for (sitr = ssimple_map.begin(); sitr != ssimple_map.end(); ++sitr)
			check_expr(csimple_map[sitr->first] == sitr->second);

		simple_map.erase(simple_map.end());
		check_expr(simple_map.size() == ssimple_map.size());
		check_expr(csimple_map.find(123) == csimple_map.end());

		check_expr(*csimple_map.lower_bound(1) == *ssimple_map.lower_bound(1));
		check_expr(*csimple_map.upper_bound(5) == *ssimple_map.upper_bound(5));
		pair<dm_int_t::const_iterator, dm_int_t::const_iterator> cnsteqrg = csimple_map.equal_range(3);
		check_expr(cnsteqrg.first->first == 3 && cnsteqrg.second->first == ssimple_map.equal_range(3).second->first);

	}
	dm1.clear();
	ssimple_map.clear();

	if (!dm1.is_hash()) {
		for (i = 0; i < n; i++) {
			dm1[i] = ptint(i);
			ssimple_map[i] = ptint(i);
		}
		dm_int_t::iterator itr, itr1;

		itr = dm1.find(3);
		itr1 = dm1.find(8);
		sitr = ssimple_map.find(3);
		sitr1 = ssimple_map.find(8);

		dm1.erase(itr, itr1);
		ssimple_map.erase(sitr, sitr1);
		pprint(dm1, "dm1 after erasing range: ");
		check_expr(is_equal(dm1, ssimple_map));
		dm1.clear();
		ssimple_map.clear();
	}
	for (i = 0; i < 10; i++) {
		simple_map.insert(make_pair(ptint(i), ptint(i)));
		ssimple_map.insert(make_pair(ptint(i), ptint(i)));
	}
	for (i = 0; i < 10; i++)
		check_expr(simple_map[i] == ssimple_map[i]);

	dm1.clear();
	// db_map<>::empty
	check_expr(dm1.empty());
	fill(dm1, m1, i = 3, n = 5);
	check_expr(!dm1.empty());
	
	dm_int_t::iterator dmi, dmi2;
	m_int_t::iterator mi;
	dm_int_t::reverse_iterator dmri;
	m_int_t::reverse_iterator mri;
	ptint ptmp;
	int itmp;


	// db_map<>::find, count
	for (dmi = dm1.begin(), mi = m1.begin(), i = 3; dmi != dm1.end() && 
	    mi !=m1.end(); dmi++, mi++, i++) {
		// check_expr both contain
		check_expr((dm1.find(i) != dm1.end()) && 
		    (m1.find(i) != m1.end()));
		
		pair<dm_int_t::iterator, dm_int_t::iterator> erp = 
		    dm1.equal_range(i);
		int jc = 0;
		dm_int_t::iterator jj;
		for (jj = erp.first, jc = 0; 
		    jj != erp.second; jj++, jc++)
		    check_expr((*jj).second == ptint(i));
		check_expr(jc == 1);
		if (i < 7 && !dm1.is_hash()) {// 7 is last element
			check_expr((*(dm1.upper_bound(i))).second == i + 1);
			check_expr((*(dm1.lower_bound(i))).second == i);
		}
		else if (i == 7 && !dm1.is_hash()) {
			check_expr(dm1.upper_bound(i) == dm1.end());
			check_expr((*(dm1.lower_bound(i))).second == i);
		} else if (!dm1.is_hash())
			check_expr(false);

		check_expr(dm1.count(i) == 1);
		check_expr(dm1.count(-i) == 0);
		check_expr((ptmp = dm1[i]) == (itmp = m1[i]));

		// order of elements in hash can not be expected
		if (!dm1.is_hash()) 
			check_expr((*dmi).second == (*mi).second);
		if (i == 3 + n - 1) {// last element
			
			for (; dmi != dm1.end(); mi--, i--) {
				// check_expr both contains
				check_expr((*(dm1.find(i))).second == i);
				check_expr((ptmp = dm1[i]) == (itmp = m1[i]));
				// order of elements in hash can not be 
				// expected
				if (!dm1.is_hash()) 
					check_expr((*dmi).second == (*mi).second);
				if (i % 2)
					dmi--;
				else 
					--dmi;
			}
			break;
		}
		
	}
	for (dmri = dm1.rbegin(), mri = m1.rbegin(), i = 3; 
	    dmri != dm1.rend() && mri != m1.rend(); dmri++, mri++, i++) {
		check_expr((dm1.find(i) != dm1.end()) && 
		    (m1.find(i) != m1.end()));// check_expr both contain

		check_expr(dm1[i] == m1[i]);	
		if (!dm1.is_hash())
			check_expr((*dmri).second == (*mri).second);
		if (i == 3 + n - 1) {// last element
			
			for (; dmri != dm1.rend(); mri--, i--) {
				// check_expr both contain
				check_expr((*(dm1.find(i))).second == i);
				// order of elements in hash can not be expected
				if (!dm1.is_hash()) 
					check_expr((*dmri).second == (*mri).second);
				check_expr((ptmp = dm1[i]) == (itmp = m1[i]));
				if (i % 2)
					dmri--;
				else
					--dmri;
				if (i == 3)
					break;
			}
			break;
		}
		
	}
	for (dmi = dm1.begin(), mi = m1.begin(), i = 3; dmi != dm1.end() && 
	    mi !=m1.end(); ++dmi, mi++)
		i++;
	check_expr(i == 3 + n);// i must have been incremented to 8
	for (dmri = dm1.rbegin(), mri = m1.rbegin(), i = 3; 
	    dmri != dm1.rend() && mri !=m1.rend(); ++dmri, mri++)
		i++;
	check_expr(i == 3 + n);// i must have been incremented to 8
		
	
	if (EXPLICIT_TXN)
		commit_txn(penv);
	if (!TEST_AUTOCOMMIT)
		begin_txn(0, penv);
	dm_int_t dm2(dmdb2, penv);
	dm2.clear();
	if (!TEST_AUTOCOMMIT)
		commit_txn(penv);
	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	dm2.insert(dm1.begin(), dm1.end());
	if (EXPLICIT_TXN)
		commit_txn(penv);
	if (!TEST_AUTOCOMMIT)
		begin_txn(0, penv);
	dm_int_t dm3 = dm2;
	if (!TEST_AUTOCOMMIT)
		commit_txn(penv);
	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	check_expr(dm3 == dm2);
	if (EXPLICIT_TXN)
		commit_txn(penv);
	if (!TEST_AUTOCOMMIT)
		begin_txn(0, penv);
	dm3 = dm1;
	if (!TEST_AUTOCOMMIT)
		commit_txn(penv);
	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	check_expr(dm3 == dm1);
	// this test case should be fine for hash because hash config is 
	// identical in dm1 and dm2
	for (dmi = dm1.begin(), dmi2 = dm2.begin(); dmi != dm1.end() && 
	    dmi2 != dm2.end(); ++dmi, dmi2++)
		check_expr(*dmi == *dmi2);
	int arr1[] = {33, 44, 55, 66, 77};
	
	for (dmi = dm1.begin(), i = 0; dmi != dm1.end(); dmi++, i++)
		(*dmi).second = tpint(arr1[i]);

	for (dmi = dm1.begin(), dmi2 = dm2.begin(), i = 0; 
		dmi != dm1.end() && dmi2 != dm2.end(); dmi++, i++, dmi2++) {
		check_expr((*dmi).second == tpint(arr1[i]));
		dmi2->second = dmi->second;
		check_expr(*dmi == *dmi2);
	}
	// db_map<>::insert(const value_type&). the range insert is already 
	// tested in fill
	//
	pair<dm_int_t::iterator, bool> res = 
	    dm1.insert(make_pair(3, tpint(33)));
	check_expr((*(res.first)).first == 3 && res.second == false);
	// we don't know which value is assigned to key 3 on hash
	if (!dm1.is_hash()) 
		check_expr((*(res.first)).second == 33);

	// db_map<>::count, insert, erase, find
	check_expr(dm1.count(3) == 1);
	check_expr(dm1.size() == (size_t)n);// n is 5
	check_expr(dm1.count(9) == 0);
	res = dm1.insert(make_pair(9, tpint(99)));
	check_expr((*(res.first)).second == 99 && (*(res.first)).first == 9 && 
	    res.second == true);
	check_expr(dm1.count(9) == 1);
	check_expr(dm1.size() == (size_t)n + 1);

	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dm1.erase(9);
	if (!TEST_AUTOCOMMIT)
		commit_txn(penv);
	if (EXPLICIT_TXN)
		begin_txn(0, penv);

	check_expr(dm1.size() == (size_t)n);
	check_expr(dm1.count(9) == 0);
	dm1.erase(dm1.find(3));
	check_expr(dm1.size() == (size_t)n - 1);
	check_expr(dm1.count(3) == 0);
	dm2.erase(dm2.begin(), dm2.end());
	check_expr(dm2.size() == 0);
	check_expr(dm2.empty());

	dmi = dm1.begin();
	dmi++;
	dmi2 = dmi;
	dmi2++;
	dmi2++;
	
	if (dm1.is_hash()) {
		check_expr(dm1.key_eq()(3, 4) == false);
		check_expr(dm1.key_eq()(3, 3) == true);
		check_expr(dm1.bucket_count() != 0);
	} else
		check_expr(dm1.key_comp()(3, 4));

	if (dm1.is_hash()) {
		check_expr(dm1.key_eq()(3, 4) == false);
		check_expr(dm1.key_eq()(3, 3) == true);
	} else {
		check_expr(dm1.key_comp()(3, 4));
	}

	check_expr(dm1.value_comp()(*dmi, *dmi2));
	if (dm1.is_hash())
		cout<<"hash value for key = 3 is: "
		    <<dm1.hash_funct()(3);

	dm2.insert(dmi, dmi2);// 2 recs inserted, [dmi, dmi2)
	check_expr(dm2.size() == 2);
	for (dmi = dm1.begin(); dmi != dm1.end(); dmi++)
		cout<<'\t'<<dmi->first<<'\t'<<dmi->second<<endl;
	if (EXPLICIT_TXN)
		commit_txn(penv);

	if (!TEST_AUTOCOMMIT) {
		begin_txn(0, penv);
	}
	dm1.swap(dm2);
	if (!TEST_AUTOCOMMIT) {
		commit_txn(penv);
	}
	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	size_t dm1sz, dm2sz;
	check_expr((dm1sz = dm1.size()) == 2 && 
	    (dm2sz = dm2.size()) == (size_t)n - 1);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);

	if (!TEST_AUTOCOMMIT) {
		begin_txn(0, penv);
	}
	dm1.clear();
	dm2.clear();
	dm3.clear();
	if (!TEST_AUTOCOMMIT) {
		commit_txn(penv);
	}
	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	fill(dm1, m1, i = 3, n = 5);
	dm1sz = dm1.size();
	for (i = (int)dm1sz -1; i >0; i--) {
		dm1[i - 1] = dm1[i];
		m1[i - 1] = m1[i];
	}
	if (!dm1.is_hash())
		check_expr (is_equal(dm1, m1));
	dm1[421] = 421;
	check_expr(dm1.count(421) == 1);
	m1[421] = 421;
	int j;
	for (i = 0; i < 100; i++) {
		j = rand() ;
		dm1[i] = i * j;
#ifdef TEST_PRIMITIVE
		m1[i] = ((ptint)dm1[i]);
#else 
		m1[i] = ((ptint)dm1[i]).v;
#endif
	}
	check_expr(dm1.size() == m1.size());
	if (!dm1.is_hash())
		check_expr (is_equal(dm1, m1));

	for (i = 0; i < 99; i++) {
		dm1[i] = dm1[i + 1];
		m1[i] = m1[i + 1];
	}
	if (!dm1.is_hash())
		check_expr (is_equal(dm1, m1));
	dm1.clear();
	m1.clear();
	if (EXPLICIT_TXN)
		commit_txn(penv);
} // test_map_member_functions

void TestAssoc::test_map_std_functions()
{
	int i;

	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	dm_int_t dm1(dmdb1, penv);
	// the following test assumes dm1 and m1 contains
	// (6,6), (7,7),...(14,14)
	for(i = 6; i < 15; i++) {
		dm1[i] = i;
		m1[i] = i;
	}
	dm_int_t::iterator mpos, mpos2;
	dm_int_t::reverse_iterator mrpos, mrpos2;
// for_each
	cout<<"\n_testing std algorithms applied to db_map...\n"; 
	cout<<"\nfor_each begin to end\n";
	for_each(dm1.begin(), dm1.end(), square_pair<dm_int_t::value_type_wrap>);
	cout<<endl;
	for_each(m1.begin(), m1.end(), square_pair2<map<int, int>::value_type>);
	cout<<"\nfor_each begin +1 to end\n";
	for_each(dm1.begin() ++, (dm1.begin()--)--, square_pair<dm_int_t::value_type_wrap>);
	cout<<endl;
	for_each(m1.begin() ++, (m1.begin() --)--, square_pair2<map<int, int>::value_type>);

	//find
	ptint tgt(12);
	mpos = find(dm1.begin(), dm1.end(), make_pair(tgt, tgt));
	check_expr(mpos != dm1.end() && ((mpos->second) == tgt));
	mpos = find(++(dm1.begin() ++), --(dm1.end() --), make_pair(tgt, tgt));
	check_expr(mpos != dm1.end() && ((mpos->second) == tgt));
	tgt = -123;
	mpos = find(dm1.begin(), dm1.end(), make_pair(tgt, tgt));
	check_expr(mpos == dm1.end());
	pair<int, int>* subseq3 = new pair<int, int>[3];
	subseq3[0] = make_pair(8, 8);
	subseq3[1] = make_pair(9, 9);
	subseq3[2] = make_pair(10, 10);
	pair<int, int>* subseq4 = new pair<int, int>[4];
	subseq4[0] = make_pair(9, 9);
	subseq4[1] = make_pair(8, 8);
	subseq4[2] = make_pair(10, 10);
	subseq4[3] = make_pair(11, 11);
	// find_end
	if (!dm1.is_hash()) {
#ifdef WIN32
		mpos = find_end(dm1.begin(), dm1.end(), dm1.begin(), ++(dm1.begin()) );
		check_expr(mpos == dm1.begin());
		mpos = find_end(dm1.begin(), dm1.end(), mpos2 = ++(++(dm1.begin()++)), 
		    --(--(dm1.end()--)));
		check_expr(mpos == mpos2);

		mpos = find_end(++dm1.begin(), dm1.end(), subseq3, subseq3 + 3);
		check_expr(mpos == ++(++(dm1.begin())));
#endif
		// find_first_of
		mpos = find_first_of(dm1.begin(), dm1.end(), dm1.begin(), 
		    ++dm1.begin());
		check_expr(mpos == dm1.begin());

		mpos = find_first_of(++dm1.begin(), --dm1.end(),
		    subseq4, subseq4 + 4);
		check_expr(mpos == ++(++dm1.begin()));
		mpos = find_first_of(--(--dm1.end()), dm1.end(), 
		    subseq4, subseq4 + 4);
		check_expr(mpos == dm1.end());
		
		// mismatch
		
		pair<dm_int_t::iterator , pair<int, int>*> resmm2 = mismatch(dm1.begin(),
		    ++(++(++dm1.begin())), subseq3);
		check_expr(resmm2.first == dm1.begin() && resmm2.second == subseq3);
	
		//search
		mpos = search(dm1.begin(), dm1.end(), dm1.begin(), ++dm1.begin());
		check_expr(mpos == dm1.begin());
		mpos = search(++dm1.begin(), dm1.end(), subseq3, subseq3 + 3);
		check_expr(mpos == ++(++dm1.begin()));
		//equal
		mpos2 = dm1.begin();
		mpos2.move_to(8);
		check_expr (equal(dm1.begin(), mpos2, subseq3) == false);
		mpos = mpos2; mpos.move_to(11);
		check_expr(equal(mpos2, mpos, subseq3) == true);
		mpos.move_to(10);
		check_expr(equal(mpos2, mpos, subseq3) == true);
	}
	delete [] subseq4;
	delete [] subseq3;
	//find_if
	mpos = find_if(dm1.begin(), dm1.end(), is2digits_pair);
	check_expr(ptint(mpos->second) == 10);

	// count_if
	oddcnt = count_if(dm1.begin(), dm1.end(), is_odd_pair);
	check_expr(oddcnt == 4);
	oddcnt = count_if(dm1.begin(), dm1.begin(), is_odd_pair);
	check_expr(oddcnt == 0);
	

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

} // test_map_std_functions

void TestAssoc::test_hot_dbclose()
{
	int i;
	map<ptint, ptint> m2;
	dm_int_t dm2(dmdb2, penv);

	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dm2.clear();// can be auto commit
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	for (i = 0; i < 5; i++) {
		dm2[i] = i;
		m2[i] = i;
	}

	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	dm_int_t dm1(dmdb1, penv);
	dbstl::close_db(dm1.get_db_handle());
	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	
	dm1.set_db_handle(dbp3 = dbstl::open_db(penv, "db_map.db", 
	    dbtype, DB_CREATE | dboflags, 0));
	if (!dm1.is_hash())
		check_expr (is_equal(dm1, m1));
	dm1.clear();
	if (EXPLICIT_TXN)
		commit_txn(penv);
} // test_hot_dbclose

void TestAssoc::test_arbitrary_object_storage()
{
	int i;

	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	// varying length objects test
	cout<<"\n_testing arbitary 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);
	// check that retrieved data is identical to stored data
	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<<"\n_testing arbitary 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);
} // test_arbitrary_object_storage

// std::string persistent test.
void TestAssoc::test_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 TestAssoc::test_secondary_containers()
{
	int i;

	if (EXPLICIT_TXN)
		begin_txn(0, penv);
	// test secondary db
	cout<<"\n_testing db 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);
	 
} // test_secondary_containers

void TestAssoc::test_mmap_member_functions()
{
	// db_multimap<>
	int i;

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);	
	dmm_int_t dmm1(dmmdb1, penv);
	
	dmm1.clear();
	// db_multimap<>::empty
	check_expr(dmm1.empty());
	{
	size_t sz0, szeq1, szeq2;

	fill(dmm1, mm1, i = 3, n = 5, 4);
	const dmm_int_t &cdmm1 = dmm1;
	dmm_int_t dmmn(NULL, penv);
	dmmn.insert(cdmm1.begin(), cdmm1.end());
	check_expr(!(dmmn != cdmm1));

	pair<dmm_int_t::const_iterator, dmm_int_t::const_iterator> cnstrg, ceqrgend;
	pair<dmm_int_t::iterator, dmm_int_t::iterator> nceqrg, eqrgend;

	for (i = 3; i < 8; i++) {
		cnstrg = cdmm1.equal_range(i);
		nceqrg = dmm1.equal_range(i);
		dmm_int_t::const_iterator cmmitr;
		dmm_int_t::iterator mmitr;
		for (sz0 = 0, cmmitr = cnstrg.first, mmitr = nceqrg.first; mmitr != nceqrg.second; mmitr++, cmmitr++, sz0++)
			check_expr(*cmmitr == *mmitr);
		check_expr(cmmitr == cnstrg.second);
		if (i < 7)
			check_expr(*(cdmm1.upper_bound(i)) == *(dmm1.upper_bound(i)));
		cnstrg = cdmm1.equal_range_N(i, szeq1);
		nceqrg = dmm1.equal_range_N(i, szeq2);
		check_expr(szeq1 == szeq2 && szeq2 == sz0);
	}
	eqrgend = dmm1.equal_range(65535);
	check_expr(eqrgend.first == dmm1.end() && eqrgend.second == dmm1.end());
	eqrgend = dmm1.equal_range_N(65535, szeq1);
	check_expr(eqrgend.first == dmm1.end() && eqrgend.second == dmm1.end() && szeq1 == 0);
	ceqrgend = cdmm1.equal_range(65535);
	check_expr(ceqrgend.first == cdmm1.end() && ceqrgend.second == cdmm1.end());
	ceqrgend = cdmm1.equal_range_N(65535, szeq1);
	check_expr(ceqrgend.first == cdmm1.end() && ceqrgend.second == cdmm1.end() && szeq1 == 0);
	if (!dmm1.is_hash()) {
		eqrgend = dmm1.equal_range(2);
		check_expr((((eqrgend.first))->first == 3) && ((eqrgend.second))->first == 3);
		ceqrgend = cdmm1.equal_range(2);
		check_expr(((ceqrgend.first))->first == 3 && ((ceqrgend.second))->first == 3);
		eqrgend = dmm1.equal_range_N(2, szeq1);
		check_expr((eqrgend.first)->first == 3 && ((eqrgend.second))->first == 3 && szeq1 == 0);
		ceqrgend = cdmm1.equal_range_N(2, szeq1);
		check_expr((ceqrgend.first)->first == 3 && ((ceqrgend.second))->first == 3 && szeq1 == 0);

		check_expr(((dmm1.upper_bound(2)))->first == 3);
		check_expr(((cdmm1.upper_bound(2)))->first == 3);
		check_expr(dmm1.upper_bound(65535) == dmm1.end());
		check_expr(cdmm1.upper_bound(65535) == cdmm1.end());
		check_expr(dmm1.lower_bound(65535) == dmm1.end());
		check_expr(cdmm1.lower_bound(65535) == cdmm1.end());
		check_expr(dmm1.count(65535) == 0);
		check_expr(dmm1.find(65535) == dmm1.end());
		check_expr(cdmm1.find(65535) == cdmm1.end());

		dmm_int_t tmpdmm0(dmm1);
		dmm1.erase(dmm1.end());
		check_expr(dmm1 == tmpdmm0);
		nceqrg = tmpdmm0.equal_range(5);
	
		for (dmm_int_t::iterator itr0 = nceqrg.first; itr0 != nceqrg.second; ++itr0)
			itr0->second *= 2;
		check_expr(tmpdmm0 != dmm1);
		tmpdmm0.insert(dmm1.begin(), ++(++dmm1.begin()));
		check_expr(tmpdmm0 != dmm1);
	}
	dmm1.clear();
	mm1.clear();
	}
	fill(dmm1, mm1, i = 3, n = 5, 4);
	check_expr(!dmm1.empty());

	typedef multimap<int, int> mm_int_t;
	dmm_int_t::iterator dmmi, dmmi2;
	mm_int_t::iterator mmi;
	dmm_int_t::reverse_iterator dmmri;
	mm_int_t::reverse_iterator mmri;

	print_mm(dmm1, dmmi);
	// db_multimap<>::find, count
	for (dmmi = dmm1.begin(), mmi = mm1.begin(), i = 3; 
	    dmmi != dmm1.end() && 
	    mmi !=mm1.end(); dmmi++, mmi++, i++) {
		// check_expr both contain
		check_expr((*(dmm1.find(i))).second == i);
		
		pair<dmm_int_t::iterator, dmm_int_t::iterator> erp1 = 
		    dmm1.equal_range(i);
		int jc = 0;
		dmm_int_t::iterator jj;
		for (jj = erp1.first, jc = 0; 
		    jj != erp1.second; jj++, jc++)
			check_expr((*jj).second == ptint(i));
		
		check_expr((size_t)jc == g_count[i]); // g_count[i] duplicates
		if (i < 7 && !dmm1.is_hash()) {// 7 is last element
			check_expr((*(dmm1.upper_bound(i))).second == i + 1);
			check_expr((*(dmm1.lower_bound(i))).second == i);
		}
		else if (i == 7 && !dmm1.is_hash()) {
			check_expr(dmm1.upper_bound(i) == dmm1.end());
			check_expr((*(dmm1.lower_bound(i))).second == i);
		} else if (!dmm1.is_hash())
			check_expr(false);

		check_expr(dmm1.count(i) == g_count[i]);
		check_expr(dmm1.count(-i) == 0);
		if (!dmm1.is_hash())
			check_expr((*dmmi).second == (*mmi).second);
		if (i == 3 + n - 1) {// last element
			
			for (; dmmi != dmm1.end(); mmi--, i--) {
				// check_expr both contains
				check_expr((*(dmm1.find(i))).second == i);
				if (!dmm1.is_hash())
					check_expr((*dmmi).second == (*mmi).second);
				if (i % 2)
					dmmi--;
				else 
					--dmmi;
			}
			break;
		}
		
	}
	for (dmmri = dmm1.rbegin(), mmri = mm1.rbegin(), i = 3; 
	    dmmri != dmm1.rend() && mmri !=mm1.rend(); dmmri++, mmri++, i++) {
		check_expr((dmm1.find(i) != dmm1.end()) && 
		    (mm1.find(i) != mm1.end()));// check_expr both contain
		if (dmm1.is_hash() == false)
			check_expr((*dmmri).second == (*mmri).second);
		if (i == 3 + n - 1) {// last element
			
			for (; dmmri != dmm1.rend() && mmri != mm1.rend(); i--) {
				// check_expr both contain
				check_expr((*(dmm1.find(i))).second == i);
				if (!dmm1.is_hash())
					check_expr((*dmmri).second == (*mmri).second);
				if (i % 2)
					dmmri--;
				else
					--dmmri;
				if (i > 3) // MS STL bug: when msri points to last element, it can not go back
					mmri--;
				
			}
			break;
		}
		
	}
	for (dmmi = dmm1.begin(), mmi = mm1.begin(), i = 3; 
	    dmmi != dmm1.end() && 
	    mmi !=mm1.end(); ++dmmi, mmi++)
		i++;
	check_expr((size_t)i == 3 + g_sum(3, 8));
	for (dmmri = dmm1.rbegin(), mmri = mm1.rbegin(), i = 3; 
	    dmmri != dmm1.rend() && mmri !=mm1.rend(); ++dmmri, mmri++)
		i++;
	check_expr((size_t)i == 3 + g_sum(3, 8));
	
	dmm_int_t dmm2(dmmdb2, penv);
	dmm2.clear();
	dmmi = dmm1.begin();
	dmmi2 = dmm1.end();
	dmm2.insert(dmmi, dmmi2);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);

	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dmm_int_t dmm3 = dmm2;
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	check_expr(dmm3 == dmm2);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);

	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dmm3 = dmm1;
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	check_expr(dmm3 == dmm1);

	for (dmmi = dmm1.begin(), dmmi2 = dmm2.begin(); dmmi != dmm1.end() && 
	    dmmi2 != dmm2.end(); ++dmmi, dmmi2++)
		check_expr(*dmmi == *dmmi2);

	// content of dmm1 and dmm2 are changed since now
	int arr1[] = {33, 44, 55, 66, 77};
	int arr11[] = {33, 44, 55, 66, 77};
	for (dmmi = dmm1.begin(), i = 0; dmmi != dmm1.end(); dmmi++, i++)
		(*dmmi).second = tpint(arr11[i % 5]);

	for (dmmi = dmm1.begin(), dmmi2 = dmm2.begin(), i = 0; 
	    dmmi != dmm1.end() && dmmi2 != dmm2.end(); 
	    dmmi++, i++, dmmi2++) {
		check_expr((*dmmi).second == tpint(arr1[i % 5]));
		dmmi2->second = dmmi->second;
		check_expr(*dmmi == *dmmi2);
	}

	// db_multimap<>::insert(const value_type&). the range insert is already 
	// tested in fill
	//
	dmm_int_t::iterator iitr = dmm1.insert(make_pair(3, tpint(33)));
	check_expr(iitr->first == 3);
	// the returned iitr points to any rec with key==3, not necessarily 
	// the newly inserted rec, so not sure of the value on hash
	if (dmm1.is_hash() == false)
		check_expr(iitr->second == 33);

	dmm1.clear();
	mm1.clear();
	fill(dmm1, mm1, i = 3, n = 5, 4);
	// db_multimap<>::count, insert, erase, find
	size_t sizet1;
	check_expr(dmm1.count(3) == g_count[3]);
	check_expr(dmm1.size() == (sizet1 = g_sum(3, 8)));// sum of recs from 3 to 8
	check_expr(dmm1.count(9) == 0);
	dmm_int_t::iterator iitr2 = dmm1.insert(make_pair(9, tpint(99)));
	check_expr(iitr2->first == 9 && iitr2->second == 99);
	check_expr(dmm1.count(9) == 1);
	check_expr(dmm1.size() == sizet1 + 1);
	dmm1.erase(9);
	check_expr(dmm1.size() == (sizet1 = g_sum(3, 8)));
	check_expr(dmm1.count(9) == 0);
	dmm1.erase(dmm1.find(3));
	check_expr(dmm1.size() == g_sum(3, 8) - 1);
	check_expr(dmm1.count(3) == (g_count[3] - 1 > 0 ? g_count[3] - 1 : 0));
	dmm2.erase(dmm2.begin(), dmm2.end());
	check_expr(dmm2.size() == 0);
	check_expr(dmm2.empty());

	dmmi = dmm1.begin();
	dmmi++;
	dmmi2 = dmmi;
	dmmi2++;
	dmmi2++;
	size_t sizet2;
	dmm2.insert(dmmi, dmmi2);// 2 recs inserted, [dmi, dmi2)
	check_expr((sizet2 = dmm2.size()) == 2);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);

	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);

	dmm1.swap(dmm2);
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

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

	size_t dmm1sz, dmm2sz;
	check_expr((dmm1sz = dmm1.size()) == 2 && (dmm2sz = 
	    dmm2.size()) == sizet1 - 1);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);

	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dmm1.clear();
	dmm2.clear();
	dmm3.clear();
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	
	dmm_int_t dmm4(dmmdb4, penv);
	dmm4.clear();
	multimap<ptint, ptint> mm4;
	int jj;
	for (i = 0; i < 10; i++) {
		sizet2 = abs(rand() % 20) + 1;
		for (jj = 0; jj < (int)sizet2; jj++) {
			dmm4.insert(make_pair(i, jj));
			mm4.insert(make_pair(i, jj));
		}
	}
	pair<dmm_int_t::iterator, dmm_int_t::iterator> eqrgdmm;
	pair<multimap<ptint, ptint>::iterator, multimap<ptint, ptint>::iterator> 
	    eqrgmm;
	dmm_int_t::iterator dmmitr;
	multimap<ptint, ptint>::iterator mmitr;
	for (i = 0; i < 10; i++) {
		eqrgdmm = dmm4.equal_range(i);
		eqrgmm = mm4.equal_range(i);
		for (dmmitr = eqrgdmm.first, mmitr = eqrgmm.first; dmmitr != 
		    eqrgdmm.second && mmitr != eqrgmm.second; dmmitr++, mmitr++)
			check_expr(*dmmitr == *mmitr);
		check_expr((dmmitr == eqrgdmm.second) && (mmitr == eqrgmm.second));
	}
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
} // test_mmap_member_functions

void TestAssoc::test_set_member_functions()
{
// db_set<>
	int i;
	int arr1[] = {33, 44, 55, 66, 77};

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	dms_int_t dms1(dmsdb1, penv);
	const dms_int_t& cnstdms1 = dms1;
	set<ptint> ms1;

	n = 10;
	for (i = 0; i < n; i++)
		dms1.insert(i);

	for (i = 0; i < n; i++) {
		dms_int_t::const_iterator citr, citr1;
		citr = dms1.find(i);
		check_expr(*citr == *dms1.find(i));
		dms_int_t::iterator itr = citr;
		check_expr(*citr == *itr);
		*itr = i;
		itr = dms1.find(i);
		citr = itr;
		citr1 = itr;
		check_expr(*citr == *itr);
		check_expr(*citr == *citr1);
		check_expr(*citr == *dms1.find(i));
	}

	for (i = 0; i < n; i++) {
		dms_int_t::const_iterator citr = dms1.find(i);
		check_expr(*citr == *dms1.find(i));
		dms_int_t::iterator itr = citr;
		check_expr(*citr == *itr);
		*itr = i;
		itr = dms1.find(i);
		citr = itr;
		dms_int_t::const_iterator citr1 = itr;
		check_expr(*citr == *itr);
		check_expr(*citr == *citr1);
		check_expr(*citr == *dms1.find(i));
	}

	for (i = 0; i < n; i++) {
		dms_int_t::iterator ncitr, ncitr1;
		dms_int_t::const_iterator citr, citr1;

		ncitr = cnstdms1.find(i);
		check_expr(*ncitr == *cnstdms1.find(i));
		citr = ncitr;
		check_expr(*citr == *ncitr);
		//*ncitr = i * 2 + 1;
		citr1 = ncitr;
		ncitr1 = citr1;
		check_expr(*citr == *ncitr);
		check_expr(*citr == *ncitr1);
		check_expr(*citr == *citr1);
		check_expr(*citr == *cnstdms1.find(i));
	}

	for (i = 0; i < n; i++) {

		dms_int_t::iterator ncitr = cnstdms1.find(i);
		check_expr(*ncitr == *cnstdms1.find(i));
		dms_int_t::const_iterator citr = ncitr;
		check_expr(*citr == *ncitr);
		//*itr = i * 2 + 1;
		dms_int_t::const_iterator citr1 = ncitr;
		dms_int_t::iterator ncitr1 = citr1;

		check_expr(*citr == *ncitr);
		check_expr(*citr == *ncitr1);
		check_expr(*citr == *citr1);
		check_expr(*citr == *cnstdms1.find(i));
	}

	if (dms1.is_hash() == false)
	{
		ms1.clear();
		dms1.clear();
		for (i = 0; i < n; i++) {
			dms1.insert(ptint(i));
			ms1.insert(ptint(i));
		}

		const dms_int_t &cnstdms1 = dms1;
		dms_int_t tmpdms0;

		tmpdms0.insert(cnstdms1.begin(), cnstdms1.end());
		dms_int_t::const_iterator citr = dms1.end(), citr2 = --dms1.begin(), itr2;
		set<ptint>::const_iterator scitr = ms1.end(), scitr2 = --ms1.begin();		
		for (citr--, scitr--; citr != citr2; --citr, --scitr) {
			check_expr(*citr == *scitr);
		}
		check_expr(scitr == scitr2);

		db_set<ptype<int> > dmspt;
		dmspt.insert(cnstdms1.begin(), cnstdms1.end());
		
		db_set<ptype<int> >::const_iterator itrpt;
		db_set<ptype<int> >::iterator itrpt2;

		for (itrpt = dmspt.begin(), citr = dms1.begin(); itrpt != dmspt.end(); ++itrpt, ++citr) {
			itrpt.refresh(true);
			check_expr(itrpt->v == *citr);
		}

		for (itrpt2 = dmspt.begin(), itr2 = dms1.begin(); itrpt2 != dmspt.end(); ++itrpt2, ++itr2) {
			itrpt2.refresh(true);
			check_expr(itrpt2->v == *itr2);
		}

		dms_int_t dmstmp(dms1);
		check_expr(dms1 == dmstmp);
		dms1.insert(dms1.begin(), 101);
		check_expr(dms1 != dmstmp);

		ms1.clear();
		dms1.clear();
	}
	dms1.clear();
	// db_map<>::empty
	check_expr(dms1.empty());
	fill(dms1, ms1, i = 3, n = 5);
	check_expr(!dms1.empty());

	typedef set<int> ms_int_t;
	dms_int_t::iterator dmsi, dmsi2;
	ms_int_t::iterator msi;
	dms_int_t::reverse_iterator dmsri;
	ms_int_t::reverse_iterator msri;

	// db_set<>::find, count
	for (dmsi = dms1.begin(), msi = ms1.begin(), i = 3; dmsi != dms1.end() && 
	    msi !=ms1.end(); dmsi++, msi++, i++) {
		// check_expr both contain
		check_expr(*(dms1.find(i)) == i);
		
		pair<dms_int_t::iterator, dms_int_t::iterator> erp = 
		    dms1.equal_range(i);
		int jc = 0;
		dms_int_t::iterator jj;
		for (jj = erp.first, jc = 0; 
		    jj != erp.second; jj++, jc++)
		    check_expr((*jj) == ptint(i));
		check_expr(jc == 1);
		if (i < 7 && !dms1.is_hash()) {// 7 is last element
			check_expr((*(dms1.upper_bound(i))) == i + 1);
			check_expr((*(dms1.lower_bound(i))) == i);
		}
		else if (i == 7 && !dms1.is_hash()) {
			check_expr(dms1.upper_bound(i) == dms1.end());
			check_expr((*(dms1.lower_bound(i))) == i);
		} else if (!dms1.is_hash())
			check_expr(false);

		check_expr(dms1.count(i) == 1);
		check_expr(dms1.count(-i) == 0);
		if (!dms1.is_hash()) {
			check_expr((*dmsi) == (*msi));
			check_expr((*dmsi) == (*msi));
		}
		if (i == 3 + n - 1) {// last element
			if (!dms1.is_hash()) 
				check_expr((*dmsi) == (*msi));
			for (; dmsi != dms1.end() && (*dmsi) == (*msi); i--) {
				// check_expr both contains
				if (!dms1.is_hash()) 
					check_expr((*dmsi) == (*msi));
				check_expr((*(dms1.find(i)) == i));
				
				if (i % 2)
					dmsi--;
				else 
					--dmsi;
				msi--;
			}
			break;
		}
		
	}
	for (dmsri = dms1.rbegin(), msri = ms1.rbegin(), i = 3; 
	    dmsri != dms1.rend() && msri != ms1.rend(); dmsri++, msri++, i++) {
		check_expr((*(dms1.find(i)) == i));
		if (!dms1.is_hash()) 
			check_expr((*dmsri) == (*msri));
		if (i == 3 + n - 1) {// last element
			
			for (; dmsri != dms1.rend() && msri != ms1.rend(); i--) {
				// check_expr both contain
				check_expr((dms1.find(i) != dms1.end()) && 
				    (ms1.find(i) != ms1.end()));
				if (!dms1.is_hash()) 
					check_expr((*dmsri) == (*msri));

				if (i % 2)
					dmsri--;
				else
					--dmsri;
				if (i > 3) // MS STL bug: when msri points to last element, it can not go back
					msri--;
				
			}
			break;
		}
		
	}
	for (dmsi = dms1.begin(), msi = ms1.begin(), i = 3; dmsi != dms1.end() && 
	    msi !=ms1.end(); ++dmsi, msi++)
		i++;
	check_expr(i == 3 + n);

	for (dmsri = dms1.rbegin(), msri = ms1.rbegin(), i = 3; 
	    dmsri != dms1.rend() && msri !=ms1.rend(); ++dmsri, msri++)
		i++;
	check_expr(i == 3 + n);
	
	dms_int_t dms2(dmsdb2, penv);
	dms2.clear();
	dms2.insert(dms1.begin(), dms1.end());
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dms_int_t dms3 = dms2;
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

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

	check_expr(dms3 == dms2);
	dms3 = dms1;
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

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

	check_expr(dms3 == dms1);

	for (dmsi = dms1.begin(), dmsi2 = dms2.begin(); dmsi != dms1.end() && 
	    dmsi2 != dms2.end(); ++dmsi, dmsi2++)
		check_expr(*dmsi == *dmsi2);
	
	/* !!!XXX 
	set keys are not supposed to be mutated, so this is an extra functionality, just like 
	the set iterator assignment in ms's stl library. Here we must use i < dms1.size() to
	limit the number of loops because after each assignment, dmsi will be positioned to 
	the element which is immediately next to the old key it sits on before the assignment,
	thus it may take more loops to go to the end() position.
	*/
	pprint(dms1, "dms1 before iterator assignment : \n");
	dms_int_t::size_type ui;
	for (dmsi = dms1.begin(), ui = 0; dmsi != dms1.end() && ui < dms1.size(); dmsi++, ui++) {
		(*dmsi) = tpint(arr1[ui]);
		pprint(dms1, "\ndms1 after one element assignment : ");
	}
	if (dms1.is_hash() == false) {
		pprint(dms1, "dms1 after iterator assignment : \n");
		for (dmsi = dms1.begin(), dmsi2 = dms2.begin(), i = 0; 
			dmsi != dms1.end() && dmsi2 != dms2.end(); dmsi++, i++, dmsi2++) {
			check_expr((*dmsi) == tpint(arr1[i]));
			*dmsi2 = *dmsi;
			// check_expr(*dmsi == *dmsi2); dmsi2 is invalidated by the assignment, so can't compare here.
		}
		// Compare here.
		for (dmsi = dms1.begin(), dmsi2 = dms2.begin(), i = 0; 
		    dmsi != dms1.end() && dmsi2 != dms2.end(); dmsi++, i++, dmsi2++)
			check_expr(*dmsi2 == *dmsi);
	}

	dms1.clear();
	//dms2.clear();
	fill(dms1, ms1, i = 3, n = 5);
	// db_set<>::insert(const value_type&). the range insert is already 
	// tested in fill
	//
	pair<dms_int_t::iterator, bool> ress = 
	    dms1.insert(tpint(3));
	check_expr((*(ress.first)) == 3 && ress.second == false);

	// db_set<>::count, insert, erase, find
	check_expr(dms1.count(3) == 1);
	check_expr(dms1.size() == (size_t)n);// n is 5
	check_expr(dms1.count(9) == 0);
	ress = dms1.insert(tpint(9));
	check_expr((*(ress.first)) == 9 && ress.second == true);
	check_expr(dms1.count(9) == 1);
	check_expr(dms1.size() == (size_t)n + 1);
	dms1.erase(9);
	check_expr(dms1.size() == (size_t)n);
	check_expr(dms1.count(9) == 0);
	dms1.erase(dms1.find(3));
	check_expr(dms1.size() == (size_t)n - 1);
	check_expr(dms1.count(3) == 0);
	dms2.erase(dms2.begin(), dms2.end());
	check_expr(dms2.size() == 0);
	check_expr(dms2.empty());

	dmsi = dms1.begin();
	dmsi++;
	dmsi2 = dmsi;
	dmsi2++;
	dmsi2++;
	dms2.insert(dmsi, dmsi2);// 2 recs inserted, [dmsi, dmsi2)
	check_expr(dms2.size() == 2);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dms1.swap(dms2);
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	size_t dms1sz, dms2sz;
	check_expr((dms1sz = dms1.size()) == 2 && (dms2sz = dms2.size()) == (size_t)n - 1);
	dms1.clear();
	dms2.clear();
	dms3.clear();

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

} // test_set_member_functions

void TestAssoc::test_mset_member_functions()
{
// db_multiset<>
	int i;
	size_t sizet1;

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	
	dmms_int_t dmms1(dmmsdb1, penv);
	multiset<int> mms1;

	dmms1.clear();
	// db_multiset<>::empty
	check_expr(dmms1.empty());
	fill(dmms1, mms1, i = 3, n = 5, 4);
	check_expr(!dmms1.empty());
	dmms_int_t tmpmms0;
	const dmms_int_t &cnstmms = dmms1;
	dmms_int_t tmpmms1;
	tmpmms1.insert(dmms1.begin(), dmms1.end());
	tmpmms0.insert(cnstmms.begin(), cnstmms.end());
	tmpmms0.insert(tmpmms0.begin(), *(--tmpmms0.end()));
	check_expr(tmpmms0 != tmpmms1);

	typedef multiset<int> mms_int_t;
	dmms_int_t::iterator dmmsi, dmmsi2;
	mms_int_t::iterator mmsi;
	dmms_int_t::reverse_iterator dmmsri;
	mms_int_t::reverse_iterator mmsri;

	// db_multiset<>::find, count
	for (dmmsi = dmms1.begin(), mmsi = mms1.begin(), i = 3; 
	    dmmsi != dmms1.end() && 
	    mmsi !=mms1.end(); dmmsi++, mmsi++, i++) {
		// check_expr both contain
		check_expr((*(dmms1.find(i))) == i);
		
		pair<dmms_int_t::iterator, dmms_int_t::iterator> erp1 = 
		    dmms1.equal_range(i);
		int jc = 0;
		dmms_int_t::iterator jj;
		for (jj = erp1.first, jc = 0; 
		    jj != erp1.second; jj++, jc++) {
			// there is bug so this line can be reached
			if ((size_t)jc == g_count[i]) {
				jj == erp1.second;// cmp again to debug it
				dmms1.get_db_handle()->stat_print(DB_STAT_ALL);
			}
			check_expr((*jj) == ptint(i));
		}
		check_expr((size_t)jc == g_count[i]); // g_count[i] duplicates
		if (i < 7 && !dmms1.is_hash()) {// 7 is last element
			check_expr((*(dmms1.upper_bound(i))) == i + 1);
			check_expr((*(dmms1.lower_bound(i))) == i);
		}
		else if (i == 7 && !dmms1.is_hash()) {
			check_expr(dmms1.upper_bound(i) == dmms1.end());
			check_expr((*(dmms1.lower_bound(i))) == i);
		} else if (!dmms1.is_hash())
			check_expr(false);

		check_expr(dmms1.count(i) == g_count[i]);
		check_expr(dmms1.count(-i) == 0);
		if (!dmms1.is_hash())
			check_expr((*dmmsi) == (*mmsi));
		if (i == 3 + n - 1) {// last element
			
			for (; dmmsi != dmms1.end(); mmsi--, i--) {
				// check_expr both contains
				check_expr((*(dmms1.find(i))) == i);
				if (!dmms1.is_hash())
					check_expr((*dmmsi) == (*mmsi));
				if (i % 2)
					dmmsi--;
				else 
					--dmmsi;
			}
			break;
		}
		
	}
	for (dmmsri = dmms1.rbegin(), mmsri = mms1.rbegin(), i = 3; 
	    dmmsri != dmms1.rend() && mmsri !=mms1.rend(); dmmsri++, mmsri++, i++) {
		check_expr((dmms1.find(i) != dmms1.end()) && 
		    (mms1.find(i) != mms1.end()));// check_expr both contain
		if (dmms1.is_hash() == false)
			check_expr((*dmmsri) == (*mmsri));
		if (i == 3 + n - 1) {// last element
			
			for (; dmmsri != dmms1.rend() && mmsri != mms1.rend(); i--) {
				// check_expr both contain
				check_expr((*(dmms1.find(i))) == i);
				if (!dmms1.is_hash())
					check_expr((*dmmsri) == (*mmsri));
				if (i % 2)
					dmmsri--;
				else
					--dmmsri;
				if (i > 3) // MS STL bug: when msri points to last element, it can not go back
					mmsri--;
			}
			break;
		}
		
	}
	for (dmmsi = dmms1.begin(), mmsi = mms1.begin(), i = 3; 
	    dmmsi != dmms1.end() && 
	    mmsi !=mms1.end(); ++dmmsi, mmsi++)
		i++;
	check_expr((size_t)i == 3 + g_sum(3, 8));
	for (dmmsri = dmms1.rbegin(), mmsri = mms1.rbegin(), i = 3; 
	    dmmsri != dmms1.rend() && mmsri !=mms1.rend(); ++dmmsri, mmsri++)
		i++;
	check_expr((size_t)i == 3 + g_sum(3, 8));
	
	
		
	
	dmms_int_t dmms2(dmmsdb2, penv);
	dmms2.clear();
	dmms2.insert(dmms1.begin(), dmms1.end());
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dmms_int_t dmms3 = dmms2;
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	cout<<"\ndmms2: \n";
	for (dmms_int_t::iterator itr = dmms2.begin(); itr != dmms2.end(); ++itr)
		cout<<"\t"<<*itr;
	cout<<"\ndmms3: \n";
	for (dmms_int_t::iterator itr = dmms3.begin(); itr != dmms3.end(); ++itr)
		cout<<"\t"<<*itr;

	check_expr(dmms3 == dmms2);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dmms3 = dmms1;
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	check_expr(dmms3 == dmms1);

	for (dmmsi = dmms1.begin(), dmmsi2 = dmms2.begin(); dmmsi != dmms1.end() && 
	    dmmsi2 != dmms2.end(); ++dmmsi, dmmsi2++)
		check_expr(*dmmsi == *dmmsi2);

	// content of dmms1 and dmms2 are changed since now
	// int arr11[] = {33, 44, 55, 66, 77};
	
	// db_multiset<>::insert(const value_type&). the range insert is already 
	// tested in fill
	//
	dmms_int_t::iterator iitrms = dmms1.insert(ptint (3));
	check_expr(*iitrms == 3);
	 
	dmms1.clear();
	mms1.clear();
	fill(dmms1, mms1, i = 3, n = 5, 4);
	// db_multiset<>::count, insert, erase, find
	check_expr(dmms1.count(3) == g_count[3]);
	check_expr(dmms1.size() == (sizet1 = g_sum(3, 8)));// sum of recs from 3 to 8
	check_expr(dmms1.count(9) == 0);
	dmms_int_t::iterator iitrms2 = dmms1.insert(ptint (9));
	check_expr(*iitrms2 == 9);
	check_expr(dmms1.count(9) == 1);
	check_expr(dmms1.size() == sizet1 + 1);
	dmms1.erase(9);
	check_expr(dmms1.size() == g_sum(3, 8));
	check_expr(dmms1.count(9) == 0);
	dmms1.erase(dmms1.find(3));
	check_expr(dmms1.size() == g_sum(3, 8) - 1);
	check_expr(dmms1.count(3) == (g_count[3] - 1 > 0 ? g_count[3] - 1 : 0));
	dmms2.erase(dmms2.begin(), dmms2.end());
	check_expr(dmms2.size() == 0);
	check_expr(dmms2.empty());

	dmmsi = dmms1.begin();
	dmmsi++;
	dmmsi2 = dmmsi;
	dmmsi2++;
	dmmsi2++;
	dmms2.insert(dmmsi, dmmsi2);// 2 recs inserted, [dmi, dmi2)
	size_t sssz;
	check_expr((sssz = dmms2.size()) == 2);
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
	if (!TEST_AUTOCOMMIT)
		dbstl::begin_txn(0, penv);
	dmms1.swap(dmms2);
	if (!TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);

	if (EXPLICIT_TXN)
		dbstl::begin_txn(0, penv);
	size_t dmms1sz, dmms2sz;
	check_expr((dmms1sz = dmms1.size()) == 2 && (dmms2sz = dmms2.size()) == sizet1 - 1);

	dmms1.clear();
	dmms2.clear();
	dmms3.clear();
	if (EXPLICIT_TXN)
		dbstl::commit_txn(penv);
	
} // test_mset_member_functions

void TestAssoc::test_char_star_string_storage()
{
	int i;
	// testing varying length data element storage/retrieval
	cout<<"\n_testing char*/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");
// = _T("abc"), *cstr2 = _T("defcd"), *cstr3 = _T("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');// itis 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);

	}
	
} // test_char_star_string_storage

void TestAssoc::test_fixed_len_obj_storage()
{
	int i;
	map<int, sms_t> ssmsmap;

	if (EXPLICIT_TXN) 
		dbstl::begin_txn(0, penv);
	
	typedef db_map<int, sms_t> smsmap_t;
	smsmap_t smsmap(dmdb6, penv);
	
	sms_t smsmsg;
	time_t now;
	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));
	}
	smsmap.clear();
	if (EXPLICIT_TXN) 
		dbstl::commit_txn(penv);
} //test_var_len_obj_storage

void TestAssoc::test_arbitray_sequence_storage()
{
	int i, j;

	if (EXPLICIT_TXN) 
		dbstl::begin_txn(0, penv);
	// storing arbitary sequence test .  
	cout<<endl<<"Testing 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];
		prgb1 = rgbelem;// Can't use rgbsmap[i] here because container::operator[] is an temporary value.;
		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);
		check_expr(memcmp(prgb1, prgb2 = srmitr->second, 
		    sizeof(RGBB) * rgblen(srmitr->second)) == 0);
		rmitr.refresh();
	}

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

void TestAssoc::test_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<<"\n_testing bulk 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);

	
} //test_bulk_retrieval_read

void TestAssoc::test_nested_txns()
{
	int i;
	size_t sizet1;

	if (!EXPLICIT_TXN)
		return;// nested txn tests can't be run if container not txnal

	// nested transaction test
	// 1. many external txns nested, no internal txn. 
	
	typedef db_map<char*, char*, ElementHolder<char*> > strstrmap_t;
	DbTxn *txn1 = NULL, *txn2, *txn3;
	char kstr[10], dstr[10];
	std::map<int, string> kstrs;
	int commit_or_abort = 0;
	set<int> idxset;
	char *keystr;
 
	strstrmap_t smap(dbstrmap, penv);	
commit_abort:

#define _DB_STL_TEST_SET_TXN_NAME(txn)	\
	txn->set_name(commit_or_abort == 0 ? #txn"_commit" : #txn"_abort")

	txn1 = begin_txn(0, penv);
	smap.clear();
	//txn1->set_name(commit_or_abort == 0 ? "txn1_commit" : "txn1_abort");
	_DB_STL_TEST_SET_TXN_NAME(txn1);
	memset(kstr, 0, sizeof(kstr));
	memset(dstr, 0, sizeof(dstr));
	for (char cc = 'a'; cc <= 'z'; cc++) {
		_snprintf(kstr, 10, "%c%c%c", cc, cc, cc);
		for (int kk = 0; kk < 9; kk++)
			dstr[kk] = cc;
		
		i = cc - 'a';
		kstrs.insert(make_pair(i, string(kstr)));
		switch (i) {
		case 3:
			txn2 = begin_txn(0, penv);// nest txn2 into txn1
			_DB_STL_TEST_SET_TXN_NAME(txn2);
			break;
		case 6:// 
			abort_txn(penv); // abort txn2
			
			for (int kk = 3; kk < 6; kk++) {
				keystr = const_cast<char*>(kstrs[kk].c_str());
				check_expr(smap.count(keystr) == 0);
			}

			txn2 = begin_txn(0, penv);// nest txn2 into txn1
			_DB_STL_TEST_SET_TXN_NAME(txn2);
			break;
		case 9:// 6--11: txn3 abort and txn2 commit
			txn3 = begin_txn(0, penv);// nest txn3 into txn2, txn2 is in txn1
			_DB_STL_TEST_SET_TXN_NAME(txn3);
			break;
		case 12:
			abort_txn(penv);// abort txn3
			commit_txn(penv);// commit txn2, its txn3 part is not applied
			for (int kk = 6; kk < 12; kk++) {
				keystr = const_cast<char*>(kstrs[kk].c_str());
				if (kk < 9)
					check_expr((sizet1 = smap.count(keystr)) > 0);
				if (kk >= 9)
					check_expr(smap.count(keystr) == 0);
			}

			break;
		case 15:// 15--18: txn3 commit and txn2 abort
			txn2 = begin_txn(0, penv);
			_DB_STL_TEST_SET_TXN_NAME(txn2);
			break;
		case 17:
			txn3 = begin_txn(0, penv);
			_DB_STL_TEST_SET_TXN_NAME(txn3);
			break;
		case 19:
			commit_txn(penv);// commit txn3
			abort_txn(penv);// abort txn2;
			for (int kk = 15; kk < 19; kk++) {
				keystr = const_cast<char*>(kstrs[kk].c_str());
				check_expr(smap.count(keystr) == 0);
			}
			txn2 = begin_txn(0, penv);
			_DB_STL_TEST_SET_TXN_NAME(txn2);
			break;
		case 20:
			txn3 = begin_txn(0, penv);
			_DB_STL_TEST_SET_TXN_NAME(txn3);
			break;
		case 22:
	// txn3 is unresolved, after commit txn2, txn3 should have been commited
			commit_txn(penv, txn2);
			for (int kk = 19; kk < 22; kk++) {
				keystr = const_cast<char*>(kstrs[kk].c_str());
				check_expr(smap.count(keystr) > 0);
			}
			txn2 = begin_txn(0, penv);
			_DB_STL_TEST_SET_TXN_NAME(txn2);
			break;
		case 23:
			txn3 = begin_txn(0, penv);
			_DB_STL_TEST_SET_TXN_NAME(txn3);
			break;
		case 25:
	// txn3 is unresolved, after abort txn2, txn3 should have been aborted
			abort_txn(penv, txn2);
			for (int kk = 22; kk < 24; kk++) {
				keystr = const_cast<char*>(kstrs[kk].c_str());
				check_expr(smap.count(keystr) == 0);
			}
			
			break;

		default:
			break;

		}//switch
		
		smap.insert(make_pair(kstr, dstr));
	}// for

	if (commit_or_abort == 0) {
		
		// if txn1 commits,  0 1 2 6 7 8 12 13 14 19 20 21 25, 26 
		idxset.insert(0);
		idxset.insert(1);
		idxset.insert(2);
		idxset.insert(6);
		idxset.insert(7);
		idxset.insert(8);
		idxset.insert(12);
		idxset.insert(13);
		idxset.insert(14);
		idxset.insert(19);
		idxset.insert(20);
		idxset.insert(21);
		idxset.insert(25);
		
		
		
		for (int kk = 0; kk < 26; kk++) {
			keystr = const_cast<char*>(kstrs[kk].c_str());
			sizet1 = smap.count(keystr);
			if (idxset.count(kk) > 0) {
				check_expr(sizet1 > 0);
			} else {
				check_expr(sizet1 == 0);
			}
		}

		commit_or_abort = 1;
		smap.clear();
		commit_txn(penv, txn1);
		goto commit_abort;
	} 

	if (commit_or_abort != 0){
		abort_txn(penv, txn1);// non could have been inserted
		begin_txn(0, penv);// put this call in a txnal context
		check_expr((sizet1 = smap.size()) == 0);
		commit_txn(penv);
	}
	 
	if (TEST_AUTOCOMMIT)
		dbstl::commit_txn(penv);
	

	 return;
} // test_nested_txns


// Testing miscellaneous functions.
void TestAssoc::test_etc()
{
	set_global_dbfile_suffix_number(65536);
	DbEnv *penvtmp = dbstl::open_env(".", 0, DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL);
	dbstl::close_db_env(penvtmp);
	db_mutex_t mtxhdl = dbstl::alloc_mutex();
	lock_mutex(mtxhdl);
	unlock_mutex(mtxhdl);
	free_mutex(mtxhdl);

	try {
	throw_bdb_exception("test_assoc::test_etc()", DB_LOCK_DEADLOCK);
	} catch (DbDeadlockException e1)
	{}

	try {
	throw_bdb_exception("test_assoc::test_etc()", DB_LOCK_NOTGRANTED);
	} catch (DbLockNotGrantedException e2)
	{}

	try {
	throw_bdb_exception("test_assoc::test_etc()", DB_REP_HANDLE_DEAD);
	} catch (DbRepHandleDeadException e13)
	{}

	try {
	throw_bdb_exception("test_assoc::test_etc()", DB_RUNRECOVERY);
	} catch (DbRunRecoveryException e4)
	{}

	try {
	throw_bdb_exception("test_assoc::test_etc()", 12345);
	} catch (DbException e1)
	{}

	try {
	throw_bdb_exception("test_assoc::test_etc()", 54321);
	} catch (DbException e1)
	{}

	ptint_vector vctr;
	vctr.set_all_flags(1, 1, 1);
	check_expr(vctr.get_commit_flags() == 1);
	check_expr(vctr.get_cursor_open_flags() == 1);
	check_expr(vctr.get_db_open_flags() & DB_CREATE);
	check_expr(vctr.get_txn_begin_flags() == 1);
	vctr.get_db_set_flags();
	vctr.set_commit_flags(2);
	check_expr(vctr.get_commit_flags() == 2);
	vctr.set_cursor_open_flags(2);
	check_expr(vctr.get_cursor_open_flags() == 2);
	vctr.set_txn_begin_flags(2);
	check_expr(vctr.get_txn_begin_flags() == 2);


}