diff --git a/YASI_12/YASI_12.vcxproj b/YASI_12/YASI_12.vcxproj
index 8ca9c31..0f47768 100644
--- a/YASI_12/YASI_12.vcxproj
+++ b/YASI_12/YASI_12.vcxproj
@@ -88,6 +88,7 @@
false
+
diff --git a/YASI_12/YASI_12.vcxproj.filters b/YASI_12/YASI_12.vcxproj.filters
index f5945f1..7196712 100644
--- a/YASI_12/YASI_12.vcxproj.filters
+++ b/YASI_12/YASI_12.vcxproj.filters
@@ -98,5 +98,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/YASI_12/ds.binarytreebase.h b/YASI_12/ds.binarytreebase.h
index 332889d..0e47c10 100644
--- a/YASI_12/ds.binarytreebase.h
+++ b/YASI_12/ds.binarytreebase.h
@@ -267,9 +267,9 @@ namespace yasi{
// inorder iterator
template< class E, class Node>
- class InorderBinaryTreeIterator : public NodeIterator{
+ class InorderBinaryTreeIterator : public ForwardNodeIterator>{
typedef InorderBinaryTreeIterator self;
- typedef NodeIterator base;
+ typedef ForwardNodeIterator> base;
protected:
BinaryTreeBase* t;
public:
@@ -278,9 +278,9 @@ namespace yasi{
InorderBinaryTreeIterator(const BinaryTreeBase* t, Node* pNode) : base(pNode), t(t){}
InorderBinaryTreeIterator(const self& other) : base(other.pNode), t(other.t){}
// operators
- self& operator ++ ()/*prefix*/{ pNode = t->inorderSuccessor(pNode); return *this; }
- self operator ++ (int)/*postfix*/{ self temp = *this; pNode = t->inorderSuccessor(pNode); return temp; }
- self& operator = (const self& other){ this->pNode = other.pNode; this->t = other.t; *this; }
+ virtual self& operator ++ ()/*prefix*/{ pNode = t->inorderSuccessor(pNode); return *this; }
+ virtual self operator ++ (int)/*postfix*/{ self temp = *this; pNode = t->inorderSuccessor(pNode); return temp; }
+ virtual self& operator = (const self& other){ this->pNode = other.pNode; this->t = other.t; *this; }
};
} // namespace ds
diff --git a/YASI_12/ds.doublylinkedlist.h b/YASI_12/ds.doublylinkedlist.h
index f904df5..bdefafe 100644
--- a/YASI_12/ds.doublylinkedlist.h
+++ b/YASI_12/ds.doublylinkedlist.h
@@ -8,44 +8,6 @@ using namespace std;
namespace yasi{
namespace ds{
- template< class E, class Node>
- class DListNodeBase : public virtual ListNodeBase{
- typedef Node node_t;
- protected:
- node_t* pPrev;
- public:
- DListNodeBase(): pPrev(NULL) { }
- DListNodeBase(const E& e) : ListNodeBase(e), pPrev(NULL) {}
- virtual ~DListNodeBase(){ pPrev = NULL; }
- virtual node_t* prev(){ return pPrev; }
- virtual void setPrev(node_t* pNode){ pPrev = pNode; }
- };
-
- template
- class DListNode : public virtual DListNodeBase< E, DListNode >{
- typedef DListNode node;
- public:
- virtual ~DListNode(){
- pPrev = pNext = NULL;
- }
- DListNode(){}
- // because of virtual inheritance, we need to initialize
- // all virtual base constructors from here
- DListNode(const E& elem): DListNodeBase >(elem), NodeBase(elem){}
- // to be in sync with ListNode class
- DListNode(const E& elem, node* pNext) : DListNodeBase >(elem), NodeBase(elem){
- this->pNext = pNext;
- }
- DListNode(
- const E& elem,
- node* pNext,
- node* pPrev) :
- NodeBase(elem),
- DListNodeBase >(elem){
- this->pNext = pNext;
- this->pPrev = pPrev;
- }
- };
// doubly linked list interface
template >
@@ -64,18 +26,20 @@ namespace ds{
// doubly linked list iterator
template
- class DLLIterator : public SLLIterator< E, Node > {
+ class DLLIterator : public virtual BidirectionalNodeIterator< E, Node, DLLIterator > {
typedef DLLIterator self;
- typedef SLLIterator base;
+ typedef BidirectionalNodeIterator< E, Node, DLLIterator > base;
public:
virtual ~DLLIterator(){}
DLLIterator() {}
- DLLIterator(Node* pNode) : base(pNode){}
- DLLIterator(const self& other) : base(other.pNode){}
- // operators
- self& operator -- ()/*prefix*/{ pNode = pNode->prev(); return *this; }
- self operator -- (int)/*postfix*/{ self temp = *this; pNode = pNode->prev(); return temp; }
- self& operator = (const self& other){ this->pNode = other.pNode; return *this; }
+ DLLIterator(Node* pNode) : IteratorBase(pNode){}
+ DLLIterator(const base& other) : IteratorBase(other.pNode){}
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
+ virtual self operator ++ (int){ self temp(this->pNode); incr(); return temp; }
+ virtual self operator -- (int){ self temp(this->pNode); decr(); return temp; }
+ virtual self& operator ++ (){ incr(); return *this; }
+ virtual self& operator -- (){ decr(); return *this; }
+ //virtual E& operator*(){ if (pNode) return pNode->element; }
};
// doubly linked list class
@@ -89,6 +53,10 @@ namespace ds{
protected:
// typedefs
typedef Node node_t;
+ public:
+ typedef DLLIterator iterator;
+ typedef node_t NodeType;
+ protected:
// new members
//node_t* pTail;
diff --git a/YASI_12/ds.hashtable.h b/YASI_12/ds.hashtable.h
index 34ae839..77cc643 100644
--- a/YASI_12/ds.hashtable.h
+++ b/YASI_12/ds.hashtable.h
@@ -2,6 +2,7 @@
#include "common.h"
#include "ds.kvpair.h"
#include "ds.doublylinkedlist.h"
+#include "ds.iterator.h"
using namespace std;
namespace yasi{
@@ -15,6 +16,8 @@ class IKeyValueStore{
virtual void put(const Key&, const Value&) = 0;
virtual bool contains(const Key&) const = 0;
virtual void remove(const Key&) = 0;
+ virtual void grow() = 0; // doubles the capacity
+ virtual void shrink() = 0; // halves the capacity
};
template
@@ -48,31 +51,55 @@ template< class Key,
>
class HashTableBase : public IKeyValueStore < Key, Value >{
protected:
+ enum {
+ RESIZE_GROW = 0,
+ RESIZE_SHRINK
+ };
// size of the table
// must be a power of two
unsigned int _size;
unsigned int _logSize;
+ static const unsigned int INIT_LOGSIZE = 5; // 32 elements
// actual number of keys stored in the table
unsigned int _population;
// the buckets array
BucketType* table;
// the hash function
HashFunction hash;
+ // load factor for growth and shrinkage
+ const float DENSITY_MAX;
+ const float DENSITY_MIN;
+ // compute hashCode modulo table size
inline int index(const Key& k) const{
return hash(k) & ((1 << _logSize) - 1); // hash(k) % _size
}
BucketType& bucket(const Key& k) const{
return table[index(k)];
}
+ void initTable(BucketType** pTable, const int numElems){
+ // initialize to zero
+ memset(*pTable, 0, numElems * sizeof(BucketType));
+ }
+ inline float density(){ return ((float) _population) / _size; }
public:
// the type of the entries in the hash table
- HashTableBase():HashTableBase(6){}
- HashTableBase(unsigned int logSize) : _logSize(logSize), _size(1 << logSize), _population(0){
+ HashTableBase():HashTableBase(INIT_LOGSIZE){}
+ HashTableBase(unsigned int logSize = INIT_LOGSIZE) : _logSize(logSize), _size(1 << logSize), _population(0), DENSITY_MAX(0.75), DENSITY_MIN(0.25){
table = new BucketType[_size];
- // initialize to zero
- memset(table, 0, _size * sizeof(BucketType));
+ initTable(&table, _size);
+ }
+ virtual ~HashTableBase(){ DELETE_ARR_SAFE(table); _size = _population = _logSize = 0; }
+
+ string toString(){
+ stringstream buf;
+ for (int i = 0; i < _size; i++){
+ buf << "[" << i << "] ";
+ if (table[i]) buf << table[i]->toString();
+ buf << endl;
+ }
+ return buf.str();
}
- virtual ~HashTableBase(){ DELETE_ARR_SAFE(table); _size = _population = 0; }
+
};
@@ -99,6 +126,7 @@ class SeparateChainingHashTable : public HashTableBase<
typedef typename List::NodeType Node;
public:
typedef List* BucketType;
+ typedef List ListType;
protected:
// returns the pointer to the value if exists, otherwise returns NULL
Node* bucketNode(BucketType pList, const Key& k) const{
@@ -146,8 +174,7 @@ class SeparateChainingHashTable : public HashTableBase<
}
}
}
- SeparateChainingHashTable() :SeparateChainingHashTable(6){}
- SeparateChainingHashTable(unsigned int logSize) : HashTableBase(logSize){
+ SeparateChainingHashTable(unsigned int logSize = INIT_LOGSIZE) : HashTableBase(logSize){
//collisionHandler(this);
}
@@ -157,7 +184,7 @@ class SeparateChainingHashTable : public HashTableBase<
BucketType pList = table[i];
if (pList == NULL){
// empty slot; create a new list
- pList = new List();
+ pList = table[i] = new List();
// pushFront for better temporal locality
pList->pushFront(Pair(k, v));
_population++;
@@ -174,6 +201,11 @@ class SeparateChainingHashTable : public HashTableBase<
_population++;
}
}
+ // do we need to grow?
+ if (density() >= DENSITY_MAX) grow();
+ }
+ virtual void put(const Key& k) {
+ put(k, k);
}
virtual Value* get(const Key& k) const override{
int i = index(k);
@@ -199,6 +231,8 @@ class SeparateChainingHashTable : public HashTableBase<
if (pNode){
pList->remove(pNode);
_population--;
+ // do we need to shrink?
+ if (density() <= DENSITY_MIN) shrink();
}
}
}
@@ -215,6 +249,57 @@ class SeparateChainingHashTable : public HashTableBase<
int size(){ return _size; }
int population(){ return _population; }
+ // grows/shrinks by specified logFactor
+ // that is, newSize is either oldSize << logFactor (grow)
+ // or oldSize >> logFactor (shrink)
+ void resize(int growOrShrink, unsigned int logFactor = 1) {
+ unsigned int oldLogSize = _logSize;
+ unsigned int oldSize = _size;
+ unsigned int oldPopulation = _population;
+ BucketType* oldTable = table;
+ unsigned int newLogSize, newSize;
+
+ if (growOrShrink == RESIZE_GROW){
+ newLogSize = _logSize + logFactor;
+ newSize = _size << logFactor;
+ }
+ else if (growOrShrink == RESIZE_SHRINK){
+ newLogSize = _logSize - logFactor;
+ newSize = _size >> logFactor;
+ }
+ else{
+ // do nothing
+ return;
+ }
+
+ // great; now we either grow or shrink
+ _logSize = newLogSize;
+ _size = newSize;
+ _population = 0;
+ table = new BucketType[newSize]; // twice the current size
+ initTable(&table, newSize); // initialize with zero
+
+ // copy old elements
+ for (unsigned int i = 0; i < oldSize; i++){
+ BucketType pList;
+ if (pList = oldTable[i]){
+ // bucket exists; copy elements
+ for (List::iterator n = pList->begin(); n != pList->end(); n++){
+ Pair p = *n;
+ put(p.key, p.value);
+ }
+ // now delete bucket
+ pList->clear();
+ DELETE_SAFE(oldTable[i]);
+ }
+ }
+ // now delete oldTable
+ DELETE_ARR_SAFE(oldTable);
+ }
+ // doubles the size and copy existing items
+ void grow() override { resize(RESIZE_GROW); }
+ // halves the size and copy existing items
+ void shrink() override { resize(RESIZE_SHRINK); }
};
class SeparateChainingHashTableTest : public yasi::Test{
@@ -222,17 +307,178 @@ class SeparateChainingHashTableTest : public yasi::Test{
typedef SeparateChainingHashTable IntHashTable;
typedef KVPair Pair;
typedef IntHashTable::BucketType BucketType;
+ typedef IntHashTable::ListType ListType;
IntHashTable h;
public:
void hashCode(){
// hashcode must be within range
- ASSERT_LT(h.index(10), h.size()) << "index(10) out of range";
+ srand((unsigned int)time(NULL));
+ for (int i = 0; i < 1000; i++){
+ int n = rand();
+ int index = h.index(n);
+ ASSERT_LT(index, h.size()) << "index(" << n << ") out of range";
+ ASSERT_GE(index, 0) << "index(" << n << ") out of range";
+ }
+ }
+
+ void all(){
+ srand((unsigned int)time(NULL));
+ int logSize = h._logSize;
+ int size = h._size;
+ ASSERT_EQ(IntHashTable::INIT_LOGSIZE, logSize) << "initial logSize not " << IntHashTable::INIT_LOGSIZE;
+ ASSERT_EQ(1 << logSize, h.size()) << "table size not 2^" << logSize;
+ for (int i = 0; i < h.size(); i++){
+ ASSERT_EQ(0, (int)h.table[i]) << "table[i] not NULL for i=" << i;
+ }
+
+ {
+ SCOPED_TRACE("add items in hashtable");
+ // now add 10 items; should not trigger grow()
+ ASSERT_LT(10 / h.size(), h.DENSITY_MAX) << "10/" << h.size() << " items triggered grow() while DENSITY_MAX=" << h.DENSITY_MAX;
+ for (int i = 0; i < 10; i++){
+ h.put(i, i);
+ }
+ // test contains()
+ for (int i = 0; i < 10; i++){
+ ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found";
+ }
+ ASSERT_EQ(false, h.contains(100)) << "absent key 100 was found";
+ // test get()
+ for (int i = 0; i < 10; i++){
+ int* pValue = h.get(i);
+ ASSERT_NE(NULL, (int)pValue) << "pValue with key " << i << " NULL";
+ ASSERT_EQ(i, *pValue) << "value with key " << i << " mismatch";
+ }
+ ASSERT_EQ(NULL, h.get(100)) << "absent key 100 was found";
+ // test duplicate insert
+ // should result in update
+ h.put(5, -6);
+ int* pValue = h.get(5);
+ ASSERT_NE(NULL, (int)pValue) << "pValue with key " << 5 << " NULL";
+ ASSERT_EQ(-6, *pValue) << "value with key " << 5 << " not -6";
+
+ // now add some more but don't trigger grow()
+ size = h.size();
+ int maxCurrent = ((int)(h.size() * h.DENSITY_MAX)) - 1;
+ int currentPop = h.population();
+ for (int i = currentPop; i < maxCurrent; i++){
+ // this insertion should not trigger grow()
+ h.put(i, i);
+ }
+ ASSERT_EQ(maxCurrent, h.population()) << "population not maxCurrent";
+ ASSERT_EQ(size, h.size()) << "size() not size";
+ // this insertion should trigger grow()
+ int key = rand();
+ while (h.contains(key)){ key = rand(); }
+ h.put(key, key); // should trigger grow()
+ ASSERT_EQ(size * 2, h.size()) << "size() not 2*oldSize";
+ ASSERT_EQ(maxCurrent + 1, h.population()) << "population() not maxCurrent+1";
+ ASSERT_GE(0.375, h.density()) << "density() > 0.375";
+
+ // print the table
+ string str = h.toString();
+ cout << "after grow(): " << endl << str << endl;
+
+ // check that all old entries are still in the table
+ for (int i = 0; i < 10; i++){ // first 10 entries
+ int v = i;
+ if (i == 5){
+ // remember that we had updated an entry
+ v = -6;
+ }
+ ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found in new table";
+ ASSERT_EQ(v, *h.get(i)) << "value with key " << i << " incorrect in new table";
+ }
+ for (int i = currentPop; i < maxCurrent; i++){ // further entries till max capacity before grow
+ ASSERT_EQ(true, h.contains(i)) << "key " << i << " not found in new table";
+ ASSERT_EQ(i, *h.get(i)) << "value with key " << i << " incorrect in new table";
+ }
+ // the entry that triggered grow()
+ ASSERT_EQ(true, h.contains(key)) << "key " << key << " not found in new table";
+ ASSERT_EQ(key, *h.get(key)) << " value with key " << key << " incorrect in new table";
+ }
+
+ {
+ SCOPED_TRACE("remove");
+ // now remove entries but do not trigger shrink()
+ int i = 0;
+ BucketType pCriticalBucket = NULL;
+ DoublyLinkedList survivedKeys;
+ int initPop = h.population();
+ int numRemoved = 0;
+ bool removedEnough = false;
+ for (; i < h.size(); i++){
+ BucketType pList = h.table[i];
+ if (pList){
+ // number of entries touched: either removed or copied
+ int remainingItems = pList->size();
+
+ while (remainingItems > 0){
+ // check if we can remove this node without causing shrink()
+ if (!removedEnough &&
+ ((float)(h._population - 1)) / h._size <= h.DENSITY_MIN){
+ // this deletion will cause shrink()
+ pCriticalBucket = pList;
+ removedEnough = true;
+ }
+ else{
+ Pair kvPair = (*pList->begin());
+ int key = kvPair.key;
+ if (removedEnough == false){
+ // remove an entry
+ int prevPop = h._population;
+ int prevSize = h.size();
+ h.remove(key);
+ ASSERT_EQ(prevPop - 1, h._population) << "remove did not work";
+ ASSERT_EQ(h._size, prevSize) << "size changed by remove";
+ ASSERT_EQ(false, h.contains(key)) << "removed key " << key << " still remains";
+ numRemoved++;
+ remainingItems--;
+ }
+ else{
+ // copy this should-be-surviving entry into a list for further verification
+ survivedKeys.push(kvPair);
+ remainingItems--;
+ }
+ }
+ }
+ }
+ } // for loop through all slots
+
+ ASSERT_EQ(initPop - numRemoved, survivedKeys.size()) << "initSize+numRemoved not survivedKeys.size";
+ if (removedEnough) {
+ // ok; removing next key should cause shrink()
+ int prevPop = h._population;
+ int prevSize = h.size();
+ int removedKey = (*pCriticalBucket->begin()).key;
+ h.remove(removedKey);
+ cout << "shrinked: \n" << h.toString() << endl;
+ ASSERT_EQ(h._population, prevPop - 1) << "remove did not work";
+ ASSERT_EQ(h._size, prevSize >> 1) << "size did not shrink by half";
+ ASSERT_EQ(false, h.contains(removedKey)) << "removed key " << removedKey << " still remains";
+
+ // now check that all should-have-survived keys are still present in the new table
+ for (DoublyLinkedList::iterator it = survivedKeys.begin(); it != survivedKeys.end(); it++){
+ int key = (*it).key;
+ if (key == removedKey) continue; // this is the removed key that made the table shrink
+ int value = (*it).value;
+ ASSERT_EQ(true, h.contains(key)) << "key " << key << " absent in shrinked table";
+ ASSERT_NE(NULL, (int)h.get(key)) << "get(" << key << ") is NULL in shrinked table";
+ ASSERT_EQ(value, *(h.get(key))) << "get(" << key << ") not " << value << "in shrinked table";
+ }
+
+ }
+ }
+
+
+ //ASSERT_EQ(0, 1) << "incomplete test";
}
};
ADD_TEST_F(SeparateChainingHashTableTest, hashCode);
+ADD_TEST_F(SeparateChainingHashTableTest, all);
} // namespace ds
} // namespace yasi
\ No newline at end of file
diff --git a/YASI_12/ds.iterator.h b/YASI_12/ds.iterator.h
index a65c02b..6ce732b 100644
--- a/YASI_12/ds.iterator.h
+++ b/YASI_12/ds.iterator.h
@@ -2,44 +2,189 @@
#include "common.h"
#include "utils.h"
#include "ds.node.h"
+#include "test.h"
using namespace std;
namespace yasi{
namespace ds{
+ template< class Elem,
+ class Iterated,
+ class Iterator >
+ class IteratorBase{
+ ////////////////// enable testing ////////////////
+ friend class IteratorTest;
+
+ typedef IteratorBase self;
+ protected:
+ // this is protected because child classes use `Node' as
+ // template argument passed as `Iterated' in this base class
+ typedef Iterated Node;
+ public:
+ // a member of items that are iterated through
+ Iterated* pNode;
+
+ virtual ~IteratorBase(){ pNode = NULL; }
+ IteratorBase() : pNode(NULL){}
+ IteratorBase(Node* pNode) : pNode(pNode){}
+ IteratorBase(const self& other) : pNode(other.pNode){}
+ //IteratorBase(const IteratorBase& other) : pNode(other.pNode){}
+ // operators
+ virtual Elem& operator* () const = 0;
+
+ virtual bool operator == (const self& other) const { return this->pNode == other.pNode; }
+ virtual bool operator != (const self& other) const { return this->pNode != other.pNode; }
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
+ };
+
+ // forward iterator
+ template< class E,
+ class Iterated,
+ class Iterator >
+ class ForwardIterator :public virtual IteratorBase{
+ ////////////////// enable testing ////////////////
+ friend class IteratorTest;
+
+ typedef IteratorBase base;
+ typedef ForwardIterator self;
+ protected:
+ // children must implement their increment method
+ virtual void incr() = 0;
+ public:
+ virtual ~ForwardIterator(){}
+ ForwardIterator(){}
+ ForwardIterator(Iterated* pNode) :IteratorBase(pNode){}
+ ForwardIterator(const self& other) :IteratorBase(other.pNode){}
+ self& operator ++ (){ incr(); return self(pNode); }
+ //virtual self operator ++ (int){ self temp(this->pNode); incr(); return temp; }
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
+ };
+
+ // backward iterator
+ template< class E,
+ class Iterated,
+ class Iterator>
+ class BackwardIterator :public virtual IteratorBase{
+ ////////////////// enable testing ////////////////
+ friend class IteratorTest;
+
+ typedef IteratorBase base;
+ typedef BackwardIterator self;
+ protected:
+ // children must implement their decrement method
+ virtual void decr() = 0;
+ public:
+ virtual ~BackwardIterator(){}
+ BackwardIterator(){}
+ BackwardIterator(Iterated* pNode) :IteratorBase(pNode){}
+ BackwardIterator(const self& other) :IteratorBase(other.pNode){}
+ self& operator -- (){ decr(); return *this; }
+ //self& operator -- (int){ self temp = *this; decr(); return temp; }
+ //virtual self operator -- (int){ self temp(this->pNode); decr(); return temp; }
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
+ };
+
+ // bidirectional iterator
+ template< class E,
+ class Iterated,
+ class Iterator
+ >
+ class BidirectionalIterator :
+ public virtual ForwardIterator,
+ public virtual BackwardIterator{
+ ////////////////// enable testing ////////////////
+ friend class IteratorTest;
+
+ typedef ForwardIterator base1;
+ typedef BackwardIterator base2;
+ typedef BidirectionalIterator self;
+ protected:
+ // children must implement their increment/decrement method
+ public:
+ virtual ~BidirectionalIterator(){}
+ BidirectionalIterator(){}
+ BidirectionalIterator(Iterated* pNode) :IteratorBase(pNode){}
+ BidirectionalIterator(const self& other) :IteratorBase(other.pNode){}
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
+ };
+
+
+
// node iterator base
- template
- class NodeIteratorBase{
- typedef NodeIteratorBase self;
+ template
+ class NodeIteratorBase : public virtual ForwardIterator{
+ ////////////////// enable testing ////////////////
+ friend class IteratorTest;
+
+ typedef ForwardIterator base;
+ typedef NodeIteratorBase self;
+ protected:
+ public:
+ virtual ~NodeIteratorBase(){}
+
+ NodeIteratorBase() : ForwardIterator(){}
+ NodeIteratorBase(Node* pNode) : IteratorBase(pNode){}
+ NodeIteratorBase(const self& other) : IteratorBase(other.pNode){}
+ // operators
+ virtual E& operator* () const override { E e; if (pNode) return pNode->element; else return e; }
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
+
+ };
+
+ // forward node iterator
+ // class Node must have next() method and `element' public member
+ template // E is the value type
+ class ForwardNodeIterator : public NodeIteratorBase{
+ ////////////////// enable testing ////////////////
+ friend class IteratorTest;
+
+ typedef NodeIteratorBase base;
+ typedef ForwardNodeIterator self;
+ protected:
+ virtual void incr() override{ pNode = pNode->next(); }
public:
- virtual ~NodeIteratorBase(){ pNode = NULL; }
- Node* pNode;
+ virtual ~ForwardNodeIterator(){ }
- NodeIteratorBase() : pNode(NULL){}
- NodeIteratorBase(Node* pNode) : pNode(pNode){}
- NodeIteratorBase(const self& other) : pNode(other.pNode){}
+ ForwardNodeIterator() {}
+ ForwardNodeIterator(Node* pNode) : IteratorBase(pNode){}
+ ForwardNodeIterator(const self& other) : IteratorBase(other.pNode){}
// operators
- E& operator* () const { if (pNode) return pNode->element; }
+ //virtual E& operator* () const { return pNode->element; };
self& operator = (const self& other){ this->pNode = other.pNode; return *this; }
- bool operator == (const self& other) const { return this->pNode == other.pNode; }
- bool operator != (const self& other) const { return this->pNode != other.pNode; }
+ self operator ++ (int){ self temp(this->pNode); incr(); return temp; }
+ self& operator ++ (){ incr(); return *this; }
};
- // node iterator
- template // E is the value type
- class NodeIterator : public NodeIteratorBase{
- typedef NodeIteratorBase base;
- typedef NodeIterator self;
+ // bidirectional node iterator
+ // class Node must support prev() and next() methods and `element' public member
+ template // E is the value type
+ class BidirectionalNodeIterator :
+ public virtual NodeIteratorBase,
+ public virtual BidirectionalIterator{
+
+ typedef NodeIteratorBase base;
+ typedef ForwardIterator base1; // for virtual inheritance
+ typedef BackwardIterator base2; // for virtual inheritance
+ typedef BidirectionalNodeIterator self;
+ protected:
+ virtual void incr() override{ pNode = pNode->next(); }
+ virtual void decr() override{ pNode = pNode->prev(); }
public:
- virtual ~NodeIterator(){ }
+ virtual ~BidirectionalNodeIterator(){ }
- NodeIterator() {}
- NodeIterator(Node* pNode) : base(pNode){}
- NodeIterator(const self& other) : base(other.pNode){}
+ BidirectionalNodeIterator() {}
+ BidirectionalNodeIterator(Node* pNode) : IteratorBase(pNode){}
+ BidirectionalNodeIterator(const self& other) : IteratorBase(other.pNode){}
// operators
+ self operator ++ (int){ self temp(this->pNode); incr(); return temp; }
+ self operator -- (int){ self temp(this->pNode); decr(); return temp; }
+ self& operator ++ (){ incr(); return *this; }
+ self& operator -- (){ decr(); return *this; }
self& operator = (const self& other){ this->pNode = other.pNode; return *this; }
};
+
+
} // namespace ds
} // namespace yasi
\ No newline at end of file
diff --git a/YASI_12/ds.iterator_test.h b/YASI_12/ds.iterator_test.h
new file mode 100644
index 0000000..1a0dd58
--- /dev/null
+++ b/YASI_12/ds.iterator_test.h
@@ -0,0 +1,50 @@
+#pragma once
+#include "ds.iterator.h"
+#include "ds.node.h"
+using namespace std;
+
+namespace yasi{
+ namespace ds{
+ ///////////////// test ///////////////
+ class IteratorTest :public yasi::Test{
+
+ // derive from forward node iterator
+ template
+ class ForwardNodeIteratorDerived : public ForwardNodeIterator < E, Node, ForwardNodeIteratorDerived > {
+ typedef ForwardNodeIterator < E, Node, ForwardNodeIteratorDerived > base;
+ public:
+ virtual ~ForwardNodeIteratorDerived(){}
+ ForwardNodeIteratorDerived(Node* pNode) : base(pNode){}
+ };
+
+ public:
+ void forwardNodeIterator(){
+ typedef SListNode Node;
+ typedef ForwardNodeIteratorDerived Iterator;
+
+ // create a list of nodes
+ Node* n[10];
+ for (int i = 0; i < 10; i++){
+ n[i] = new Node(i);
+ if (i > 0) n[i - 1]->setNext(n[i]);
+ }
+
+ // create iterator
+ Iterator it(n[0]);
+ int i = 0;
+ do{
+ ASSERT_EQ(it.pNode, n[i]) << "wrong iterator node at i=" << i;
+ ASSERT_EQ(*it, i) << "wrong iterator value at i=" << i;
+ ASSERT_EQ(it.pNode, (it++).pNode) << "wrong postfix increment at i=" << i;
+ ASSERT_EQ(it.pNode->next(), (++it).pNode) << "wrong postfix increment at i=" << i;
+ // it already incremente
+ i++;
+ } while (it.pNode != NULL);
+ ASSERT_EQ(11, i) << "i not 11";
+ }
+ };
+
+ ADD_TEST_F(IteratorTest, forwardNodeIterator);
+
+ }
+}
\ No newline at end of file
diff --git a/YASI_12/ds.node.h b/YASI_12/ds.node.h
index 24acbd9..3d7d65b 100644
--- a/YASI_12/ds.node.h
+++ b/YASI_12/ds.node.h
@@ -10,13 +10,124 @@ namespace yasi{
E element;
NodeBase(){}
NodeBase(const E& e) : element(e){}
- //NodeBase(const self& other) : element(other.e){}
- //self& operator=(const self& other){
- // element = other.element;
- // return *this;
- //}
+ NodeBase(const self& other) : element(other.element){}
+
+ self& operator=(const self& other){
+ element = other.element;
+ return *this;
+ }
void setElement(const E& e){ this->element = e; }
};
- }
-}
\ No newline at end of file
+ // list node base
+ template
+ class SListNodeBase : public virtual NodeBase{
+ typedef Node node_t;
+ typedef NodeBase base;
+ typedef SListNodeBase self;
+ protected:
+ node_t* pNext;
+ public:
+
+ SListNodeBase() : pNext(NULL){}
+ SListNodeBase(node_t* pNode) : pNext(pNode){}
+ SListNodeBase(const E& elem) : base(elem), pNext(NULL){ }
+ SListNodeBase(const E& elem, node_t* pNode) : base(elem), pNext(pNode){
+ element = elem;
+ }
+ SListNodeBase(const self& other) : base(other.elem), pNext(other.pNext){ }
+ self& operator=(const self& other){
+ e = other.element;
+ pNext = other.pNext;
+ return *this;
+ }
+ virtual ~SListNodeBase(){ pNext = NULL; }
+ virtual node_t* next(){ return pNext; }
+ virtual void setNext(node_t* pNode){ pNext = pNode; }
+ };
+
+
+ // singly linkedlist node
+ template // E is the element class
+ class SListNode : public virtual SListNodeBase< E, SListNode >{
+ typedef SListNode Node;
+ typedef SListNodeBase > base;
+ typedef SListNode self;
+ public:
+ SListNode(){}
+ SListNode(const E& elem) : base(elem){}
+ /////////////////////////////////////
+ //// very important: since we are doing virtual inheritance,
+ //// we need to specify all vertual constructors in the initializer list
+ //// Had we done non-virtual inheritance from ListNode<--ListNodeBase<--NodeBase,
+ //// it would suffice to use ListNodeBase constructor only.
+ ////////////////////////////////////
+ SListNode(const E& elem, Node* pNode) : base(pNode), NodeBase(elem){ }
+ SListNode(const self& other) : base(other.elem,other.pNode), NodeBase(other.elem){ }
+ self& operator=(const self& other){
+ e = other.element;
+ pNext = other.pNext;
+ return *this;
+ }
+ virtual ~SListNode(){}
+ };
+ template< class E, class Node>
+ class DListNodeBase : public virtual SListNodeBase{
+ typedef Node node_t;
+ typedef SListNodeBase > base;
+ typedef DListNodeBase self;
+ protected:
+ node_t* pPrev;
+ public:
+ DListNodeBase() : pPrev(NULL) { }
+ DListNodeBase(node_t* pPrev) : pPrev(pPrev){}
+ DListNodeBase(const E& e) : NodeBase(e), pPrev(NULL) {}
+ DListNodeBase(const E& e, node_t* pPrev) : NodeBase(e), pPrev(pPrev) {}
+ DListNodeBase(const self& other) : base(other.elem, other.pNext), pPrev(other.pPrev){ }
+ self& operator=(const self& other){
+ e = other.element;
+ pNext = other.pNext;
+ pPrev = other.pPrev;
+ return *this;
+ }
+ virtual ~DListNodeBase(){ pPrev = NULL; }
+ virtual node_t* prev(){ return pPrev; }
+ virtual void setPrev(node_t* pNode){ pPrev = pNode; }
+ };
+
+ template
+ class DListNode : public virtual DListNodeBase< E, DListNode >{
+ typedef DListNode node;
+ typedef DListNodeBase < E, DListNode > base;
+ typedef DListNode self;
+ public:
+ virtual ~DListNode(){
+ pPrev = pNext = NULL;
+ }
+ DListNode(){}
+ // because of virtual inheritance, we need to initialize
+ // all virtual base constructors from here
+ DListNode(const E& elem) : NodeBase(elem){}
+ // to be in sync with ListNode class
+ DListNode(const E& elem, node* pNext) : SListNodeBase(pNext), NodeBase(elem){
+ this->pNext = pNext;
+ }
+ DListNode(
+ const E& elem,
+ node* pNext,
+ node* pPrev) :
+ NodeBase(elem),
+ SListNodeBase(pNext),
+ DListNodeBase(pPrev){
+ }
+ DListNode(const self& other) : base(other.elem, other.pPrev), NodeBase(other.elem),SListNodeBase(other.pNext){ }
+ self& operator=(const self& other){
+ e = other.element;
+ pNext = other.pNext;
+ pPrev = other.pPrev;
+ return *this;
+ }
+
+ };
+ } // namespace ds
+} // namespace yasi
\ No newline at end of file
diff --git a/YASI_12/ds.singlylinkedlist.h b/YASI_12/ds.singlylinkedlist.h
index a76d74f..24a1995 100644
--- a/YASI_12/ds.singlylinkedlist.h
+++ b/YASI_12/ds.singlylinkedlist.h
@@ -12,61 +12,6 @@ namespace yasi{
namespace ds{
-// list node base
-template
-class ListNodeBase: public virtual NodeBase{
- typedef Node node_t;
- typedef NodeBase base;
- typedef ListNodeBase self;
-protected:
- node_t* pNext;
-public:
-
- ListNodeBase() : pNext(NULL){}
- ListNodeBase(node_t* pNode) : pNext(pNode){}
- ListNodeBase(const E& elem) : base(elem), pNext(NULL){ }
- ListNodeBase(const E& elem, node_t* pNode) : NodeBase(elem), pNext(pNode){
- element = elem;
- }
- //ListNodeBase(const self& other) : element(other.e), pNext(other.pNext){}
- //self& operator=(const self& other){
- // e = other.element;
- // pNext = other.pNext;
- // return *this;
- //}
- virtual ~ListNodeBase(){ pNext = NULL; }
- virtual node_t* next(){ return pNext; }
- virtual void setNext(node_t* pNode){ pNext = pNode; }
-};
-
-
-// linkedlist node
-template // E is the element class
-class ListNode: public virtual ListNodeBase< E, ListNode >{
- typedef ListNode Node;
- typedef ListNodeBase > base;
- typedef ListNode self;
-public:
- ListNode(){}
- ListNode(const E& elem) : base(elem){}
- //ListNode(const self& other) {
- // e = other.element;
- // pNext = other.pNext;
- //}
- //self& operator=(const self& other){
- // e = other.element;
- // pNext = other.pNext;
- // return *this;
- //}
- /////////////////////////////////////
- //// very important: since we are doing virtual inheritance,
- //// we need to specify all vertual constructors in the initializer list
- //// Had we done non-virtual inheritance from ListNode<--ListNodeBase<--NodeBase,
- //// it would suffice to use ListNodeBase constructor only.
- ////////////////////////////////////
- ListNode(const E& elem, Node* pNode) : ListNodeBase >(pNode), NodeBase(elem){ }
- virtual ~ListNode(){}
-};
// singly linked list interface
template
@@ -88,19 +33,16 @@ class ISinglyLinkedList:virtual public IList{
// singly linked list iterator
// iterator
template< class E, class Node>
-class SLLIterator : public NodeIterator{
+class SLLIterator : public ForwardNodeIterator >{
typedef SLLIterator self;
- typedef NodeIterator base;
+ typedef ForwardNodeIterator > base;
protected:
public:
virtual ~SLLIterator(){ }
SLLIterator() {}
- SLLIterator(Node* pNode) : base(pNode){}
- SLLIterator(const self& other) : base(other.pNode){}
- // operators
- self& operator ++ ()/*prefix*/{ pNode = pNode->next(); return *this; }
- self operator ++ (int)/*postfix*/{ self temp = *this; pNode = pNode->next(); return temp; }
- self& operator = (const self& other){ this->pNode = other.pNode; return *this; }
+ SLLIterator(Node* pNode) : IteratorBase(pNode){}
+ SLLIterator(const self& other) : IteratorBase(other.pNode){}
+ self& operator=(const self& other){ pNode = other.pNode; return *this; }
};
@@ -108,7 +50,7 @@ class SLLIterator : public NodeIterator{
// virtual inheritance needed because
// (1) the IDoublyLinkedList will also inherit from ISinglyLinkedList, and
// (2) DoublyLinkedList will inherit from SinglyLinkedList
-template, class Iter = SLLIterator >
+template, class Iter = SLLIterator >
class SinglyLinkedList : public virtual ISinglyLinkedList < E, Node, Iter > {
///////////////// enable testing////////////////
friend class SinglyLinkedListTest;
@@ -161,7 +103,7 @@ class SinglyLinkedList : public virtual ISinglyLinkedList < E, Node, Iter > {
public:
// typedef SLLIterator iterator;
- SinglyLinkedList(){
+ SinglyLinkedList():_size(0){
//pHead = NULL;
pHorizon = new node_t();
pHorizon->setNext(pHorizon); // unit loop: horizon--horizon--horizon
@@ -336,18 +278,18 @@ class SinglyLinkedList : public virtual ISinglyLinkedList < E, Node, Iter > {
class SinglyLinkedListTest : public yasi::Test{
- typedef SinglyLinkedList >::node_t node_t;
- typedef SinglyLinkedList > IntList;
+ typedef SinglyLinkedList >::node_t node_t;
+ typedef SinglyLinkedList > IntList;
typedef IntList::iterator IntIterator;
public:
template
- void checkListElements(SinglyLinkedList >* pList, const E* srcArr, const int n){
+ void checkListElements(SinglyLinkedList >* pList, const E* srcArr, const int n){
E* pElems = new int[n];
int numElems;
- SinglyLinkedList >& list = *pList;
+ SinglyLinkedList >& list = *pList;
list.elements(&pElems);
ASSERT_EQ(true, arrcmp(srcArr, pElems, n)) << "arrays are not the same" << endl
<< "actual: " << arrToString(srcArr, n) << endl
@@ -420,6 +362,9 @@ class SinglyLinkedListTest : public yasi::Test{
IntList list;
ASSERT_EQ(0, list.size()) << "size should be zero";
ASSERT_EQ(0, (int)list.head()) << "head should be NULL";
+ ASSERT_EQ(list.begin(), list.end()) << "begin != end";
+ ASSERT_EQ(list.begin().pNode, list.end().pNode) << "begin.pNode != end.pNode";
+ ASSERT_EQ(list.pHorizon, list.end().pNode) << "pHorizon != end.pNode";
// add before NULL
node_t* pNode = list.addBefore(5, NULL); // should fail