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