diff --git a/YASI_12.sln b/YASI_12.sln new file mode 100644 index 0000000..58de379 --- /dev/null +++ b/YASI_12.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YASI_12", "YASI_12\YASI_12.vcxproj", "{63C1FF10-6FFA-4A4E-958B-9BD1CF80CAEB}" + ProjectSection(ProjectDependencies) = postProject + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7} = {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "..\gtest-1.7.0\msvc\gtest.vcxproj", "{C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63C1FF10-6FFA-4A4E-958B-9BD1CF80CAEB}.Debug|Win32.ActiveCfg = Debug|Win32 + {63C1FF10-6FFA-4A4E-958B-9BD1CF80CAEB}.Debug|Win32.Build.0 = Debug|Win32 + {63C1FF10-6FFA-4A4E-958B-9BD1CF80CAEB}.Release|Win32.ActiveCfg = Release|Win32 + {63C1FF10-6FFA-4A4E-958B-9BD1CF80CAEB}.Release|Win32.Build.0 = Release|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Debug|Win32.ActiveCfg = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Debug|Win32.Build.0 = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Release|Win32.ActiveCfg = Release|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/YASI_12/Sorter.cpp b/YASI_12/Sorter.cpp new file mode 100644 index 0000000..6ac4f9b --- /dev/null +++ b/YASI_12/Sorter.cpp @@ -0,0 +1,4 @@ +#include "Sorter.h" + +namespace yasi{ +} diff --git a/YASI_12/Sorter.h b/YASI_12/Sorter.h new file mode 100644 index 0000000..47bb733 --- /dev/null +++ b/YASI_12/Sorter.h @@ -0,0 +1,50 @@ +#pragma once +namespace yasi{ + template + class Sorter + { + public: + typedef typename T::iterator iter_t; + Sorter(); + + // insertion sort + + // bubble sort + template + void bubbleSort(const T& src, T& dest){ + // copy to dest + int size = src.size(); + for (int i = 0; i < size; i++){ + dest.push_back(src[i]); + } + + // now sort + size = dest.size(); + for (int i = 0; i < size - 1; i++){ + for (int j = i + 1; j < size; j++){ + if (dest[i] > dest[j]){ + typename T::value_type temp = dest[i]; + dest[i] = dest[j]; + dest[j] = temp; + } + } + } + } + ~Sorter(); + }; + + // implementations + // constructor + template + Sorter::Sorter() + { + } + + // destructor + template + Sorter::~Sorter() + { + } + +} + diff --git a/YASI_12/YASI_12.vcxproj b/YASI_12/YASI_12.vcxproj new file mode 100644 index 0000000..d02ea45 --- /dev/null +++ b/YASI_12/YASI_12.vcxproj @@ -0,0 +1,106 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {63C1FF10-6FFA-4A4E-958B-9BD1CF80CAEB} + YASI_12 + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + "$(SolutionDir)\..\gtest-1.7.0\include" + MultiThreadedDebug + + + true + $(SolutionDir)..\gtest-1.7.0\msvc\$(SolutionName)\$(ConfigurationName)\ + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);gtest.lib; + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + true + + + + + + + + \ No newline at end of file diff --git a/YASI_12/YASI_12.vcxproj.filters b/YASI_12/YASI_12.vcxproj.filters new file mode 100644 index 0000000..8ab932c --- /dev/null +++ b/YASI_12/YASI_12.vcxproj.filters @@ -0,0 +1,99 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {e771e66c-6407-4d3a-bd6f-97ad63c60a2d} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\nonproject + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/YASI_12/common.h b/YASI_12/common.h new file mode 100644 index 0000000..948dce4 --- /dev/null +++ b/YASI_12/common.h @@ -0,0 +1,30 @@ +#pragma once +//////////////// CRT memory leak detection ///////////// +#define _CRTDBG_MAP_ALLOC +#include +#include + + +#ifdef _DEBUG +#ifndef DBG_NEW +#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) +#define new DBG_NEW +#endif +#endif // _DEBUG +///////////////////////////////////////////////////////// +#include "test.h" + + +namespace yasi{ + +#define CONCAT_DETAIL(x,y) x ## y +#define CONCAT(x,y) CONCAT_DETAIL(x,y) +#define MAKE_UNIQUE(x) CONCAT(x, __COUNTER__ ) +#define MAKE_UNIQUE2(x,y) MAKE_UNIQUE( CONCAT(x,y) ) +#define MAKE_UNIQUE3(x,y, z) MAKE_UNIQUE2( CONCAT(x,y), z ) + +#define DELETE_SAFE(x) {if( (x) != 0 ) delete (x); (x) = 0; } +#define DELETE_ARR_SAFE(x) {if( (x) != 0 ) delete[] (x); (x) = 0; } +#define ARR_LENGTH(x) ( sizeof( (x) )/sizeof( (x)[0] ) ) + +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.BSTDictionary.h b/YASI_12/ds.BSTDictionary.h new file mode 100644 index 0000000..707bc39 --- /dev/null +++ b/YASI_12/ds.BSTDictionary.h @@ -0,0 +1,121 @@ +#pragma once +#include "common.h" +#include "ds.binarysearchtree.h" +#include "ds.dictionary.h" +#include "utils.h" +#include "gtest\gtest.h" +#include +#include "ds.aux.hastree.h" +using namespace std; + +namespace yasi{ +namespace ds{ + +// binary search tree based dictionary +template +class BSTDictionary : public virtual IDictionary, + public ds::aux::HasTree > >{ + /////////// enable testing //////// + friend class BSTDictionaryTest; + typedef KVPair Pair; + typedef BinarySearchTree Tree; +protected: + BinarySearchTree t; +public: + BSTDictionary(){} + ~BSTDictionary(){ + clear(); + } + + // method from abstract class HasTree + const Tree& tree() const{ return t; } + + // returns a pointer to the value + Value* find(Key k) const override{ + typename Tree::Node* pNode = t.find(Pair(k)); + if (pNode == NULL){ + // not found + return NULL; + } + else{ + return & pNode->element.value; + } + } + void remove(Key k) override{ + typename Tree::Node* pNode = t.find(Pair(k)); + if (pNode){ + // entry exists + t.removeNode(pNode); + } + } + void insert(Key k, Value v) override{ + Value* pValue = find(k); + if (pValue){ + // update + *pValue = v; + } + else{ + t.insert(Pair(k, v)); + } + } + int size() const override{ return t.size(); } + bool empty() const override{ return t.empty(); } + void clear() override{ t.clear(); } + +}; + +class BSTDictionaryTest : public yasi::Test{ + typedef BSTDictionary Dict; +public: + void all(){ + Dict d; + ASSERT_EQ(0, d.size()) << "size not 0"; + ASSERT_EQ(true, d.empty()) << "d not empty"; + + for (int i = 0; i < 20; i += 5){ + // keys: 0, 5, 10, 15 + // values: 0, 50, 100, 150 + d.insert(i, i * 10); + } + ASSERT_EQ(4, d.size()) << "size not 4"; + ASSERT_EQ(false, d.empty()) << "d empty"; + + // insert an already-existing item; should update current value + d.insert(5, 500); + ASSERT_EQ(4, d.size()) << "size changed by re-insertion"; + ASSERT_EQ(500, *d.find(5)) << "value not updated by re-insertion"; + + /// try to find non-existent items + ASSERT_EQ(0, (int)d.find(3)) << "non-null ptr for non-existent key"; + // try to find existent items + ASSERT_EQ(500, * d.find(5)) << "value at 5 not 500"; + ASSERT_EQ(150, * d.find(15)) << "value at 15 not 150"; + + // remove some items + d.remove(5); + d.remove(15); + ASSERT_EQ(2, d.size()) << "size not 2"; + + /// try to find non-existent items + ASSERT_EQ(0, (int)d.find(3)) << "non-null ptr for non-existent key 3"; + ASSERT_EQ(0, (int)d.find(5)) << "non-null ptr for non-existent key 5"; + ASSERT_EQ(0, (int)d.find(15)) << "non-null ptr for non-existent key 15"; + // try to find existent items + ASSERT_EQ(0, *d.find(0)) << "value at 0 not 50"; + ASSERT_EQ(100, *d.find(10)) << "value at 10 not 100"; + + // clear + d.clear(); + ASSERT_EQ(0, d.size()) << "size not 0"; + ASSERT_EQ(true, d.empty()) << "d not empty"; + + + } + +}; + +ADD_TEST_F(BSTDictionaryTest, all); + + +}// namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.arraybinarytree.h b/YASI_12/ds.arraybinarytree.h new file mode 100644 index 0000000..647d681 --- /dev/null +++ b/YASI_12/ds.arraybinarytree.h @@ -0,0 +1,1006 @@ +#pragma once +#include "common.h" +#include +#include "utils.h" +#include "ds.tree.h" +#include "ds.binarytree.h" +#include "ds.binarytreebase.h" +#include +#include + +namespace yasi{ + using namespace std; + namespace ds{ + + ///// abstract class + //template + //class BinaryTreeNode : public TreeNode < E > { + //public: + // virtual BinaryTreeNode left() = 0; + // virtual BinaryTreeNode right() = 0; + // virtual BinaryTreeNode parent() = 0; + //}; + + + class IArrayBinaryTreeNode{ + public: + virtual ~IArrayBinaryTreeNode(){} + virtual int index() const = 0; + virtual void setIndex(int) = 0; + virtual int leftIndex() const = 0; + virtual int rightIndex() const = 0; + virtual int parentIndex() const = 0; + }; + + + template + class IndexedBTNode : public virtual IArrayBinaryTreeNode, + public virtual TreeNode { + friend class IndexedBTNodeTest; // enable testing + protected: + int _index; /// -1 by default + const int NULL_INDEX; // marks whether a node is empty/invalid + public: + IndexedBTNode() : TreeNode(), NULL_INDEX(-1) { + _index = NULL_INDEX; + } + IndexedBTNode(const E& e) : TreeNode(e), NULL_INDEX(-1){ + _index = NULL_INDEX; + } + IndexedBTNode(const E& e, int index) : TreeNode(e), NULL_INDEX(-1){ + _index = index; + } + void setIndex(int index) override{ + _index = index; + } + int index() const override{ + return _index; + } + int leftIndex() const override{ + return (_index << 1); + } + int rightIndex() const override{ + return (_index << 1) + 1; + } + int parentIndex() const override{ + return _index >> 1; + } + + // check if this node is marked as null + bool isNull(){ + return _index == NULL_INDEX; + } + void makeNull(){ + _index = NULL_INDEX; + } + virtual ~IndexedBTNode(){ + + } + }; + + // interface for ArrayBinaryTree + template + class IArrayBinaryTree : public virtual IBinaryTree < E, Node > { + public: + virtual ~IArrayBinaryTree(){} + virtual void fromArray(const E*, const int) = 0; + virtual int getRootIndex() const = 0; + virtual Node* nodeAt(int) const = 0; + virtual Node* insertAt(const E& e, int) = 0; + }; + + + // nodes are internally kept in an array (vector, actually) + // the 0 index is always empty to facilitate easy parent-child indexing. + // thus the root is stored at index 1 + // it does not yet implement the IArrayBinaryTree interface + template + class ArrayBinaryTree : public virtual IArrayBinaryTree >, + public virtual BinaryTreeBase>{ + ///////////// enable testing //////////////// + friend class ArrayBinaryTreeTest; + friend class BinaryHeapTest; + + typedef IndexedBTNode node_t; + const int ROOT_INDEX = 1; // for quick parent-child indexing, 1 instead of 0 + + // the underlying container + const int INIT_CAPACITY = 2; // initial size of the array + const float LOAD_FACTOR = 2; // how much the array size increases at each resize + + // do not initialize _capacity and _size here; let constructors do that + int _capacity; // current length of the array, not necessarily the same as the capacity of a vector (due to automatic resizing) + int _size; // the actual size of the tree, always <= _capacity. + + // the vector + vector< node_t* > _arr; // the array of pointers to nodes + + // returns true if 0 <= index < capacity + bool isArrIndex(int k) const{ + return k >= 0 && k < _capacity; + } + // returns true if the node-ptr at index is not null + bool nodeNotNull(int k) const{ + return nodeAt(k) != NULL; + } + // returns the content at the index of the vector + // the return value can be NULL + + // replaces the element of a valid node at an occupied index + node_t* replaceAt(E e, int index){ + node_t* pNode = NULL; + if (nodeNotNull(index)){ + // the index contains non-null node + pNode = _arr[index]; // find node + pNode->setElement(e); // update value + } + return pNode; + } + + ////////////////// has bugs, incomplete + //E _pullSubtreeUp(node_t* pFrom, node_t* pTo, bool bRemoveWriteSubtree = 1){ + // if (nodeNotNull(pFrom) && nodeNotNull(pTo)){ + // E eRet = pTo->element; // the return value + // // now make two cursors: one to read and one to write + // node_t *pRead = pFrom, *pWrite = pTo; + // // first, delete the existing subtree at pTo + // // this eliminates issues stemming from size-difference between two subtrees + // while (nodeNotNull(pRead) && nodeNotNull(pWrite)){ + // // copy element from read to write + // pWrite->setElement(pRead->element); + // // copy left subtree of pRead + // if (hasLeft(pRead) && !hasLeft(pWrite) ){ + // // pWrite does not have a left child + // // create a new left node + // addLeft(pRead->element, pWrite); + // } + // if (hasLeft(pRead)){ + // // now recursive to the left + // _pullSubtreeUp(left(pRead), left(pWrite), 0); // no need to delete the write subtree again + // } + // // copy right subtree of pRead + // if (hasRight(pRead) && !hasRight(pWrite)){ + // // pWrite does not have a right child + // // create a new right node + // addRight(pRead->element, pWrite); + // } + // if (hasRight(pRead)){ + // // now recursive to the right + // _pullSubtreeUp(right(pRead), right(pWrite), 0); // no need to delete the write subtree again + // } + // } + // // finally, delete the the source subtree + // remove(pFrom); + // } + //} + + + // creates the actual container and fills with NULL pointers + void initArr(const int capacity){ + _capacity = capacity; + _size = 0; + _arr = vector(capacity, 0); + for (int i = 0; i < capacity; i++){ + _arr.push_back(0); // fill the vector with NULL pointers + } + + } + // increases the size of the array + // initializes new slots with NULL pointer + void resize(int newSize, node_t* initValue = 0){ + int i = _capacity; // old capacity + _arr.resize(newSize, initValue); + _capacity = _arr.capacity() - 1; // new capacity; one less than vector-capacity to possibly stop auto-resize + while (i < _capacity){ + _arr.push_back(0); // fill the unused slots with NULL + i = i + 1; + } + } + protected: + typedef ArrayBinaryTree self; + public: + typedef node_t Node; + + // initial size is zero, capacity is INIT_CAPACITY + // all slots of the array is filled with 0 + ArrayBinaryTree() : _size(0), _capacity(INIT_CAPACITY){ + initArr(INIT_CAPACITY); + } + + // construct a binary tree from a given array + ArrayBinaryTree(const E* pSrcArr, const unsigned int arrSize) : _capacity(0), _size(0){ + fromArray(pSrcArr, arrSize); + } + + // copy constructor + ArrayBinaryTree(const self& other){ + *this = other; + } + + // construct a binary tree from a given array + void fromArray(const E* pSrcArr, const int arrSize){ + if (arrSize <= 0) { + cout << "Argument arrSize is negative" << endl; + initArr(INIT_CAPACITY); + } + else{ + // either _arr is initialized, or not + // if it is, we need to clear it first + if (_capacity != 0){ // initialized + clear(); + } + // create array + initArr(arrSize + 1); // +1 for the unused 0-index slot + // insert elements into array + for (int i = 1; i < arrSize + 1; i++){ + E e = pSrcArr[i - 1]; + insertAt(e, i); + } + } + } + + // returns true if the node-ptr at index is not null + inline bool nodeNotNull(const node_t* pNode) const override{ + return pNode != NULL && nodeAt(pNode->index()) != NULL; + } + + self& operator=(const self& other){ + this->_arr = other._arr; + this->_capacity = other._capacity; + this->_size = other._size; + return *this; + } + + + ///////////// done but needs test/reason-for-existence + //// + //// sets the param "pDestArr" with a dynamically-allocated array of elements; the order is the same as that in the internal array + //// also sets the param "numElems" with the number of elements in the array + //// the param "emptyElem" is used to denote empty slots in the internal array + //// elements in "pDestArr" maintains the tree-structure + //bool toArray(E* pDestArr, int& numElems, E nullElem){ + // numElems = 0; + // E* pRawArr = new E[_capacity]; + // + // int countElems = 0; // how many elements touched + // for (int i = 0; i < _capacity && countElems < _size; i++){ // discard the index 0 + // node_t* pNode = nodeAt(i); + // if (pNode == NULL){ + // pRawArr[i] = nullElem; + // } + // else{ + // pRawArr[i] = pNode->element; + // countElems++; + // } + // numElems++; + // } + // if (countElems == _size){ + // // good + // pDestArr = new E[numElems]; + // for (int i = 0; i < numElems; i++){ + // pDestArr[i] = pRawArr[i]; + // } + // DELETE_ARR_SAFE(pRawArr); + // return true; + // } + // else + // return false; + //} + + + /////////// done but needs test/reason-for-existence + // + // sets the param "*ppDestArr" with a dynamically-allocated array of elements; the order is the same as that in the internal array + // also sets the param "numElems" with the number of elements in the array + // the param "emptyElem" is used to denote empty slots in the internal array + // + // Caution: elements in "*ppDestArr" DOES NOT MAINTAIN the tree-structure + bool elements(E** ppDestArr, int& numElems){ + numElems = _size; + *ppDestArr = new E[_size]; + E* pDestArr = *ppDestArr; + + int countElems = 0; // how many elements touched + for (int i = 1; i < _capacity && countElems < _size; i++){ // discard the index 0 + node_t* pNode = nodeAt(i); + if (pNode != NULL){ + pDestArr[countElems] = pNode->element; + countElems++; + } + } + if (countElems == _size){ + // good + return true; + } + else + return false; + } + void reset(){ + clear(); + initArr(INIT_CAPACITY); + } + void clear(){ + // delete each pointer contained in the vector + for (int i = 0; i < _capacity && i < _arr.size(); i++){ + DELETE_SAFE(_arr[i]); + } + + // clear the vector + _arr.clear(); + + // clean the slate + _size = 0; + _capacity = INIT_CAPACITY; + } + + /////////////////////////////////////////////// + ////////////// Interface IArrayBinaryTree ///// + /////////////////////////////////////////////// + int getRootIndex() const override{ + return ROOT_INDEX; + } + node_t* nodeAt(int k) const override{ + if (isArrIndex(k)){ + return _arr[k]; + } + else + return NULL; + } + // master method for inserting new items + // caution: + // (1) inserts at arbitrary unoccupied index, increasing the capacity when the index is too large + // (2) cannot insert at zero index + // (3) does not check parent/child relationship; the node can be possibly without a parent + // dynamically allocates a new node; increases the tree-size by one + // returns the node-ptr on success, NULL on failure + node_t* insertAt(const E& e, int index) override{ + node_t* pNode = NULL; + if (index > 0){ + while (index >= _capacity){ + // need to increase capacity + _capacity = (int)(_capacity * LOAD_FACTOR); + _arr.resize(_capacity, 0); // newly added values are zero + } + + // now insert into desired position. it must be unoccupied + if (isArrIndex(index) && !nodeNotNull(index)){ + pNode = new node_t(e, index); // dynamic allocation of the node object. Must delete later. + _arr[index] = pNode; // put in the array + _size++; // increment size + } + } + return pNode; + } + + + /////////////////////////////////////////////// + ////////////// Interface ITree //////////////// + /////////////////////////////////////////////// + bool empty() const override{ + return _size == 0; // because we are keeping the first slot empty + } + int size() const override{ + return _size; // because we are keeping the first slot empty + } + virtual node_t* root() const { + if (empty()) + return NULL; + else + return nodeAt(ROOT_INDEX); + } + // removes the subtree rooted at a node and discards the element stored at that node. + // returns false if any error occurs, true otherwise + bool remove(node_t* pNode)override{ + E temp; + return remove(pNode, temp); + } + + // removes the subtree rooted at a node and saves the value at that node at the output argument e. + // returns false if any error occurs, true otherwise + bool remove(node_t* pNode, E& e) override{ // e is output param + if (pNode != NULL && nodeNotNull(pNode->index())){ + bool success = true; + e = pNode->element; + E temp; + if (hasLeft(pNode)){ + success = remove(left(pNode), temp); + } + if (hasRight(pNode)){ + success &= remove(right(pNode), temp); + } + // subtree deleted + _arr[pNode->index()] = 0; // mark the slot as empty + DELETE_SAFE(pNode); // delete the allocated memory + + _size--; // size decreases + return success; + } + else + return false; + } + + bool removeNode(node_t* pNode)override { + E temp; + return removeNode(pNode, temp); + } + + // removes a node and saves the value at that node at the output argument e. + // caution: this may leave the tree broken + // returns false if any error occurs, true otherwise + bool removeNode(node_t* pNode, E& e) override{ // e is output param + if (pNode != NULL && nodeNotNull(pNode->index())){ + e = pNode->element; + _arr[pNode->index()] = 0; // mark the slot as empty + DELETE_SAFE(pNode); // delete the allocated memory + + _size--; // size decreases + return true; + } + else + return false; + } + bool isRoot(const node_t* pNode) const override{ + return (pNode != NULL) && + pNode->index() == ROOT_INDEX && + parent(pNode) == NULL; + } + + // succeeds if the tree is empty + // otherwise, returns NULL + node_t* addRoot(const E& e) override{ + if (root() == NULL){ + // no root + return insertAt(e, ROOT_INDEX); + } + else + return NULL; + } + + + // prints out the nodes in level order + string toString() const override{ + return this->toStringNodeArray("|", ""); + } + string toStringNodeArray() const { + return this->toStringNodeArray("|", ""); + } + string toStringNodeArray(const char* strDelim, const char* strEmptySlot) const{ + // cout << endl << "ArrayBinaryTree contents:" << endl; + std::stringstream buffer; + for (int i = 0; i < _capacity; i++){ + if (_arr[i] == NULL){ + // print empty slots + buffer << strEmptySlot; + } + else{ + buffer << _arr[i]->element; + } + // don't print the last delim + //if (i < _capacity - 1){ + buffer << strDelim; + //} + } + // done + return buffer.str(); + } + + /////////////////////////////////////////////// + /////////// Interface IBinaryTree ///////////// + /////////////////////////////////////////////// + node_t* parent(const node_t* pNode) const override{ + return nodeAt(pNode->parentIndex()); + } + node_t* left(const node_t* pNode) const override{ + return nodeAt(pNode->leftIndex()); + } + node_t* right(const node_t* pNode) const override{ + return nodeAt(pNode->rightIndex()); + } + // returns the sibling of a node; + // the return value wll be null if no sibling + node_t* addLeft(const E& e, node_t* pParent) override{ + return insertAt(e, pParent->leftIndex()); + } + node_t* addRight(const E& e, node_t* pParent) override{ + return insertAt(e, pParent->rightIndex()); + } + + + //////////////////// buggy and incomplete + //// removes a node having zero or one child; the child, if exists, is not deleted + //// the child becomes a child of the parent of the deleted node + //// returns the value at the deleted node. + //// throws exception E_NODE_NOT_FOUND if the node is bad + //// throws exception E_BAD_NODE if the node has both children + //E removeAndLink(node_t* pNode){ + // if (nodeNotNull(pNode)){ + // if (numChildren(pNode) != 2){ + // E e = pNode->element; + // if (isRoot(pNode)){ + // // we are removing the root + // /////// todo: pullup entire subtree + // } + // else{ + // // removing non-root node + // node_t* pParent = parent(pNode); + // // it is easy if the node is external + // if (isExternal(pNode)){ + // // deleting external node + // if (isLeft(pNode)){ + // _arr[pParent->leftIndex] = NULL; + // } + // else{ + // _arr[pParent->rightIndex] = NULL; + // } + // } + // else{ + // // deleting internal node + // node_t* pChild = hasLeft(pNode) + // ? left(pNode) + // : right(pNode); + // // connect child and grand-parent + // ///////// todo: pull-up entire subtree + // } + // } + // // ok + // DELETE_SAFE(pNode); // manually free-up the memory + // _size--; // update size + // // done + // return e; + // } + // else{ + // throw E_BAD_NODE; + // } + // } + // else throw E_NODE_NOT_FOUND; + //} + + + + virtual ~ArrayBinaryTree(){ + this->clear(); + } + + // returns the string-representation of the internal arr in the param "*pcStr" + // the memory for "*pcStr" is allocated dynamically, which must be deleted later + + template + bool operator==(const ArrayBinaryTree& other) const{ + return this->size() == other.size() && + this->toStringBFS() == other.toStringBFS(); + } + template + bool operator!=(const ArrayBinaryTree& other) const{ + return !(*this == other); + } + + }; + + // for printing purpose only + // prints the BFS representation of the tree + template + ostream& operator << (ostream& out, const ArrayBinaryTree& t){ + out << t.toStringBFS(); + return out; + } + + /// + /// Test IndexedBTNode + /// + class IndexedBTNodeTest :public yasi::Test{ + protected: + void init(){ + IndexedBTNode n1; + ASSERT_EQ(-1, n1._index) << "Default index should be -1" ; + n1.setIndex(5); + ASSERT_EQ(5, n1.index()) << "index should be 5"; + n1.setElement(7); + ASSERT_EQ(7, n1.element) << "element should be 5"; + + n1.makeNull(); + ASSERT_EQ(1, n1.isNull()) << "makeNull() and isNull() disagree"; + } + void indexing(){ + const int N = 7; + IndexedBTNode n[N]; + for (int i = 0; i < N; i++){ + n[i].setIndex(i); + } + ASSERT_EQ(0, n[0].leftIndex()) << "left of 0 must be 0"; + ASSERT_EQ(1, n[0].rightIndex()) << "right of 0 must be 1"; + + ASSERT_EQ(1, n[2].parentIndex()) << "parent of 2 must be 1"; + ASSERT_EQ(1, n[3].parentIndex()) << "parent of 3 must be 1"; + ASSERT_EQ(2, n[4].parentIndex()) << "parent of 4 must be 2"; + ASSERT_EQ(4, n[2].leftIndex()) << "left of 2 must be 4"; + ASSERT_EQ(5, n[2].rightIndex()) << "right of 2 must be 5"; + + } + }; + ADD_TEST_F(IndexedBTNodeTest, init); + ADD_TEST_F(IndexedBTNodeTest, indexing); + ////////////// end testing IndexedBTNode + + /// + /// Test ArrayBinaryTree + /// + class ArrayBinaryTreeTest : public yasi::Test{ + protected: + typedef ArrayBinaryTree::node_t node_t; + typedef ArrayBinaryTree::node_t* node_tptr; + public: + + template + static void checkElements(ArrayBinaryTree *pBt, const E* pElemsArr, const int numElems){ + // finally, check array + E *pTreeElems; + int n; + ArrayBinaryTree& bt = *pBt; // must use reference, otherwise the tree will be destroyed upon exit + bt.elements(&pTreeElems, n); + ASSERT_EQ(n, numElems) << "actual array size " << n << " should equal target size " << numElems << endl + << "actual : " << arrToString(pTreeElems, n) << endl + << "expected: " << arrToString(pElemsArr, n); + + ASSERT_EQ(true, arrcmp(pTreeElems, pElemsArr, n)) << "elements array not right." << endl + << "actual : " << arrToString(pTreeElems, n) << endl + << "expected: " << arrToString(pElemsArr, n); + + // done + DELETE_ARR_SAFE(pTreeElems); + } + + void constructor(){ + ArrayBinaryTree bt; + ASSERT_EQ(1, bt.ROOT_INDEX) << "Index of root is not 1"; + ASSERT_LE(bt.INIT_CAPACITY, bt._arr.capacity() ) << "Initial _capacity must be <= containers capacity"; + ASSERT_LE(2, bt._arr.capacity()) << "Initial capacity must be >= 2"; + ASSERT_EQ(0, bt.size()) << "Initial size is not zero"; + for (int i = 0; i < bt._capacity; i++){ + ASSERT_EQ(0, bt._arr[i]) << "Pointer at index " << i << " is not initialized to NULL"; + } + + ASSERT_EQ(NULL, bt.root()) << "root should have been NULL"; + } + + void elements(){ + int srcArr[] = { 10, 20, 30, 40, 50, 60 }; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n); + + int* pElemArr; + int numElems; + bt.elements(&pElemArr, numElems); + ASSERT_EQ(n, numElems) << "number of elements is not " << n; + for (int i = 0; i < numElems; i++){ + ASSERT_EQ(srcArr[i], pElemArr[i]) << "element mismatch at index " << i; + } + DELETE_ARR_SAFE(pElemArr); + } + + void fromArr(){ + int srcArr[] = {10, 20, 30, 40, 50, 60}; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n ); + ASSERT_EQ(n, bt.size()) << "size of tree is not " << n; + // check node-ptrs stored in internal container + int i = 0; + for (int i = 0; i < bt._capacity; i++){ + if (i >= 1 && i <= n){ + // valid node-ptrs + ASSERT_NE(0, (int)bt._arr[i]) << "Pointer NULL at index " << i; + ASSERT_EQ(srcArr[i-1], bt._arr[i]->element) << "array element mismatch for internal index " << i; + } + else{ + // invalid node-ptrs + ASSERT_EQ(0, (int)bt._arr[i]) << "Pointer not NULL at index " << i; + } + } + // also, use elements() method + int* pElemArr; + int numElems; + bt.elements(&pElemArr, numElems); + ASSERT_EQ(n, numElems) << "number of elements is not " << n; + for (int i = 0; i < numElems; i++){ + ASSERT_EQ(srcArr[i], pElemArr[i]) << "element mismatch at index " << i; + } + DELETE_ARR_SAFE(pElemArr); + } + + void isArrIndex(){ + ArrayBinaryTree bt; + ASSERT_EQ(false, bt.isArrIndex(-1)) << "Negative index is accepted"; + ASSERT_EQ(true, bt.isArrIndex(0)) << "0 index is rejected"; + ASSERT_EQ(true, bt.isArrIndex(bt._capacity - 1)) << "index (capacity - 1) is rejected"; + ASSERT_EQ(false, bt.isArrIndex(bt._capacity)) << "index beyond capacity is accepted"; + } + + void nodeAt(){ + ArrayBinaryTree bt; + bt.addRoot(5); + + bt.nodeAt(-1); + + ASSERT_EQ(0, (int) bt.nodeAt(-1)) << "Negative index is accepted"; + ASSERT_EQ(0, (int) bt.nodeAt(0)) << "Index 0 is accepted"; + ASSERT_EQ(0, (int) bt.nodeAt(bt._capacity)) << "Too large index is accepted"; + ASSERT_NE(0, (int) bt.nodeAt(1)) << "The root is not recognized"; + + int e; + bt.remove(bt.root(), e); // remove root + ASSERT_EQ(0, (int) bt.nodeAt(1)) << "TreeNode accepted after deletion"; + } + + void nodeNotNull(){ + ArrayBinaryTree bt; + bt.addRoot(5); + + ASSERT_EQ(false, bt.nodeNotNull(-1)) << "Negative index is accepted"; + ASSERT_EQ(false, bt.nodeNotNull(0)) << "Zero index is accepted"; + ASSERT_EQ(false, bt.nodeNotNull(bt._capacity)) << "Too large index is accepted"; + ASSERT_EQ(true, bt.nodeNotNull(1)) << "The root is not recognized"; + + int e; + bt.remove(bt.root(), e); // remove root + ASSERT_EQ(false, bt.nodeNotNull(1)) << "TreeNode accepted after deletion"; + } + + void addRoot(){ + ArrayBinaryTree bt; + ASSERT_EQ(0, bt.size()); + bt.addRoot(10); + ASSERT_EQ(1, bt.size()) << "tree-size should have been 1"; + ASSERT_EQ(0, (int) bt.nodeAt(0)) << "As per Goodrich-Tamassia convention, first slot in the array should have been empty"; + ASSERT_EQ(10, bt.nodeAt(1)->element) << "Value at the root should have been 10"; + ASSERT_EQ(false, bt.isLeft(bt.root())) << "root cannot be a left child"; + ASSERT_EQ(false, bt.isRight(bt.root())) << "root cannot be a right child"; + ASSERT_EQ(false, bt.isInternal(bt.root())) << "root should have been external"; + ASSERT_EQ(false, bt.hasLeft(bt.root())) << "root does not have a left child"; + ASSERT_EQ(false, bt.hasRight(bt.root())) << "root does not have a right child"; + } + + void insertAt(){ + ArrayBinaryTree bt; + EXPECT_EQ(0, (int)bt.insertAt(4, -1)) << "inserting at negative index succeeded"; + EXPECT_EQ(0, (int)bt.insertAt(4, 0)) << "inserting at 0 index succeeded"; + EXPECT_NE(0, (int)bt.insertAt(4, 2 * bt._capacity)) << "inserting at too large index failed"; + ASSERT_NE(0, (int)bt.insertAt(4, 1)) << "inserting at index 1 failed"; + ASSERT_EQ(0, (int)bt.insertAt(4, 1)) << "inserting at an occupied index succeeded"; + bt.remove(bt.nodeAt(1)); + ASSERT_NE(0, (int)bt.insertAt(4, 1)) << "inserting at a recently deleted slot failed"; + } + + void replaceAt(){ + ArrayBinaryTree bt; + EXPECT_EQ(0, (int)bt.replaceAt(4, -1)) << "replacing at negative index succeeded"; + EXPECT_EQ(0, (int)bt.replaceAt(4, 0)) << "replacing at 0 index succeeded"; + EXPECT_EQ(0, (int)bt.replaceAt(4, 2 * bt._capacity)) << "replacing at too large index succeeded"; + ASSERT_EQ(0, (int)bt.replaceAt(4, 1)) << "replacing at unused index 1 succeeded"; + bt.addRoot(10); + ASSERT_NE(0, (int)bt.replaceAt(4, 1)) << "replacing at occupied index 1 failed"; + int index = bt.root()->index(); + bt.remove(bt.root()); + ASSERT_EQ(0, (int)bt.replaceAt(4, index) ) << "replacing at a recently deleted index succeeded"; + } + + void sibling(){ + ArrayBinaryTree bt; + ASSERT_EQ(0, (int)bt.sibling(bt.nodeAt(0))) << "sibling of NULL node succeeded"; + bt.addRoot(4); + ASSERT_EQ(0, (int)bt.sibling(bt.root())) << "sibling of root node succeeded"; + ArrayBinaryTree::node_t* n1 = bt.addLeft(5, bt.root()); + ASSERT_EQ(0, (int)bt.sibling(n1)) << "sibling of only-child succeeded"; + ArrayBinaryTree::node_t* n2 = bt.addRight(5, bt.root()); + ASSERT_EQ(n2, bt.sibling(n1)) << "sibling of valid node failed"; + bt.remove(bt.left(bt.root())); // remove n1 + ASSERT_EQ(0, (int) bt.sibling(n2)) << "sibling of a deleted-sibling-node succeeded"; + + } + + void resize(){ + int srcArr[] = { 10, 20, 30, 40, 50 }; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n); + int oldCapacity = bt._capacity; + // copy current data + // node_tptr is actually int (because it is a pointer) + node_tptr* pOldArr = new node_tptr[oldCapacity]; + memcpy(pOldArr, (node_tptr*)bt._arr.data(), sizeof(node_tptr) * oldCapacity); + + bt.resize(2 * oldCapacity + 5, 0); + int newCapacity = bt._capacity; + const node_tptr* pNewArr = (node_tptr*)bt._arr.data(); + // all new slots must be initialized to zero + for (int i = 0; i < newCapacity; i++){ + if (i < oldCapacity){ + // old slots + ASSERT_EQ(pOldArr[i], pNewArr[i]) << "content mismatch at index " << i; + } + else{ + // new slots + ASSERT_EQ(0, pNewArr[i]) << "new slots contain non-NULL pointer at index " << i; + } + } + // done test + DELETE_ARR_SAFE(pOldArr); + } + + void toString(){ + int srcArr[] = { 10, 20, 30, 40, 50, 60, 70 }; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n); + // delete 50 and 60 + int e1, e2; + bool ok = bt.remove(bt.nodeAt(5), e1); + ASSERT_EQ(true, ok) << "falied to remove 50"; + ASSERT_EQ(50, e1) << "50 was not deleted"; + ok = bt.remove(bt.nodeAt(6), e2); + ASSERT_EQ(true, ok) << "falied to remove 60"; + ASSERT_EQ(60, e2) << "60 was not deleted"; + + // now print + const char* sDelim = "|"; // need to be single-char for testing purpose + const char* sEmptySlot = ""; // need to be empty for testing purpose + string strBt = bt.toStringNodeArray(sDelim, sEmptySlot); + // remove trailing single-char delimiters + for (int i = strBt.length() - 1; strBt[i] == sDelim[0]; i--){ + strBt[i] = NULL; + } + strBt = string(strBt.c_str()); + // now strBt should equal "|10|20|30|40|||70" + string strExpected = "|10|20|30|40|||70"; + ASSERT_EQ(strExpected, strBt) << "Expected " << strExpected << " but found " << strBt; + + + bt.clear(); + bt.fromArray(srcArr, n);// 10 20 30 40 50 60 70 + /////////// preorder + strBt = bt.toStringPreOrder(); + strBt = strPurge(strBt, "| "); + strExpected = "10204050306070"; + ASSERT_EQ(strExpected, strBt) << "preorder mismatch"; + + /////////// postorder + strBt = bt.toStringPostOrder(); + strBt = strPurge(strBt, "| "); + strExpected = "40502060703010"; + ASSERT_EQ(strExpected, strBt) << "postorder mismatch"; + + /////////// inorder + strBt = bt.toStringInOrder(); + strBt = strPurge(strBt, "| "); + strExpected = "40205010603070"; + ASSERT_EQ(strExpected, strBt) << "inorder mismatch"; + + + /////////// BFSorder + strBt = bt.toStringBFS(); + strBt = strPurge(strBt, "| "); + strExpected = "10203040506070"; + ASSERT_EQ(strExpected, strBt) << "BFS order mismatch"; + } + + void remove(){ + int e; + int srcArr[] = { 10, 20, 30, 40, 50, 60 }; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n); + { + SCOPED_TRACE("tree arr before remove"); + checkElements(&bt, srcArr, n); + } + //bt.toString(&cBt); + //DELETE_ARR_SAFE(cBt); + + + bool ok = bt.remove(bt.nodeAt(0)); + ASSERT_EQ(false, ok) << "removing from index 0 succeeded"; + ok = bt.remove(bt.nodeAt(-1)); + ASSERT_EQ(false, ok) << "removing from negative index succeeded"; + + //bt.toString(&cBt); + //DELETE_ARR_SAFE(cBt); + + // removing the root will remove the entire tree + ok = bt.remove(bt.root(), e); // 10 + ASSERT_EQ(true, ok) << "remove(root) failed"; + ASSERT_EQ(10, e) << "removed element should have been 10"; + ASSERT_EQ(0, bt.size()) << "Deleting subtree at root should make the size zero."; + + // remove a subtree + bt.fromArray(srcArr, ARR_LENGTH(srcArr)); + // arr = { 10, 20, 30, 40, 50, 60 }; + ok = bt.remove(bt.left(bt.root()), e); // remove subtree 20 + ASSERT_EQ(true, ok) << "remove(20) failed"; + ASSERT_EQ(20, e) << "removed element should have been 20"; + ASSERT_EQ(ARR_LENGTH(srcArr) - 3, bt.size()) << "Remaining tree has improper size"; + { + SCOPED_TRACE("remove(20)"); + int newArr[] = {10, 30, 60}; + checkElements(&bt, newArr, ARR_LENGTH(newArr)); + } + } + + void removeNode(){ + int e; + int srcArr[] = { 10, 20, 30, 40, 50, 60 }; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n); + { + SCOPED_TRACE("tree arr before remove"); + checkElements(&bt, srcArr, n); + } + + // remove a single node + bool ok = bt.removeNode(bt.root(), e); // 10 + ASSERT_EQ(true, ok) << "remove(root) failed"; + ASSERT_EQ(10, e) << "removed element should have been 10"; + // arr = { ?, 20, 30, 40, 50, 60 }; + { + SCOPED_TRACE("removeNode(10)"); + int newArr[] = { 20, 30, 40, 50, 60 }; + checkElements(&bt, newArr, 5); + + ASSERT_EQ(5, bt.size()) << "size of the tree should be 5"; + ASSERT_EQ(0, (int)bt.nodeAt(1)) << "the pointer at the deleted position must be NULL"; + } + + // remove another single node + ok = bt.removeNode(bt.nodeAt(3), e); // 30 + ASSERT_EQ(true, ok) << "removeNode(30) failed"; + ASSERT_EQ(30, e) << "removed element should have been 30"; + // arr = { ?, 20, ?, 40, 50, 60 }; + { + SCOPED_TRACE("removeNode(30)"); + int newArr[] = { 20, 40, 50, 60 }; + checkElements(&bt, newArr, 4); + + ASSERT_EQ(4, bt.size()) << "size of the tree should be 4"; + ASSERT_EQ(0, (int)bt.nodeAt(3)) << "the pointer at the deleted position must be NULL"; + } + + } + + void clear(){ + int srcArr[] = { 10, 20, 30, 40, 50, 60 }; + int n = ARR_LENGTH(srcArr); + ArrayBinaryTree bt(srcArr, n); + bt.clear(); + ASSERT_EQ(0, bt.size()) << "size not 0"; + ASSERT_EQ(true, bt.empty()) << "tree not empty"; + ASSERT_EQ(NULL, bt.root()) << "root not NULL"; + + } + + //void addElements(){ + // ArrayBinaryTree bt; + // bt.addRoot(10); + // bt.addLeft(20, bt.root()); + // bt.addRight(30, bt.root()); + // ASSERT_EQ(true, bt.isInternal(bt.root())) << "root should have been internal"; + // ASSERT_EQ(true, bt.isExternal(bt.left(bt.root()))) << "root's left child should have been external"; + // bt.addLeft(40, bt.left(bt.root())); + // ASSERT_EQ(false, bt.isExternal(bt.left(bt.root()))) << "root's left child should have been internal"; + // bt.addRight(70, bt.right(bt.root())); + //} + }; + + // create tests + ADD_TEST_F(ArrayBinaryTreeTest, constructor); + ADD_TEST_F(ArrayBinaryTreeTest, isArrIndex); + ADD_TEST_F(ArrayBinaryTreeTest, nodeAt); + ADD_TEST_F(ArrayBinaryTreeTest, nodeNotNull); + ADD_TEST_F(ArrayBinaryTreeTest, addRoot); + ADD_TEST_F(ArrayBinaryTreeTest, insertAt); + ADD_TEST_F(ArrayBinaryTreeTest, replaceAt); + ADD_TEST_F(ArrayBinaryTreeTest, remove); + ADD_TEST_F(ArrayBinaryTreeTest, removeNode); + ADD_TEST_F(ArrayBinaryTreeTest, sibling); + ADD_TEST_F(ArrayBinaryTreeTest, elements); + ADD_TEST_F(ArrayBinaryTreeTest, fromArr); + ADD_TEST_F(ArrayBinaryTreeTest, resize); + ADD_TEST_F(ArrayBinaryTreeTest, toString); + ADD_TEST_F(ArrayBinaryTreeTest, clear); + + + } +} \ No newline at end of file diff --git a/YASI_12/ds.aux.hastree.h b/YASI_12/ds.aux.hastree.h new file mode 100644 index 0000000..12fda4b --- /dev/null +++ b/YASI_12/ds.aux.hastree.h @@ -0,0 +1,36 @@ +#pragma once +#include "common.h" +#include + +namespace yasi{ + namespace ds{ + namespace aux{ + using namespace std; + + template + class HasTree{ + public: + // returns a const reference to the tree used + virtual const Tree& tree() const = 0; + virtual ~HasTree(){} + + HasTree(){} + string toString(){ + return tree().toString(s); + } + string toStringPreOrder(){ + return tree().toStringPreOrder(); + } + string toStringPostOrder(){ + return tree().toStringPostOrder(); + } + string toStringInOrder(){ + return tree().toStringInOrder(); + } + string toStringBFS(){ + return tree().toStringBFS(); + } + }; + } + } +} \ No newline at end of file diff --git a/YASI_12/ds.binaryheap.h b/YASI_12/ds.binaryheap.h new file mode 100644 index 0000000..880f29b --- /dev/null +++ b/YASI_12/ds.binaryheap.h @@ -0,0 +1,834 @@ +#pragma once +#include "common.h" +#include "ds.arraybinarytree.h" +#include +#include +#include +#include "ds.aux.hastree.h" +#include "ds.kvpair.h" + + +namespace yasi{ + +using namespace std; + +namespace ds{ + + // the Heap interface + template // E is the element class + class IBinaryHeap { + virtual bool heapify(const E* srcArr, const int numElems) = 0; // create a heap from an array; ; returns true on success/false on failure + virtual const E& top() const = 0; // gives the top element; behavior undefined when heap empty + virtual void pop() = 0; // gives the top element and removes it from the heap; behavior undefined when heap empty + virtual void push(const E&) = 0; // adds an element to the heap + virtual Node* insert(const E&) = 0; // adds an element to the heap; returns the pointer to the new node, or NULL on failure + virtual int size() const = 0; + virtual bool empty() const = 0; + public: + virtual ~IBinaryHeap(){}; + + + }; + + // default min heap parent-child predicate + // returns true if parent <= child + // note: operator < and == must be defined/overloaded for template class E +template +class MinHeapParentChildPredicate{ +public: + MinHeapParentChildPredicate(){} + bool operator() ( const E& parent, const E& child) const { + return (parent <= child); + } + virtual ~MinHeapParentChildPredicate(){} +}; +template +class MaxHeapParentChildPredicate{ +public: + MaxHeapParentChildPredicate(){} + bool operator() (const E& parent, const E& child) const { + return !(parent < child); + } + virtual ~MaxHeapParentChildPredicate(){} +}; + +template +class BinaryHeapBase : public IBinaryHeap >, + public virtual ds::aux::HasTree >{ +public: + BinaryHeapBase(){} + virtual ~BinaryHeapBase(){} + +}; + +// +// HeapParentChildPredicate will take two arguments of type E: parentElem and childElem, +// and returns true if the parentElem is allowed to be the parent of childElem in the heap. +// This predicate defines the heap comparison, which is by default min-heap +// +template > +class BinaryHeap : public BinaryHeapBase{ + friend class BinaryHeapTest; +public: + // public typedef + typedef ArrayBinaryTree Tree; +protected: + Tree bt; + typedef IndexedBTNode node_t; + typedef IndexedBTNode* node_tptr; + HeapParentChildPredicate lesseq; // the comparator predicate + + // This is the next insertion index + int _insertIndex; + bool _heapBad; // flag for corrupted heap + E _defaultElement; // placeholder value for returning from methods + + + // parent must be less than or equal to child + bool heapPropertyParentChild(node_t* pParent, node_t* pChild){ + if (pParent != NULL && pChild != NULL) + return lesseq(pParent->element, pChild->element); + else + return false; + } + + int getRootIndex(){ + return bt.getRootIndex(); + } + + // swap the elements of two nodes + // todo: is it possible to optimize the swap? + void swapElements(node_t* p1, node_t* p2){ + if (p1 && p2){ + E e1 = p1->element; + p1->setElement(p2->element); + p2->setElement(e1); + } + } + + // selects the appropriate child for bubble down + // checks heap property among parent, left, right nodes + // if no child is appropriate (or pNode is external/NULL), returns NULL + node_t* selectChildForBubbleDown(node_t* pNode){ + node_t *pChild = NULL; + if (pNode != NULL && bt.isInternal(pNode)){ + + node_t *pLeft = bt.left(pNode), + *pRight = bt.right(pNode); + + // if pNode has only one child, select it if it violates heap property + if (pLeft == NULL && pRight != NULL && + !heapPropertyParentChild(pNode, pRight)) { + pChild = pRight; + } + else if (pRight == NULL && pLeft != NULL && + !heapPropertyParentChild(pNode, pLeft) ) { + pChild = pLeft; + } + else{ + // both non-null + // find the smaller element among left and right + if (heapPropertyParentChild(pLeft, pRight)){ + // left <= right + pChild = pLeft; + } + else{ + // left > right + pChild = pRight; + } + // note that if left == right, left is selected + + // check heap property with parent + if (heapPropertyParentChild(pNode, pChild)){ + // no need to bubble down + pChild = NULL; + } + else{ + // should bubble down through pChild already selected + } + } + } + return pChild; + } + + // propagate a node up until the heap property is satisfied + void bubbleUp(node_t* pNode){ + if (pNode != NULL){ + if (pNode == bt.root()) return; // root; done + + node_t* pParent = bt.parent(pNode); // get parent + while ( pParent && pNode && !heapPropertyParentChild(pParent, pNode)){ // check heap property + swapElements(pParent, pNode); // swap because heap property violated + // prepare for next level + pNode = pParent; + pParent = bt.parent(pParent); + } + } + } + + // propagate a node down until the heap property is satisfied + void bubbleDown(node_t* pNode){ + if (pNode != NULL && bt.isInternal(pNode)){ + // find the child with largest discrepancy + node_t *pChild = selectChildForBubbleDown(pNode); + if(pChild != NULL){ + // found appropriate child for bubble down + swapElements(pNode, pChild); + bubbleDown(pChild); + } + } + } + + // forcefully sets the elements in the tree + // heap-property and size property is ignored + void setTree(const E* pElems, const int numElems){ + bt.clear(); // clear the tree + bt.fromArray(pElems, numElems); // build the tree + _insertIndex = size() + 1; // prepare for add/remove + } + +public: + // to be used by client code to refer to the output of push() + typedef node_t HeapNode; + + BinaryHeap(): _heapBad(false){ + } + BinaryHeap(const E* pArr, const int numElems) : _heapBad(false){ + heapify(pArr, numElems); + } + virtual ~BinaryHeap(){ + clear(); + } + + // pure virtual method from HasTree class + const Tree& tree() const{ return bt; } + + + + bool corrupted(){ + return _heapBad; + } + + void clear(){ + bt.clear(); + _insertIndex = bt.getRootIndex(); + _heapBad = false; + } + + void reset(){ + bt.reset(); + _insertIndex = bt.getRootIndex(); + _heapBad = false; + } + + /// public interface + // create a heap from an array + bool heapify(const E* pArr, const int numElems) { + if (pArr != NULL && numElems > 0){ + if (!empty()) + reset(); // clear current data and prepare for adding nodes + + for (int i = 0; i < numElems; i++){ + if (insert(pArr[i]) == NULL){ + _heapBad = true; + return false; + } + } + return true; + } + return false; + } + + + + // returns the top element + const E& top()const override { + E e; + if (size() > 0){ + return bt.root()->element; + } + else + return _defaultElement; + } + + // returns the top element and removes it from the heap + void pop() override + { + if (size() <= 0) return; + else{ + if (size() == 1){ + assert(bt.remove(bt.root()) == true); // // remove the only node + _insertIndex--; + } + else { + node_t* pNode = bt.nodeAt(_insertIndex - 1); // most recently added node + assert(pNode != NULL); + + swapElements(bt.root(), pNode); // swap element with root + assert(bt.remove(pNode) == true); // remove the most recently added node + bubbleDown(bt.root()); + _insertIndex--; // the last node at last level is deleted + } + } + } + + + // adds an element to the heap + // returns the pointer to the node + node_t* insert(const E& e) { + node_t* pNode = NULL; + if (bt.empty()){ + pNode = bt.addRoot(e); + if ( pNode != NULL){ + this->_insertIndex = pNode->index() + 1; + } + } + else{ + // insert the new node at appropriate index + pNode = bt.insertAt(e, _insertIndex); + if (pNode != NULL){ + // insertion ok + _insertIndex++; + // fix the heap + bubbleUp(pNode); + } + } + return pNode; + } + + void push(const E& e) override{ + assert(insert(e) != NULL); + } + + int size() const override{ + return bt.size(); + } + bool empty() const override{ + return bt.empty(); + } + // gives a string represenation of the heap + string toString(){ + return bt.toStringBFS(); + } +}; + + // a custom binary tree comparator + // treeA <= treeB if treeB/treeA not empty and + // treeA.root()->element <= treeB.root()->element + template + class TreeComparisonPredicate{ + typedef ArrayBinaryTree Tree; + public: + bool operator() (const Tree* pParent, const Tree* pChild) const{ + typedef ArrayBinaryTree::Node Node; + if (!pChild->empty() && !pParent->empty()){ + Node* pParentRoot = pParent->root(); + Node* pChildRoot = pChild->root(); + const E& pe = pParentRoot->element; + const E& ce = pChildRoot->element; + return pe <= ce; + } + else + return false; + } + }; + + // max-heap of numeric strings + class NumericStringMaxHeapPredicate{ + public: + bool operator() (const string& parent, const string& child) const{ + int p = atoi(parent.c_str()), + c = atoi(child.c_str()); + return p >= c; + } + }; + + +// test the BinaryHeap class +class BinaryHeapTest : public yasi::Test{ +protected: + typedef BinaryHeap::node_t node_t; + +public: + + template > + void checkHeapElements(HeapType* pHeap, const E* pElemsArr, const int numElems){ + return ArrayBinaryTreeTest::checkElements(&pHeap->bt, pElemsArr, numElems); + } + + void heapify(){ + const int heapSize = 5; + int arr[] = { 10, 5, 20, 15, 25 }; + int n = ARR_LENGTH(arr); + BinaryHeap h; + bool ok = h.heapify(arr, n); + ASSERT_EQ(true, ok) << "heapify failed"; + + // assuming min-heap + { + SCOPED_TRACE("heapify #1"); + int newArr[] = { 5, 10, 20, 15, 25 }; + checkHeapElements(&h, newArr, heapSize); + } + + ok = h.heapify(arr, -1); + ASSERT_EQ(false, ok) << "heapify succeeded with negative size"; + + ok = h.heapify(NULL, 4); + ASSERT_EQ(false, ok) << "heapify succeeded with NULL array"; + + } + + void top(){ + int e; + bool ok = true; + BinaryHeap h; + + h.insert(15); + h.insert(5); + h.insert(25); + h.insert(10); + h.insert(20); + + // assuming min-heap + ASSERT_EQ(5, h.size()) << "heap size should have been 5"; + e = h.top(); + ASSERT_EQ(5, e) << "top should have been 5"; + e = h.top(); + ASSERT_EQ(5, e) << "top should still have been 5"; + h.pop(); // pop 5 + e = h.top(); + ASSERT_EQ(10, e) << "top should still have been 10"; + h.pop(); // pop 10 + e = h.top(); + ASSERT_EQ(15, e) << "top should have been 15"; + h.pop(); // pop 15 + e = h.top(); + ASSERT_EQ(20, e) << "top should have been 20"; + h.pop(); // pop 20 + e = h.top(); + ASSERT_EQ(25, e) << "top should have been 25"; + h.pop(); // pop 25 + ASSERT_EQ(0, h.size()) << "heap size should have been 0"; + + + } + + void bubbleUp(){ + const int heapSize = 6; + int arr[] = { 5, 10, 15, 2, 30, 7 }; // 2 (child of 10) and 7 (child of 15), violate heap property + int n = ARR_LENGTH(arr); + BinaryHeap h; + h.setTree(arr, n); + // now the binary tree has heap-property violations + + node_t* pNode = h.bt.nodeAt(4); // 2 + h.bubbleUp(pNode); + // arr = {2, 5, 15, 10, 30, 7} + { + SCOPED_TRACE("bubbleUp(2)"); + int newArr[] = { 2, 5, 15, 10, 30, 7 }; + checkHeapElements(&h, newArr, heapSize); + } + + pNode = h.bt.nodeAt(6); // 7 + h.bubbleUp(pNode); // 7 should stop as a child of 2 + // arr = {2, 5, 7, 10, 30, 15} + { + SCOPED_TRACE("bubbleUp(7)"); + int newArr[] = { 2, 5, 7, 10, 30, 15 }; + checkHeapElements(&h, newArr, heapSize); + } + } + + void selectChildForBubbleDown(){ + BinaryHeap h; + // case: bubbleDown along right child + int arr[] = { 10, 20, 30 }; // 35 (parent of 100 and 15) and 15 (parent of 7) violate heap property + h.setTree(arr, 3); + node_t* pNode = h.selectChildForBubbleDown(h.bt.root()); // should be NULL + ASSERT_EQ(0, (int)pNode) << "should not bubble down"; + + h.clear(); + int arr2[] = { 10, 5, 8 }; + h.setTree(arr2, 3); + pNode = h.selectChildForBubbleDown(h.bt.root()); // should be 5 + ASSERT_EQ(5, pNode->element) << "should be 5"; + + h.clear(); + int arr3[] = { 10, 15, 8 }; + h.setTree(arr3, 3); + pNode = h.selectChildForBubbleDown(h.bt.root()); // should be 8 + ASSERT_EQ(8, pNode->element) << "should be 8"; + + h.clear(); + int arr4[] = { 5, 10, 10 }; + h.setTree(arr4, 3); + pNode = h.selectChildForBubbleDown(h.bt.root()); // should be right child of root + ASSERT_EQ(0, (int) pNode) << "should not bubble down"; + + h.clear(); + int arr5[] = { 10, 10, 10 }; + h.setTree(arr5, 3); + pNode = h.selectChildForBubbleDown(h.bt.root()); // should not bubble down + ASSERT_EQ(0, (int) pNode) << "should not bubble down when all equal"; + } + + void bubbleDown(){ + const int heapSize = 6; + // case: bubbleDown along right child + int arr[] = { 35, 100, 15, 50, 30, 7 }; // 35 (parent of 100 and 15) and 15 (parent of 7) violate heap property + int n = ARR_LENGTH(arr); + BinaryHeap h; + h.setTree(arr, n); + // now the binary tree has heap-property violations + + node_t* pNode = h.bt.root(); // 35 + h.bubbleDown(pNode); + // arr={15, 100, 7, 50, 30, 35} + { + SCOPED_TRACE("bubbleDown(35)"); + int newArr[] = { 15, 100, 7, 50, 30, 35 }; + checkHeapElements(&h, newArr, heapSize); + } + + h.bubbleDown(h.bt.root()); // 15 + // arr={7, 100, 15, 50, 30, 35} + { + SCOPED_TRACE("bubbleDown(15)"); + int newArr[] = { 7, 100, 15, 50, 30, 35 }; + checkHeapElements(&h, newArr, heapSize); + } + + h.bubbleDown(h.bt.nodeAt(4)); // no effect: external + // arr={7, 100, 15, 50, 30, 35} + { + SCOPED_TRACE("bubbleDown(50)"); + int newArr[] = { 7, 100, 15, 50, 30, 35 }; + checkHeapElements(&h, newArr, heapSize); + } + + // case: bubbleDown along left child + int arr2[] = { 35, 15, 100, 50, 30, 7 }; // 35 (parent of 100 and 15) and 100 (parent of 7) violate heap property + n = ARR_LENGTH(arr2); + BinaryHeap h2; + h2.setTree(arr2, n); + // now the binary tree has heap-property violations + + pNode = h2.bt.root(); // 35 + h2.bubbleDown(pNode); + // arr={15, 30, 100, 50, 35, 7} + { + SCOPED_TRACE("bubbleDown(35)"); + int newArr[] = { 15, 30, 100, 50, 35, 7 }; + checkHeapElements(&h2, newArr, heapSize); + } + } + + void insert(){ + BinaryHeap h; + + // root + h.insert(5); + // arr={5} + { + SCOPED_TRACE("insert(5)"); + int newArr[] = { 5 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + // add a large element, should not bubble up + h.insert(10); + // arr={5, 10} + { + SCOPED_TRACE("insert(10)"); + int newArr[] = { 5, 10 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + // add a small element, should bubble up + h.insert(3); + // arr = {3, 10, 5} + { + SCOPED_TRACE("insert(3)"); + int newArr[] = { 3, 10, 5 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + // add another small element, should bubble up + h.insert(1); + // arr = {1, 3, 5, 10} + { + SCOPED_TRACE("insert(1)"); + int newArr[] = { 1, 3, 5, 10 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + h.insert(2); + // arr = {1, 2, 5, 10, 3} + { + SCOPED_TRACE("insert(2)"); + int newArr[] = { 1, 2, 5, 10, 3 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + } + + void pop(){ + const int heapSize = 6; + int arr[] = { 5, 10, 15, 20, 25, 30 }; // valid heap + int n = ARR_LENGTH(arr); + BinaryHeap h; + h.setTree(arr, n); // valid heap + int e; + + e = h.top(); // e = 5 + h.pop(); + ASSERT_EQ(5, e) << "top elem should be 5"; + // arr={10, 20, 15, 30, 25} + { + SCOPED_TRACE("pop #1"); + int newArr[] = { 10, 20, 15, 30, 25 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + e = h.top(); // e = 10 + h.pop(); + ASSERT_EQ(10, e) << "top elem should be 10"; + // arr={15, 20, 25, 30} + { + SCOPED_TRACE("pop #2"); + int newArr[] = { 15, 20, 25, 30 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + e = h.top(); // e = 15 + h.pop(); + ASSERT_EQ(15, e) << "top elem should be 15"; + // arr={20, 30, 25} + { + SCOPED_TRACE("pop #3"); + int newArr[] = { 20, 30, 25 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + e = h.top(); // e = 20 + h.pop(); + ASSERT_EQ(20, e) << "top elem should be 20"; + // arr={25, 30} + { + SCOPED_TRACE("pop #4"); + int newArr[] = { 25, 30 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + e = h.top(); // e = 25 + h.pop(); + ASSERT_EQ(25, e) << "top elem should be 25"; + // arr={30} + { + SCOPED_TRACE("pop #5"); + int newArr[] = { 30 }; + checkHeapElements(&h, newArr, ARR_LENGTH(newArr)); + } + + e = h.top(); // e = 30 + h.pop(); + ASSERT_EQ(30, e) << "top elem should be 30"; + // arr={} + ASSERT_EQ(true, h.empty()) << "heap should have been empty"; + + } + + void maxHeap(){ + const int heapSize = 5; + int arr[] = { 10, 5, 20, 15, 25 }; + int n = ARR_LENGTH(arr); + typedef BinaryHeap > HeapType; + HeapType h; + bool ok = h.heapify(arr, n); + ASSERT_EQ(true, ok) << "heapify failed"; + + // should be max-heap + { + /* heap: + 25 + 20 10 + 5 15 + */ + SCOPED_TRACE("max-heap #1"); + //int newArr[] = { 25, 20, 10, 5, 15 }; + //checkHeapElements(&h, newArr, heapSize); + string actual = strPurge(h.toStringBFS(), "| "); + ASSERT_EQ("252010515", actual) << "bad max heap from heapify"; + } + + // add some more items + { + h.push(12); + h.push(30); + h.push(3); + h.push(40); + /* heap: + 40 + 30 25 + 20 15 10 12 + 3 5 + */ + SCOPED_TRACE("max-heap #2"); + string actual = strPurge(h.toStringBFS(), "| "); + ASSERT_EQ("4030252015101235", actual) << "bad max heap after add"; + } + + + } + + // compare a heap made of numeric strings, + // and the comparison will be done after turning them into numbers + // creates a max heap with predicate NumericStringMaxHeapPredicate + void customComparatorHeap(){ + const int heapSize = 5; + typedef string HeapElement; + HeapElement srcArr[] = { + HeapElement("10"), + HeapElement("5"), + HeapElement("20"), + HeapElement("15"), + HeapElement("25") + }; + int n = ARR_LENGTH(srcArr); + // max heap with binary trees as elements + typedef BinaryHeap HeapType; + HeapType h; + bool ok = h.heapify(srcArr, n); + ASSERT_EQ(true, ok) << "heapify failed"; + // should be max-heap + { + /* heap: + 25 + 20 10 + 5 15 + */ + SCOPED_TRACE("max-heap #1"); + string actual = strPurge(h.toStringBFS(), "| "); + ASSERT_EQ("252010515", actual) << "bad max heap from heapify"; + } + + // add some more items + { + h.push("12"); + h.push("30"); + h.push("3"); + h.push("40"); + /* heap: + 40 + 30 25 + 20 15 10 12 + 3 5 + */ + SCOPED_TRACE("max-heap #2"); + string actual = strPurge(h.toStringBFS(), "| "); + ASSERT_EQ("4030252015101235", actual) << "bad max heap after add"; + } + + + } + +}; + + + +ADD_TEST_F(BinaryHeapTest, heapify); +ADD_TEST_F(BinaryHeapTest, top); +ADD_TEST_F(BinaryHeapTest, pop); +ADD_TEST_F(BinaryHeapTest, insert); +ADD_TEST_F(BinaryHeapTest, bubbleUp); +ADD_TEST_F(BinaryHeapTest, selectChildForBubbleDown); +ADD_TEST_F(BinaryHeapTest, bubbleDown); +ADD_TEST_F(BinaryHeapTest, maxHeap); +ADD_TEST_F(BinaryHeapTest, customComparatorHeap); + + +////////////////////////////////////////////// +//////////////// Mutable Heap //////////////// +////////////////////////////////////////////// +template +class MutableHeap : public BinaryHeap < KVPair > { + ////////// enable testing /////////// + friend class MutableHeapTest; + typedef KVPair HeapElement; + typedef BinaryHeap Heap; + typedef typename Heap::HeapNode Node; + +public: + virtual ~MutableHeap(){} + MutableHeap(){} + MutableHeap(const HeapElement* pArr, const int numElems) : Heap(pArr, numElems){} + void updateKey(Node* pNode, const Key newKey){ + if (pNode){ + HeapElement newElem(newKey); + if (pNode->element != newElem){ + // need update + if (newElem < pNode->element){ + // need bubble-up + ((HeapElement*)& pNode->element)->key = newKey; // updated + bubbleUp(pNode); + } + else{ + // need bubble-down + ((HeapElement*)& pNode->element)->key = newKey; // updated + bubbleDown(pNode); + } + } + } + } +}; + +class MutableHeapTest : public yasi::Test{ + typedef KVPair Pair; + typedef MutableHeap MHeap; +public: + void updateKey(){ + MHeap h; + MHeap::HeapNode* n[5]; + for (int i = 0; i < 5; i++){ + n[i] = h.insert(Pair(i)); // both key and value are i + } + + string str = h.toStringBFS(); // should be 0 1 2 3 4 + string strBefore = strPurge(str, "| "); + string expectedStrBefore = "01234"; + ASSERT_EQ(expectedStrBefore, strBefore) << "heap toString() mismatch"; + + // now update key 3 to 9 + h.updateKey(n[3], 9); + // heap: 0 1 2 9 4 + str = h.toStringBFS(); + string strAfter = strPurge(str, "| "); + string expectedStrAfter = "01294"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 3-->9"; + + // now update key 2 to -8 + h.updateKey(n[2], -8); + // heap: -8 1 0 9 4 + str = h.toStringBFS(); + strAfter = strPurge(str, "| "); + expectedStrAfter = "-81094"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 2-->-8"; + + // now update key 9 to -9 + h.updateKey(n[3], -9); + // heap: -9 -8 0 1 4 + str = h.toStringBFS(); + strAfter = strPurge(str, "| "); + expectedStrAfter = "-9-8014"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 9-->-9"; + + } +}; + +ADD_TEST_F(MutableHeapTest, updateKey); + + +} // namespace yasi.ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.binarysearchtree.h b/YASI_12/ds.binarysearchtree.h new file mode 100644 index 0000000..7a4fe00 --- /dev/null +++ b/YASI_12/ds.binarysearchtree.h @@ -0,0 +1,449 @@ +#pragma once +#include "common.h" +#include "gtest\gtest.h" +#include "ds.binarytree.h" +#include "ds.comparable.h" +#include "ds.aux.hastree.h" + +namespace yasi{ +namespace ds{ + + + // binary search tree interface + template > + class IBinarySearchTree : public virtual IBinaryTree{ + public: + virtual ~IBinarySearchTree(){} + virtual Node* find(const E&) const = 0; + virtual Node* insert(const E&) = 0; + virtual Node* findMin() const = 0; + virtual Node* findMax() const = 0; + virtual bool removeNode(Node*, E&) = 0; + virtual bool removeNode(Node*) = 0; + }; + + //template + //class LessEq{ + //public: + // virtual ~LessEq(){} + // virtual bool operator() (const E& a, const E& b) { + // return (a <= b); // operator <= must be defined for class E + // } + //}; + + + + // binary search tree + // the element class E must implement IComparable interface + // i.e. have the <= operator defined/overloaded + // + template + class BinarySearchTree :public virtual BinaryTree, + public virtual IBinarySearchTree{ + //////////////// enable testing ////////////// + friend class BinarySearchTreeTest; + protected: + typedef BinaryTreeNode node_t; + typedef BinaryTree tree_t; + enum { + LEFT = 0, + RIGHT + }; + + // default comparator functional object + // user must supply/have appropriate funcitonal + // see class LessEq + //LessEq lessEq; + + node_t* _find(const E& e, node_t* pNode) const{ + if (pNode == NULL) return NULL; + + if (e == pNode->element) return pNode; + else if (e < pNode->element){ + // search left + return _find(e, pNode->left()); + } + else{ + // search right + return _find(e, pNode->right()); + } + } + + node_t* _insert(const E& e, node_t* pParent, int dir){ + node_t* pNode = (dir == LEFT) ? pParent->left() : pParent->right(); + + if (pNode == NULL){ + // create a new node and set as the child of the parent + if (dir == LEFT) + return addLeft(e, pParent); + else + return addRight(e, pParent); + } + else{ + if (e < pNode->element) + return _insert(e, pNode, LEFT); + else + return _insert(e, pNode, RIGHT); + } + } + //node_t* leftMostChild(node_t* pNode) const{ + // if (pNode == NULL ) return NULL; + // while (pNode->left() != NULL){ + // pNode = pNode->left(); + // } + // return pNode; + + //} + + //// returns the leftmost node in the right subtree + //node_t* inorderSuccessor(const node_t* pNode, const node_t* pRoot) const{ + // if (pNode == NULL) return NULL; + + // if (pNode->right() == NULL){ + // // right is NULL + + // node_t* pParent = pNode->parent(); + // if (isLeft(pNode)){ + // // insucc(left-child) = parent + // return pParent; + // } + // else{ + // // insucc(right-child) = ancestor that is a left child + // while (isRight(pParent)){ + // pParent = pParent->parent(); + // } + // if (pParent != pRoot){ + // return pParent->parent(); + // } + // else{ + // // last node in inorder traversal + // return NULL; + // } + // } + // } + // else{ + // // right is not NULL + // if (pNode->right()->left() == NULL) + // return pNode->right(); // right->left = NULL + // else + // return leftMostChild(pNode->right()); + // } + //} + + + public: + // for public use e.g., with the output of find() method + typedef node_t Node; + + BinarySearchTree() : tree_t() { + //this->_lessEqPredicate = this->lessEq; + + } + virtual ~BinarySearchTree(){} + + //void setLessEqFunctional(LessEq pred){ + // this->lessEq = pred; + //} + + ///////////////////////////////////////////// + ////////// Interface IBinarySearchTree ////// + ///////////////////////////////////////////// + node_t* find(const E& e) const override{ + return _find(e, root()); + } + node_t* insert(const E& e) override { + node_t* pRoot = root(); + if (pRoot == NULL){ + addRoot(e); + return root(); + } + else{ + pRoot = root(); + if (e < pRoot->element) + return _insert(e, pRoot, LEFT); + else + return _insert(e, pRoot, RIGHT); + } + } + // left..left..left..most node + node_t* findMin() const override{ + return leftMostChild(root()); + } + // right..right..right..most node + node_t* findMax() const override{ + node_t* pNode = root(); + if (pNode == NULL) return NULL; + while (pNode->right() != NULL){ + pNode = pNode->right(); + } + return pNode; + } + + bool removeNode(node_t* pNode, E& e) override{ + if (pNode == NULL) return false; + + e = pNode->element; + if (isExternal(pNode)){ + // easy: node is external + // caution: could be an isolated node + if (isLeft(pNode)){ + pNode->parent()->setLeft(NULL); + } + else if(isRight(pNode)){ + pNode->parent()->setRight(NULL); + } + // update root, if needed + if (pNode == pRoot) pRoot = NULL; + // finally + DELETE_SAFE(pNode); + _size--; + if (_size == 0) pRoot = NULL; + } + else{ + // node is internal + if (numChildren(pNode) == 1){ + // easy: node has only one child + // simply elevate it + node_t* pChild = hasLeft(pNode) ? pNode->left() : pNode->right(); + if (isLeft(pNode)){ + pNode->parent()->setLeft(pChild); + } + else if(isRight(pNode)){ + pNode->parent()->setRight(pChild); + } + pChild->setParent(pNode->parent()); + // update root, if needed + if (pNode == pRoot) pRoot = pChild; + // finally + DELETE_SAFE(pNode); + _size--; + if (_size == 0) pRoot = NULL; + } + else{ + // hard: node has both children + // replace node with its inorder successor which must exist because node is internal + node_t* pSucc = inorderSuccessor(pNode); + pNode->setElement(pSucc->element); + // now delete the inorder successor + return removeNode(pSucc); + } + } + + // check for degenerate case when tree is empty + return true; + } + bool removeNode(node_t* pNode) override{ + E e; + return removeNode(pNode, e); + } + }; + + class BinarySearchTreeTest : public yasi::Test{ + //template + //class CustomLessEq : public virtual LessEq{ + //public: + // virtual ~CustomLessEq(){} + // bool operator() (const E& a, const E& b) override { + // return 2 * a <= b; + // } + //}; + protected: + typedef BinaryTreeNode node_t; + typedef BinarySearchTree tree_t; + public: + void all(){ + tree_t t; + int temp; + + ASSERT_EQ(0, t.size()) << "initial size not zero"; + ASSERT_EQ(true, t.empty()) << "initially not empty"; + ASSERT_EQ(NULL, (int)t.root()) << "initially root not NULL"; + ASSERT_EQ(NULL, (int)t.findMin()) << "findMin() not NULL on empty tree"; + ASSERT_EQ(NULL, (int)t.findMax()) << "findMax() not NULL on empty tree"; + + node_t *n3, *n5, *n7, *n72, *n10, *n11; + + n3 = t.insert(temp = 3); + ASSERT_EQ(1, t.size()) << "size not 1"; + ASSERT_EQ(false, t.empty()) << "tree empty"; + ASSERT_NE(NULL, (int)t.root()) << "root NULL"; + ASSERT_EQ(n3, t.root()) << "root not n3"; + ASSERT_EQ(3, t.root()->element) << "root element not 3"; + + // insert a bunch of nodes + n10 = t.insert(temp = 10); + n7 = t.insert(temp = 7); + n5 = t.insert(temp = 5); + n11 = t.insert(temp = 11); + n72 = t.insert(temp = 7); + ASSERT_EQ(6, t.size()) << "size not 6"; + + string str = t.toString(); + string actual = strPurge(str, "| "); + // preorder + string expected = "31075711"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + + // insert some more + node_t *n1, *n2, *n4, *n32, *n73, *n102, *n13; + n2 = t.insert(temp = 2); + n73 = t.insert(temp = 7); + n1 = t.insert(temp = 1); + n4 = t.insert(temp = 4); + n102 = t.insert(temp = 10); + n32 = t.insert(temp = 3); + n13 = t.insert(temp = 13); + ASSERT_EQ(13, t.size()) << "size not 13"; + + str = t.toString(); + actual = strPurge(str, "| "); + // preorder + expected = "32110754377111013"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + + //////// now find ////////// + node_t* n = t.find(100); // should be NULL + ASSERT_EQ(NULL, (int)n) << "n not NULL"; + + n = t.find(8); // should be NULL + ASSERT_EQ(NULL, (int)n) << "n not NULL"; + + n = t.find(10); // should be right child of 3 + ASSERT_NE(NULL, (int)n) << "n is NULL"; + ASSERT_EQ(n10, n) << "n not n10"; + ASSERT_EQ(10, n->element) << "n->element not 10"; + ASSERT_EQ(n3, n->parent()) << "n->parent not n3"; + ASSERT_EQ(n3->right(), n) << "n3->right not n"; + + n = t.find(4); // should be left child of 5 + ASSERT_NE(NULL, (int)n) << "n is NULL"; + ASSERT_EQ(n4, n) << "n not n4"; + ASSERT_EQ(4, n->element) << "n->element not 4"; + ASSERT_EQ(n5, n->parent()) << "n->parent not n5"; + ASSERT_EQ(n5->left(), n) << "n5->left not n"; + + ///////// find min //////// + n = t.findMin(); + ASSERT_EQ(n1, n) << "n not n1"; + ASSERT_EQ(1, n->element) << "n->element not 1"; + + n = t.findMax(); + ASSERT_EQ(n13, n) << "n not n13"; + ASSERT_EQ(13, n->element) << "n->element not 13"; + + ////// inorder successor ///////// + n = t.inorderSuccessor(t.root()); + ASSERT_EQ(n32, n) << "n32 not the inorder successor of n3"; + n = t.inorderSuccessor(n2); + ASSERT_EQ(n3, n) << "n3 not the inorder successor of n2"; + n = t.inorderSuccessor(n73); + ASSERT_EQ(n10, n) << "n10 not the inorder successor of n73"; + n = t.inorderSuccessor(n13); + ASSERT_EQ(NULL, (int)n) << "NULL not the inorder successor of n13"; + n = t.inorderSuccessor(n7); + ASSERT_EQ(n72, n) << "n72 not the inorder successor of n7"; + + //////////////////// remove ///////////////// + int e; + bool ok; + ok = t.removeNode(NULL, e); + ASSERT_EQ(false, ok) << "ok not false"; + + // remove external node + { + SCOPED_TRACE("remove n13"); + ok = t.removeNode(n13, e); + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(13, e) << "removed element not 13"; + ASSERT_EQ(12, t.size()) << "t.size() not 12"; + ASSERT_EQ(NULL, (int)t.sibling(n102)) << "sibling of n102 not NULL"; + ASSERT_EQ(1, t.numChildren(n11)) << "n11 should have 1 child"; + ASSERT_EQ(11, t.findMax()->element) << "max not 11"; + str = t.toString(); + actual = strPurge(str, "| "); + // preorder + expected = "321107543771110"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + } + + // remove internal node with one child + { + SCOPED_TRACE("remove n4"); + ok = t.removeNode(n4, e); + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(4, e) << "removed element not 4"; + ASSERT_EQ(11, t.size()) << "t.size() not 11"; + ASSERT_EQ(5, n32->parent()->element) << "parent->element of n32 not 5"; + ASSERT_EQ(n5, n32->parent()) << "parent of n32 not n5"; + ASSERT_EQ(n32, n5->left()) << "left of n5 not n32"; + str = t.toString(); + actual = strPurge(str, "| "); + // preorder + expected = "32110753771110"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + } + + // remove internal node with two children + { + SCOPED_TRACE("remove n7"); + ok = t.removeNode(n7, e); // inorder-succ is n72 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(7, e) << "removed element not 7"; + ASSERT_EQ(10, t.size()) << "t.size() not 10"; + ASSERT_EQ(n7, n73->parent()) << "n73->parent not n7"; + ASSERT_EQ(n73, n7->right()) << "n7->right not n73"; + str = t.toString(); + actual = strPurge(str, "| "); + // preorder + expected = "3211075371110"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + } + + // remove another internal node with two children + { + SCOPED_TRACE("remove n10"); + ASSERT_EQ(false, t.isExternal(n11)) << "n11 not internal"; + ASSERT_EQ(n102, n11->left()) << "n11->left not n102"; + ASSERT_EQ(10, n11->left()->element) << "n11->left->element not 10"; + ok = t.removeNode(n10, e); + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(10, e) << "removed element not 10"; + ASSERT_EQ(9, t.size()) << "t.size() not 9"; + ASSERT_EQ(true, t.isExternal(n11)) << "n11 not external"; + str = t.toString(); + actual = strPurge(str, "| "); + // preorder + expected = "32110753711"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + } + + // remove root + { + SCOPED_TRACE("remove root (n3)"); + ASSERT_EQ(false, t.isExternal(n5)) << "n5 not internal"; + ASSERT_EQ(3, n5->left()->element) << "n5->left->element not 3"; + ok = t.removeNode(t.root(), e); + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(3, e) << "removed element not 10"; + ASSERT_EQ(8, t.size()) << "t.size() not 9"; + ASSERT_EQ(true, t.isExternal(n5)) << "n5 not external"; + str = t.toString(); + actual = strPurge(str, "| "); + // preorder + expected = "3211075711"; + ASSERT_EQ(expected, actual) << "tree string not correct"; + } + + // clear + t.clear(); + ASSERT_EQ(0, t.size()) << "size not 0"; + ASSERT_EQ(true, t.empty()) << "t not empty"; + + + } + }; + + ADD_TEST_F(BinarySearchTreeTest, all); + +} // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.binarytree.h b/YASI_12/ds.binarytree.h new file mode 100644 index 0000000..dc8d8e2 --- /dev/null +++ b/YASI_12/ds.binarytree.h @@ -0,0 +1,532 @@ +#pragma once +#include "common.h" +#include "utils.h" +#include "ds.binarytreebase.h" +#include "gtest\gtest.h" +#include "ds.tree.h" +#include +using namespace std; + +namespace yasi{ +namespace ds{ + +// class binary tree node + +template +class IBinaryTreeNode{ +public: + virtual ~IBinaryTreeNode(){} + virtual Node* parent() const = 0; + virtual void setParent(Node*) = 0; + virtual Node* left() const = 0; + virtual void setLeft(Node*) = 0; + virtual Node* right() const = 0; + virtual void setRight(Node*) = 0; +}; + + +template +class BinaryTreeNode : public virtual TreeNode, + public virtual IBinaryTreeNode< BinaryTreeNode >{ + friend class BinaryTreeNodeTest; + typedef BinaryTreeNode node_t; + //inline node_t* This(){ + // return static_cast(this); + //} +protected: + node_t *pLeft, + *pRight, + *pParent; +public: + BinaryTreeNode() : TreeNode(){ + pLeft = pRight = pParent = NULL; + } + BinaryTreeNode(E e) : TreeNode(e){ + pLeft = pRight = pParent = NULL; + } + BinaryTreeNode(E e, node_t *pParent) : TreeNode(e){ + pLeft = pRight = NULL; + this->pParent = pParent; + } + node_t* parent() const override{ + return pParent; + } + node_t* left() const override{ + return pLeft; + } + node_t* right() const override{ + return pRight; + } + virtual void setParent(node_t* pNode) override{ pParent = pNode; } + virtual void setLeft(node_t* pNode)override{ pLeft = pNode; } + virtual void setRight(node_t* pNode)override{ pRight = pNode; } +}; + + + + +template > +class BinaryTree : public virtual BinaryTreeBase { + //////////////// enable testing //////////// + friend class BinaryTreeTest; + +protected: + typedef Node node_t; + + int _size; + node_t* pRoot; +public: + BinaryTree(){ + _size = 0; + pRoot = NULL; + } + ~BinaryTree(){ + this->clear(); + } + + ////////////////////////////////////////////// + /////////// Interface ITree ////////////////// + ////////////////////////////////////////////// + + int size() const override { return _size; } + bool empty() const override { return _size == 0 && pRoot == NULL; } + bool isRoot(const node_t* pNode) const override { return pNode && pNode == pRoot; } + + // removes the subtree rooted at a node and saves the value at that node at the output argument e. + // returns false if any error occurs, true otherwise + bool remove(node_t* pNode, E& e) override { + if (pNode){ + bool success = true; + E temp; + if (hasLeft(pNode)){ + success = remove(left(pNode), temp); + } + if (hasRight(pNode)){ + success &= remove(right(pNode), temp); + } + // subtree deleted + return success && removeNode(pNode, e); + } + else + return false; + } + bool remove(node_t* pNode) override { E e; return remove(pNode, e); } + // removes a node and saves the value at that node at the output argument e. + // only external nodes can be removed + // returns false if any error occurs, true otherwise + bool removeNode(node_t* pNode, E& e) override { + if (pNode && isExternal(pNode)){ + e = pNode->element; + if (pNode == pRoot) pRoot = NULL; + // clear incoming links + if (isLeft(pNode)) pNode->parent()->setLeft(NULL); + else if (isRight(pNode)) pNode->parent()->setRight(NULL); + // finally + DELETE_SAFE(pNode); // delete the allocated memory + _size--; // size decreases + return true; + } + else + return false; + } + bool removeNode(node_t* pNode) override { E e; return removeNode(pNode, e); } + + + node_t* root() const override { return pRoot; } + node_t* addRoot(const E& e)override { + if (!pRoot && _size == 0) { + pRoot = new node_t(e); + _size = 1; + return pRoot; + } + else return NULL; + } + + + ////////////////////////////////////////////// + //////// Interface IBinaryTree /////////////// + ////////////////////////////////////////////// + inline bool nodeNotNull(const node_t* pNode) const override{ return pNode != NULL; } + node_t* left(const node_t* pNode) const override { return pNode ? pNode->left() : NULL; } + node_t* right(const node_t* pNode) const override { return pNode ? pNode->right() : NULL; } + node_t* parent(const node_t* pNode) const override { return pNode ? pNode->parent() : NULL; } + // succeeds if the node does not have a left child + node_t* addLeft(const E& e, node_t* pParent) override { + if (!pParent || hasLeft(pParent)) return NULL; + + node_t* pNode = new node_t(e, pParent); + pParent->setLeft(pNode); + _size++; + return pNode; + } + node_t* addRight(const E& e, node_t* pParent) override { + if (!pParent || hasRight(pParent)) return NULL; + + node_t* pNode = new node_t(e, pParent); + pParent->setRight(pNode); + _size++; + return pNode; + } + + //// populates the list argument with the children, if exists, first left then right + //void children(node_t* pNode, DoublyLinkedList& list) const{ + // if (hasRight(pNode)) list.addFront(right(pNode)); + // if (hasLeft(pNode)) list->addFront(left(pNode)); + //} + + void clear(){ + remove(root()); + pRoot = NULL; + _size = 0; + } + +}; + +// test +class BinaryTreeTest : public yasi::Test{ +public: + typedef BinaryTree > Tree; + typedef Tree::node_t node_t; + /////////////////////////// + /////// IBinaryTree methods + /////////////////////////// + + // size, empty + // left, right, parent, sibling, root, numChildren + // isLeft, isRight, hasLeft, hasRight + // isInternal, isExternal, isRoot + // addLeft, addRight, addRoot + void add(){ + Tree t; + + // add root + node_t* n0 = t.addRoot(0); + ASSERT_EQ(1, t.size()) << "size not 1"; + ASSERT_EQ(false, t.empty()) << "tree empty"; + ASSERT_EQ(n0, t.root()) << "root not n0"; + ASSERT_EQ(true, t.isRoot(n0)) << "root not n0"; + ASSERT_EQ(0, n0->element) << "root-element not 0"; + ASSERT_EQ(false, t.isLeft(n0)) << "n0 not a left child"; + ASSERT_EQ(false, t.isRight(n0)) << "n0 not a right child"; + ASSERT_EQ(false, t.hasLeft(n0)) << "root has not left child"; + ASSERT_EQ(false, t.hasRight(n0)) << "root has no right child"; + ASSERT_EQ(false, t.isInternal(n0)) << "root not internal yet"; + ASSERT_EQ(true, t.isExternal(n0)) << "root should be external now"; + ASSERT_EQ(NULL, t.left(n0)) << "left(root) should be NULL"; + ASSERT_EQ(NULL, t.right(n0)) << "right(root) should be NULL"; + ASSERT_EQ(NULL, t.parent(n0)) << "parent(root) should be NULL"; + ASSERT_EQ(NULL, t.sibling(n0)) << "sibling(root) should be NULL"; + ASSERT_EQ(0, t.numChildren(n0)) << "numChildren(root) should be 0"; + + /////////////// add root->left + node_t* n1 = t.addLeft(1, t.root()); + ASSERT_EQ(2, t.size()) << "size not 2"; + ASSERT_EQ(false, t.empty()) << "tree empty"; + // about root + ASSERT_EQ(n0, t.root()) << "root not n0"; + ASSERT_EQ(n0, t.parent(n1)) << "n1 not child of n0"; + ASSERT_EQ(true, t.hasLeft(n0)) << "root has a left child"; + ASSERT_EQ(false, t.hasRight(n0)) << "root has no right child"; + ASSERT_EQ(true, t.isInternal(n0)) << "root is internal"; + ASSERT_EQ(false, t.isExternal(n0)) << "root not external now"; + ASSERT_EQ(NULL, t.right(n0)) << "right(root) should be NULL"; + ASSERT_EQ(NULL, t.parent(n0)) << "parent(root) should be NULL"; + ASSERT_EQ(NULL, t.sibling(n0)) << "sibling(root) should be NULL"; + ASSERT_EQ(1, t.numChildren(n0)) << "numChildren(root) should be 1"; + // about root->left + ASSERT_EQ(1, n1->element) << "element not 1"; + ASSERT_EQ(n1, t.left(n0)) << "n1 not left child of n0"; + ASSERT_NE(n1, t.right(n0)) << "n1 right child of n0"; + ASSERT_EQ(true, t.isLeft(n1)) << "n1 is a left child"; + ASSERT_EQ(false, t.isRight(n1)) << "n1 is a right child"; + ASSERT_EQ(n0, t.parent(n1)) << "n0 not parent of n1"; + ASSERT_EQ(NULL, t.sibling(n1)) << "sibling(n1) wrong"; + ASSERT_EQ(false, t.isInternal(n1)) << "n1 is external"; + ASSERT_EQ(true, t.isExternal(n1)) << "n1 not internal now"; + ASSERT_EQ(false, t.hasLeft(n1)) << "n1 has left child"; + ASSERT_EQ(false, t.hasRight(n1)) << "n1 has right child"; + ASSERT_EQ(0, t.numChildren(n1)) << "numChildren(n1) should be 0"; + ASSERT_EQ(NULL, t.addLeft(-3, t.root())) << "root->left overwritten by addLeft()"; + + /////////// add root->right + node_t* n2 = t.addRight(2, t.root()); + ASSERT_EQ(3, t.size()) << "size not 3"; + ASSERT_EQ(false, t.empty()) << "tree empty"; + // about root + ASSERT_EQ(n0, t.root()) << "root not n0"; + ASSERT_EQ(n0, t.parent(n2)) << "n2 not child of n0"; + ASSERT_EQ(true, t.hasLeft(n0)) << "root has a left child"; + ASSERT_EQ(true, t.hasRight(n0)) << "root has no right child"; + ASSERT_EQ(true, t.isInternal(n0)) << "root is internal"; + ASSERT_EQ(false, t.isExternal(n0)) << "root not external now"; + ASSERT_EQ(n2, t.right(n0)) << "right(root) should be n2"; + ASSERT_EQ(NULL, t.parent(n0)) << "parent(root) should be NULL"; + ASSERT_EQ(NULL, t.sibling(n0)) << "sibling(root) should be NULL"; + ASSERT_EQ(2, t.numChildren(n0)) << "numChildren(root) should be 2"; + // about root->left + ASSERT_EQ(1, n1->element) << "element not 1"; + ASSERT_EQ(n1, t.left(n0)) << "n1 not left child of n0"; + ASSERT_NE(n1, t.right(n0)) << "n1 right child of n0"; + ASSERT_EQ(true, t.isLeft(n1)) << "n1 is a left child"; + ASSERT_EQ(false, t.isRight(n1)) << "n1 is a right child"; + ASSERT_EQ(n0, t.parent(n1)) << "n0 not parent of n1"; + ASSERT_EQ(n2, t.sibling(n1)) << "sibling(n1) wrong"; + ASSERT_EQ(false, t.isInternal(n1)) << "n1 is external"; + ASSERT_EQ(true, t.isExternal(n1)) << "n1 not internal now"; + ASSERT_EQ(false, t.hasLeft(n1)) << "n1 has left child"; + ASSERT_EQ(false, t.hasRight(n1)) << "n1 has right child"; + ASSERT_EQ(0, t.numChildren(n1)) << "numChildren(n1) should be 0"; + // about root->right + ASSERT_EQ(2, n2->element) << "element not 2"; + ASSERT_EQ(n2, t.right(n0)) << "n2 not right child of n0"; + ASSERT_NE(n2, t.left(n0)) << "n2 left child of n0"; + ASSERT_EQ(false, t.isLeft(n2)) << "n2 is a left child"; + ASSERT_EQ(true, t.isRight(n2)) << "n2 not a right child"; + ASSERT_EQ(n0, t.parent(n2)) << "n0 not parent of n2"; + ASSERT_EQ(n1, t.sibling(n2)) << "sibling(n2) should be n1"; + ASSERT_EQ(false, t.isInternal(n2)) << "n2 is external"; + ASSERT_EQ(true, t.isExternal(n2)) << "n2 not internal now"; + ASSERT_EQ(false, t.hasLeft(n2)) << "n2 has left child"; + ASSERT_EQ(false, t.hasRight(n2)) << "n2 has right child"; + ASSERT_EQ(0, t.numChildren(n2)) << "numChildren(n2) should be 0"; + ASSERT_EQ(NULL, t.addRight(-3, t.root())) << "root->right overwritten by addRight()"; + + /////////// add root->left->right + node_t* n4 = t.addRight(4, n1); + ASSERT_EQ(4, t.size()) << "size not 4"; + ASSERT_EQ(false, t.empty()) << "tree empty"; + // about root + ASSERT_EQ(n0, t.root()) << "root not n0"; + ASSERT_EQ(n0, t.parent(t.parent(n4))) << "n2 not grand child of n0"; + ASSERT_EQ(true, t.hasLeft(n0)) << "root has a left child"; + ASSERT_EQ(true, t.hasRight(n0)) << "root has no right child"; + ASSERT_EQ(true, t.isInternal(n0)) << "root is internal"; + ASSERT_EQ(false, t.isExternal(n0)) << "root not external now"; + ASSERT_EQ(n2, t.right(n0)) << "right(root) should be n2"; + ASSERT_EQ(n1, t.left(n0)) << "left(root) should be n1"; + ASSERT_EQ(NULL, t.parent(n0)) << "parent(root) should be NULL"; + ASSERT_EQ(NULL, t.sibling(n0)) << "sibling(root) should be NULL"; + ASSERT_EQ(2, t.numChildren(n0)) << "numChildren(root) should be 1"; + // about root->left + ASSERT_EQ(1, n1->element) << "element not 1"; + ASSERT_EQ(n1, t.left(n0)) << "n1 not left child of n0"; + ASSERT_NE(n1, t.right(n0)) << "n1 right child of n0"; + ASSERT_EQ(true, t.isLeft(n1)) << "n1 is a left child"; + ASSERT_EQ(false, t.isRight(n1)) << "n1 is a right child"; + ASSERT_EQ(n0, t.parent(n1)) << "n0 not parent of n1"; + ASSERT_EQ(n2, t.sibling(n1)) << "sibling(n1) wrong"; + ASSERT_EQ(true, t.isInternal(n1)) << "n1 external"; + ASSERT_EQ(false, t.isExternal(n1)) << "n1 internal now"; + ASSERT_EQ(false, t.hasLeft(n1)) << "n1 has no left child"; + ASSERT_EQ(true, t.hasRight(n1)) << "n1 has no right child"; + ASSERT_EQ(1, t.numChildren(n1)) << "numChildren(n1) should be 1"; + // about root->right + ASSERT_EQ(2, n2->element) << "element not 2"; + ASSERT_EQ(n2, t.right(n0)) << "n2 not right child of n0"; + ASSERT_NE(n2, t.left(n0)) << "n2 left child of n0"; + ASSERT_EQ(false, t.isLeft(n2)) << "n2 is a left child"; + ASSERT_EQ(true, t.isRight(n2)) << "n2 not a right child"; + ASSERT_EQ(n0, t.parent(n2)) << "n0 not parent of n2"; + ASSERT_EQ(n1, t.sibling(n2)) << "sibling(n2) should be n1"; + ASSERT_EQ(false, t.isInternal(n2)) << "n2 is external"; + ASSERT_EQ(true, t.isExternal(n2)) << "n2 not internal now"; + ASSERT_EQ(false, t.hasLeft(n2)) << "n2 has left child"; + ASSERT_EQ(false, t.hasRight(n2)) << "n2 has right child"; + ASSERT_EQ(0, t.numChildren(n2)) << "numChildren(n2) should be 0"; + // about root->left->right + ASSERT_EQ(4, n4->element) << "element not 4"; + ASSERT_EQ(n4, t.right(n1)) << "n4 not right child of n1"; + ASSERT_NE(n4, t.left(n1)) << "n4 left child of n1"; + ASSERT_EQ(false, t.isLeft(n4)) << "n4 is a left child"; + ASSERT_EQ(true, t.isRight(n4)) << "n4 not a right child"; + ASSERT_EQ(n1, t.parent(n4)) << "n1 not parent of n4"; + ASSERT_EQ(NULL, t.sibling(n4)) << "sibling(n4) should be NULL"; + ASSERT_EQ(false, t.isInternal(n4)) << "n4 is external"; + ASSERT_EQ(true, t.isExternal(n4)) << "n4 not internal now"; + ASSERT_EQ(false, t.hasLeft(n4)) << "n4 has left child"; + ASSERT_EQ(false, t.hasRight(n4)) << "n4 has right child"; + ASSERT_EQ(0, t.numChildren(n4)) << "numChildren(n4) should be 0"; + ASSERT_EQ(NULL, t.addRight(-3, t.root()->left() )) << "root->left->right overwritten by addRight()"; + + + + //ASSERT_EQ(0, 1) << "test incomplete"; + + } + + + + // remove + void removeNode(){ + Tree t; + node_t* n0 = t.addRoot(0); + node_t* n1 = t.addLeft(1, n0); + node_t* n2 = t.addRight(2, n0); + node_t* n4 = t.addRight(4, n1); + // remove root->left->right + int e; + bool ok = t.removeNode(n4, e); // remove n4 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(4, e) << "wrong element"; + ASSERT_EQ(3, t.size()) << "size not 3"; + ASSERT_EQ(NULL, n1->left()) << "n1->left not NULL"; + ASSERT_EQ(0, t.numChildren(n1)) << "n1 has children"; + ASSERT_EQ(false, t.isInternal(n1)) << "n1 still internal"; + ASSERT_EQ(true, t.isExternal(n1)) << "n1 not external"; + + // now delete n2 + ok = t.removeNode(n2, e); // remove n4 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(2, e) << "wrong element"; + ASSERT_EQ(2, t.size()) << "size not 2"; + ASSERT_EQ(NULL, n0->right()) << "n0->right not NULL"; + ASSERT_EQ(n1, n0->left()) << "n0->left not n1"; + ASSERT_EQ(false, t.hasRight(t.root())) << "root still has right"; + ASSERT_EQ(true, t.hasLeft(t.root())) << "root has no left"; + ASSERT_EQ(1, t.numChildren(t.root())) << "n1->left not NULL"; + ASSERT_EQ(NULL, t.sibling(n1)) << "n1 still has sibling"; + + // try to remove an internal node (should not work) + // add n4 back at root->left->right + n4 = t.addRight(4, t.root()->left()); + ASSERT_EQ(true, t.isInternal(n1)) << "n1 still external"; + ASSERT_EQ(3, t.size()) << "size not 3"; + ok = t.removeNode(n1, e); // attempt removing n1 + ASSERT_EQ(false, ok) << "removeNode() deleted internal node"; + + // try to delete root + ok = t.removeNode(t.root(), e); // remove n1 + ASSERT_EQ(false, ok) << "removeNode() deleted root"; + + + } + + void remove(){ + Tree t; + int e; + node_t* n0 = t.addRoot(0); + node_t* n1 = t.addLeft(1, n0); + node_t* n2 = t.addRight(2, n0); + node_t* n3 = t.addLeft(3, n1); + node_t* n4 = t.addRight(4, n1); + + ///////////////// try deleting subtree at internal node + ASSERT_EQ(5, t.size()) << "size not 4"; + + bool ok = t.remove(n1, e); // remove subtree at n1 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(2, t.size()) << "size not 2"; + ASSERT_EQ(1, e) << "wrong element removed"; + ASSERT_EQ(false, t.hasLeft(t.root())) << "root still has left"; + ASSERT_EQ(1, t.numChildren(t.root())) << "root does not have 1 child"; + + // try removing external node + ok = t.remove(n2, e); // remove subtree at n2 (single node) + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(1, t.size()) << "size not 2"; + ASSERT_EQ(2, e) << "wrong element removed"; + ASSERT_EQ(false, t.isInternal(t.root())) << "root still internal"; + ASSERT_EQ(0, t.numChildren(t.root())) << "root does not have any child"; + + // try deleting the entire tree + n1 = t.addLeft(1, t.root()); + n2 = t.addRight(2, t.root()); + n3 = t.addLeft(3, n1); + n4 = t.addRight(4, n1); + node_t* n5 = t.addLeft(5, n2); + + ok = t.remove(t.root(), e); // remove subtree at root (whole tree) + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(0, t.size()) << "size not 0"; + ASSERT_EQ(true, t.empty()) << "tree not empty"; + ASSERT_EQ(0, e) << "wrong element removed"; + ASSERT_EQ(NULL, t.root()) << "root not NULL"; + + } + + void clear(){ + node_t *n0, *n1, *n2, *n3, *n4; + Tree t; + //////// clear + t.clear(); // should have no effect + + n0 = t.addRoot(0); + n1 = t.addLeft(1, t.root()); + n2 = t.addRight(2, t.root()); + n3 = t.addLeft(3, n1); + n4 = t.addRight(4, n1); + + ASSERT_EQ(4, t.size()) << "size not 4"; + ASSERT_EQ(false, t.empty()) << "tree empty"; + ASSERT_NE(NULL, (int)t.root()) << "root NULL"; + + t.clear(); + + ASSERT_EQ(0, t.size()) << "size not 0"; + ASSERT_EQ(true, t.empty()) << "tree not empty"; + ASSERT_EQ(NULL, (int) t.root()) << "root not NULL"; + + } + + void toString(){ + Tree t; + node_t* n0 = t.addRoot(0); + + node_t* n1 = t.addLeft(1, n0); + node_t* n2 = t.addRight(2, n0); + + node_t* n3 = t.addLeft(3, n1); + // no 4 + node_t* n5 = t.addLeft(5, n2); + node_t* n6 = t.addRight(6, n2); + // no 7 + node_t* n8 = t.addRight(8, n3); + /* + 0 + 1 2 + 3 - 5 6 + - 8 + + */ + + const string preorderTrimmed = "0138256"; // preorder, without space + string str = t.toString(); // result, with space + // remove space + string actualTrimmed = strPurge(str, "| "); // purge | and space + ASSERT_EQ(actualTrimmed, preorderTrimmed) << "toString() mismatch"; + + string strBt, strExpected; + /////////// preorder + strBt = t.toStringPreOrder(); + strBt = strPurge(strBt, "| "); + strExpected = "0138256"; + ASSERT_EQ(strExpected, strBt) << "preorder mismatch"; + + /////////// postorder + strBt = t.toStringPostOrder(); + strBt = strPurge(strBt, "| "); + strExpected = "8315620"; + ASSERT_EQ(strExpected, strBt) << "postorder mismatch"; + + /////////// inorder + strBt = t.toStringInOrder(); + strBt = strPurge(strBt, "| "); + strExpected = "3810526"; + ASSERT_EQ(strExpected, strBt) << "inorder mismatch"; + + + /////////// BFSorder + strBt = t.toStringBFS(); + strBt = strPurge(strBt, "| "); + strExpected = "0123568"; + ASSERT_EQ(strExpected, strBt) << "BFS order mismatch"; + } + +}; +ADD_TEST_F(BinaryTreeTest, add); +ADD_TEST_F(BinaryTreeTest, removeNode); +ADD_TEST_F(BinaryTreeTest, remove); +ADD_TEST_F(BinaryTreeTest, toString); + +} // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.binarytreebase.h b/YASI_12/ds.binarytreebase.h new file mode 100644 index 0000000..332889d --- /dev/null +++ b/YASI_12/ds.binarytreebase.h @@ -0,0 +1,287 @@ +#pragma once +#include "common.h" +#include "ds.doublylinkedlist.h" +#include +using namespace std; + +namespace yasi{ + namespace ds{ + // interface for binary tree + template + class IBinaryTree :public virtual ITree{ + public: + //virtual + virtual ~IBinaryTree(){} + virtual Node* left(const Node*) const = 0; + virtual Node* right(const Node*) const = 0; + virtual Node* parent(const Node*) const = 0; + virtual Node* sibling(const Node*) const = 0; + virtual bool hasLeft(const Node*) const = 0; + virtual bool hasRight(const Node*) const = 0; + virtual bool isLeft(const Node*) const = 0; + virtual bool isRight(const Node*) const = 0; + virtual Node* addLeft(const E&, Node*) = 0; + virtual Node* addRight(const E&, Node*) = 0; + virtual bool nodeNotNull(const Node*) const = 0; + bool nodeNull(const Node* pNode) const { return !nodeNotNull(pNode); } + }; + +// pre-declaration + template< class E, class Node> + class InorderBinaryTreeIterator; + + ////////////////////////////////////////// + //// class BinaryTreeBase + //// Extended by: Array based/Node based binary trees + template + class BinaryTreeBase : public virtual IBinaryTree{ + + protected: + typedef Node node_t; + public: + typedef InorderBinaryTreeIterator inorderIterator; + + // not efficient (todo) + inorderIterator inorderBegin() const{ + return iterator(this, leftMostChild(root())); + } + // not efficient (todo) + inorderIterator inorderEnd() const{ + return iterator(this, rightMostChild(root)); + } + + virtual ~BinaryTreeBase(){} + BinaryTreeBase(){} + + //////////// partial IBinaryTree interface methods ////////// + bool isInternal(const node_t* pNode) const override { return nodeNotNull(pNode) && (hasLeft(pNode) || hasRight(pNode)); } + bool isExternal(const node_t* pNode) const override { return nodeNotNull(pNode) && !isInternal(pNode); } + int numChildren(const node_t* pNode) const override { + int n = 0; + if (nodeNotNull(pNode)){ + if (hasLeft(pNode)) n++; + if (hasRight(pNode)) n++; + } + return n; + } + node_t* sibling(const node_t* pNode)const override { + if (nodeNotNull(pNode)){ + if (isLeft(pNode)){ + return right(parent(pNode)); + } + else if (isRight(pNode)){ + return left(parent(pNode)); + } + else{ + return NULL; // root has no sibling + } + } + else{ + return NULL; + } + } + bool hasLeft(const node_t* pNode) const override { + return nodeNotNull(pNode) && nodeNotNull(left(pNode)); + } + bool hasRight(const node_t* pNode) const override { + return nodeNotNull(pNode) && nodeNotNull(right(pNode)); + } + bool isLeft(const node_t* pNode) const override{ + node_t* pParent = parent(pNode); + if (nodeNotNull(pParent) && + hasLeft(pParent) && + pNode == left(pParent)){ + return true; + } + else + return false; + } + bool isRight(const node_t* pNode) const override{ + node_t* pParent = parent(pNode); + if (nodeNotNull(pParent) && + hasRight(pParent) && + pNode == right(pParent)){ + return true; + } + else + return false; + } + + ////////////////////// some helper methods ///////////// + node_t* leftMostChild(node_t* pNode) const{ + if (nodeNull(pNode)) return NULL; + while (left(pNode) != NULL){ + pNode = left(pNode); + } + return pNode; + + } + node_t* rightMostChild(node_t* pNode) const{ + if (nodeNull(pNode)) return NULL; + while (right(pNode) != NULL){ + pNode = right(pNode); + } + return pNode; + + } + + // returns the leftmost node in the right subtree + node_t* inorderSuccessor(const node_t* pNode) const{ + if (nodeNull(pNode)) return NULL; + + if (right(pNode) == NULL){ + // right is NULL + + node_t* pParent = parent(pNode); + if (isLeft(pNode)){ + // insucc(left-child) = parent + return pParent; + } + else{ + // insucc(right-child) = ancestor that is a left child + while (isRight(pParent)){ + pParent = parent(pParent); + } + if (pParent != root()){ + return parent(pParent); + } + else{ + // last node in inorder traversal + return NULL; + } + } + } + else{ + // right is not NULL + if (left(right(pNode)) == NULL) + return right(pNode); // right->left = NULL + else + return leftMostChild(right(pNode)); + } + } + + // the preorder traversal + string toStringPreOrder(const node_t* pNode) const { + if (nodeNotNull(pNode)){ + string strLeft, strRight; + stringstream buf; + + buf << pNode->element << "|"; // print element + + strLeft = toStringPreOrder(left(pNode)); // print left + buf << strLeft; + + + strRight = toStringPreOrder(this->right(pNode)); // print right + buf << strRight; + + return buf.str(); + } + else{ + return " |"; + } + } + + // the postorder traversal + string toStringPostOrder(const node_t* pNode) const { + if (nodeNotNull(pNode)){ + string strLeft, strRight; + stringstream buf; + + strLeft = toStringPostOrder(left(pNode)); // print left + buf << strLeft; + + + strRight = toStringPostOrder(this->right(pNode)); // print right + buf << strRight; + + buf << pNode->element << "|"; // print element + + return buf.str(); + } + else{ + return " |"; + } + } + + // the inorder traversal + string toStringInOrder(const node_t* pNode) const { + if (nodeNotNull(pNode)){ + string strLeft, strRight; + stringstream buf; + + strLeft = toStringInOrder(left(pNode)); // print left + buf << strLeft; + + + buf << pNode->element << "|"; // print element + + + strRight = toStringInOrder(this->right(pNode)); // print right + buf << strRight; + + return buf.str(); + } + else{ + return " |"; + } + } + + // returns the preorder traversal of the tree + string toStringPreOrder() const { return toStringPreOrder(root()); } + // returns the postorder traversal of the tree + string toStringPostOrder() const { return toStringPostOrder(root()); } + // returns the intorder traversal of the tree + string toStringInOrder() const { return toStringInOrder(root()); } + // returns the BFS traversal of the tree + string toStringBFS() const{ + stringstream buf; + DoublyLinkedList q; + q.pushBack(root()); + while (!q.empty()){ + string str = q.toString(); + // visit node + node_t* pNode = *q.begin(); // look up the earliest candidate + q.popFront(); // remove from the list + buf << pNode->element << "|"; + // push next-level elements (children) onto stack + if (hasLeft(pNode)) q.pushBack(left(pNode)); + if (hasRight(pNode)) q.pushBack(right(pNode)); + } + // done + return buf.str(); + } + + + // returns the preorder traversal of the tree + string toString() const override { + return toStringPreOrder(root()); + } + + + void clear() override{ + remove(root()); + } + }; + + + // inorder iterator + template< class E, class Node> + class InorderBinaryTreeIterator : public NodeIterator{ + typedef InorderBinaryTreeIterator self; + typedef NodeIterator base; + protected: + BinaryTreeBase* t; + public: + virtual ~InorderBinaryTreeIterator(){ } + //InorderBinaryTreeIterator() {} + 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; } + }; + + } // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.comparable.h b/YASI_12/ds.comparable.h new file mode 100644 index 0000000..5b7cc0a --- /dev/null +++ b/YASI_12/ds.comparable.h @@ -0,0 +1,25 @@ +#pragma once +#include "common.h" +namespace yasi{ + namespace ds{ + // interface IComparable + // overloads operators + //class IComparable{ + //public: + // virtual ~IComparable(){} + // virtual bool operator < (const IComparable&) const = 0; + // virtual bool operator <= (const IComparable&) const = 0; + // virtual bool operator == (const IComparable&) const = 0; + //}; + + template + class IComparable{ + public: + virtual ~IComparable(){} + virtual bool operator < (const _Self&) const = 0; + virtual bool operator <= (const _Self&) const = 0; + virtual bool operator == (const _Self&) const = 0; + }; + + } +} \ No newline at end of file diff --git a/YASI_12/ds.dictionary.h b/YASI_12/ds.dictionary.h new file mode 100644 index 0000000..7caa81c --- /dev/null +++ b/YASI_12/ds.dictionary.h @@ -0,0 +1,27 @@ +#pragma once +#include "common.h" +#include "gtest\gtest.h" +#include "ds.kvpair.h" +#include "ds.comparable.h" +using namespace std; + +namespace yasi{ + namespace ds{ + + + // the dictionary interface + template + class IDictionary{ + public: + virtual ~IDictionary(){} + virtual Value* find(Key) const = 0; + virtual void remove(Key) = 0; + // re-insertion will update current value + virtual void insert(Key, Value) = 0; + virtual int size() const = 0; + virtual bool empty() const = 0; + virtual void clear() = 0; + }; + + } // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.doublylinkedlist.h b/YASI_12/ds.doublylinkedlist.h new file mode 100644 index 0000000..6e8c27f --- /dev/null +++ b/YASI_12/ds.doublylinkedlist.h @@ -0,0 +1,1012 @@ +// doubly linked list +#pragma once +#include "common.h" +#include "ds.singlylinkedlist.h" +#include +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 > + class IDoublyLinkedList : public virtual ISinglyLinkedList < E, Node, Iter > + { + public: + virtual ~IDoublyLinkedList(){}; + virtual Node* tail() const = 0; + virtual Node* addBack(const E&) = 0; + virtual void pushFront(const E&) = 0; + virtual void pushBack(const E&) = 0; + virtual void popFront() = 0; + virtual void popBack() = 0; + virtual const E& back() const = 0; + }; + + // doubly linked list iterator + template + class DLLIterator : public SLLIterator< E, Node > { + typedef DLLIterator self; + typedef SLLIterator 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; } + }; + + // doubly linked list class + template, class Iter = DLLIterator > + class DoublyLinkedList :public virtual IDoublyLinkedList < E, Node, Iter >, // implements interface + public virtual SinglyLinkedList { // inherits singly-linked list + ////////// enable testing //////////////// + friend class DoublyLinkedListTest; + typedef SinglyLinkedList base; + + protected: + // typedefs + typedef Node node_t; + + // new members + //node_t* pTail; + + // make the list circular, so that we can make the iterator end() + // tail-->horizon-->head + + + node_t* before(node_t* pNode) override{ + if (pNode) return pNode->prev(); + else return NULL; + } + virtual node_t* tail()const override{ return (_size == 0) ? NULL : end().pNode->prev(); } + + public: + typedef DLLIterator iterator; + /////// constructor/destructor /////////// + DoublyLinkedList(){ + // make a unit circle: horizon--horizon--horizon + pHorizon->setPrev(pHorizon); + } + DoublyLinkedList(const E* srcArr, const int numElems) : DoublyLinkedList() { + fromArray(srcArr, numElems); + } + ~DoublyLinkedList(){ + this->clear(); + } + ///////////////////////////////////////////////////////////// + //////////// Implement IDoubleLinkedList methods //////////// + ///////////////////////////////////////////////////////////// + // Inherited from SinglyLinkedList + // Node* head() = 0; + + // Inherited from SinglyLinkedList + // int size() const = 0; + + // Inherited from SinglyLinkedList + // bool elements(E**, int&) = 0; + + // Inherited from SinglyLinkedList + // void toString(string&) = 0; + //////////////////////////////////////// + /////////// IList methods ////////////// + //////////////////////////////////////// + void pushFront(const E& e) override{ + this->addFront(e); + } + void pushBack(const E& e) override{ + this->addBack(e); + } + void popFront() override{ + this->remove(head()); + } + void popBack() override{ + this->remove(tail()); + } + inline const E& back() const override{ + if (size() > 0) + return tail()->element; + else + return _defaultElem; + } + + ////////////// iterators ///////////// + // need to redefine because DLLIterator + // is different from SLLIterator + + virtual bool empty() const{ return (begin() == end()) && _size == 0; } + virtual node_t* addFront(const E& e) { + return insert(begin(), e); + //node_t* pNode = new node_t(e, pHead, NULL); // pNode->next = pHead + //if (pHead != NULL) pHead->setPrev(pNode); + //pHead = pNode; + //if (pTail == NULL) pTail = pNode; + //_size++; + //return pNode; + } + virtual node_t* addBack(const E& e){ + return insert(end(), e); + //node_t* pNode = new node_t(e, NULL, pTail); // pNode->prev = pTail + //if (pTail != NULL) pTail->setNext(pNode); + //pTail = pNode; + //if(pHead == NULL) pHead = pNode; + //_size++; + //return pNode; + } + virtual node_t* addBefore(const E& e, node_t* pAfter) { + if (pAfter) + return insert(pAfter, e); + else + return NULL; + + //if (pAfter == NULL) return NULL; + + //if (pAfter == pHead) + // return addFront(e); // add before head + + //node_t* pBefore = before(pAfter); + //if (pBefore){ + // node_t* pNode = new node_t(e, pAfter, pBefore); // dynamic memory allocation + // pBefore->setNext(pNode); + // pAfter->setPrev(pNode); + // _size++; + // return pNode; + //} + //else return NULL; + } + virtual node_t* addAfter(const E& e, node_t* pBefore){ + if (pBefore) + return insert(pBefore->next(), e); + else + return NULL; + + //if (pBefore == NULL) return NULL; + + //if (pBefore == pTail) + // return addBack(e); // add after tail + + //node_t* pAfter = pBefore->next(); + //if (pAfter){ + // node_t* pNode = new node_t(e, pAfter, pBefore); // dynamic memory allocation + // pBefore->setNext(pNode); + // pAfter->setPrev(pNode); + // _size++; + // return pNode; + //} + //else return NULL; + } + + // inserts the element e at the specified position + node_t* insert(iterator here, const E& e = E()){ + // before--here.............. would become + // before--newnode--here + node_t* pBefore = here.pNode->prev(); + node_t* pHere = here.pNode; + node_t* pNewNode = new node_t(e, pHere, pBefore); // before<--newnode-->here + pBefore->setNext(pNewNode); // before-->newnode + pHere->setPrev(pNewNode); // newnode<--here + + _size++; + + // finally + return pNewNode; + } + + virtual bool remove(node_t* pNode, E& e) { + // cannot remove the pHorizon or the NULL node + if (pNode == NULL || pNode == pHorizon) return false; + + e = pNode->element; + // removing from the middle + node_t* pAfter = pNode->next(); + node_t* pBefore = pNode->prev(); + // pBefore and pAfter must be non-null + if (pBefore && pAfter){ + pBefore->setNext(pAfter); + pAfter->setPrev(pBefore); + _size--; + DELETE_SAFE(pNode); // free memory + return true; + } + else{ + // something is wrong; pNode may not be in the list + return false; + } + } + virtual bool remove(node_t* pNode) { + E e; + return remove(pNode, e); + } + virtual void clear(){ + for (iterator n = begin(); n != end(); ){ + node_t* pNode = (n++).pNode; + DELETE_SAFE(pNode); + } + _size = 0; + pHorizon->setNext(pHorizon); + pHorizon->setPrev(pHorizon); + } + }; + + // test + class DoublyLinkedListTest : public yasi::Test { + typedef DoublyLinkedList >::node_t node_t; + typedef DoublyLinkedList > IntList; + typedef IntList::iterator IntIterator; + protected: + template + void checkListElements(DoublyLinkedList >* pList, const E* srcArr, const int n){ + E* pElems = new E[n]; + int numElems; + DoublyLinkedList >& list = *pList; + list.elements(&pElems); + ASSERT_EQ(true, arrcmp(srcArr, pElems, n)) << "arrays are not the same" << endl + << "actual: " << arrToString(srcArr, n) << endl + << "expected: " << arrToString(pElems, n); + DELETE_ARR_SAFE(pElems); + } + public: + virtual void fromArray(){ + IntList list; + int srcArr[] = { 10, 20, 30, 40 }; + int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + ASSERT_NE(0, (int)list.head()) << "head is NULL"; + ASSERT_EQ(4, (int)list.size()) << "size is not 4"; + int j = n - 1; + + for (IntIterator it = list.begin(); it != list.end(); it++){ + ASSERT_EQ(srcArr[j], *it) << "element mismatch at item " << n - j - 1 << " in list"; + j--; + } + } + virtual void elements(){ + IntList list; + int srcArr[] = { 10, 20, 30, 40 }; + int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + //ASSERT_NE(0, (int)list.head()) << "list head is NULL"; + ASSERT_EQ(n, list.size()) << "list size should have been " << n; + // since the elements are given in reverse order + int* pRev = new int[n]; + arrrev(srcArr, n, &pRev); + ASSERT_NE(0, (int)pRev) << "array reverse failed"; + { + SCOPED_TRACE("elements"); + checkListElements(&list, pRev, n); + } + DELETE_ARR_SAFE(pRev); + } + + void relements(){ + IntList list; + int srcArr[] = { 10, 20, 30, 40 }; + const int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + ASSERT_EQ(n, list.size()) << "list size should have been " << n; + + int* pRelems = new int[n]; + list.relements(&pRelems); + // pRelems should be in the same order as srcArr + ASSERT_EQ(true, arrcmp(srcArr, pRelems, n)) << "arrays are not the same" << endl + << "actual: " << arrToString(pRelems, n) << endl + << "expected: " << arrToString(srcArr, n); + DELETE_ARR_SAFE(pRelems); + } + + void toString(){ + int srcArr[] = { 1, 2, 3, 4 }; + const string expected = "4->3->2->1->"; + IntList list(srcArr, ARR_LENGTH(srcArr)); + string actual = list.toString(); + ASSERT_EQ(expected, actual) << "toString() is wrong"; + } + + void addFront(){ + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + node_t *n1, *n2, *n3; + + // add head + { + SCOPED_TRACE("add #1"); + n1 = list.addFront(4); // new node is at head + // list: 4 + ASSERT_NE(0, (int)list.head()) << "head is NULL"; + ASSERT_NE(0, (int)list.tail()) << "tail is NULL"; + ASSERT_EQ(list.head(), list.tail()) << "head != tail"; + + ASSERT_EQ(list.head(), n1) << "addFront() does not return correct node"; + ASSERT_NE(0, (int)n1) << "head is NULL although list has items"; + ASSERT_EQ(list.end().pNode, n1->next()) << "head's next should be end()"; + ASSERT_EQ(list.end().pNode, n1->prev()) << "head's prev should be end()"; + ASSERT_EQ(1, list.size()) << "size should have been 1"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + ASSERT_EQ(4, n1->element) << "head-element should have been 4"; + + // iterators + ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + ASSERT_EQ(list.tail(), list.begin().pNode) << "tail() not equal to begin().pNode"; + ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + ASSERT_EQ(list.end(), ++list.begin()) << "++begin() and end() not same"; + ASSERT_EQ(list.begin(), --list.end()) << "--end() and begin() not same"; + ASSERT_NE(list.end(), list.begin()++) << "begin()++ and end() same"; + ASSERT_NE(list.begin(), list.end()--) << "end()-- and begin() same"; + ASSERT_EQ(list.head(), (list.begin()++).pNode) << "begin()++.pNode not head()"; + ASSERT_EQ(list.tail(), (--list.end()).pNode) << "--end().pNode not tail()"; + ASSERT_EQ(list.end().pNode, (++list.begin()).pNode) << "begin++.pNode should be end()"; + ASSERT_EQ(list.begin().pNode, (--list.end()).pNode) << "--end.pNode should be begin()"; + ASSERT_EQ(4, *list.begin()) << "begin()-element should have been 4"; + ASSERT_EQ(4, *(--list.end())) << "--end()-element should have been 4"; + } + + // add 2nd element + { + SCOPED_TRACE("add #2"); + n2 = list.addFront(5); // new node is at head + + // list: 5->4 + ASSERT_NE(0, (int)list.head()) << "head is NULL although list has items"; + ASSERT_EQ(list.head(), n2) << "n2 should be head"; + ASSERT_EQ(list.tail(), n1) << "n1 should be tail"; + + ASSERT_NE(list.end().pNode, list.head()->next()) << "head->next is end() although list has 2 items"; + ASSERT_EQ(n1, list.head()->next()) << "head->next hsould be n1"; + ASSERT_EQ(list.head(), n1->prev()) << "n1->prev should be head"; + + ASSERT_EQ(list.end().pNode, list.tail()->next()) << "tail->next should be end()"; + ASSERT_EQ(list.end().pNode, n1->prev()->prev()) << "n1->perv->prev should be end()"; + ASSERT_EQ(5, list.head()->element) << "head element should have been 5"; + ASSERT_EQ(2, list.size()) << "size should have been 2"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + + ASSERT_EQ(5, list.head()->element) << "head-element should have been 5"; + ASSERT_EQ(4, list.head()->next()->element) << "head->next element should have been 4"; + + + // iterators + ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + ASSERT_EQ(list.tail(), (--(list.end())).pNode) << "tail() not equal to --end().pNode"; + ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + ASSERT_EQ(list.end(), ++(++(list.begin()))) << "++++begin() not end()"; + ASSERT_EQ(list.begin(), --(--(list.end()))) << "----end() not begin()"; + ASSERT_EQ(n2, (list.begin()++).pNode) << "begin++.pNode should be n2"; + ASSERT_EQ(n1, (++(list.begin())).pNode) << "++begin.pNode should be n1"; + ASSERT_EQ(n2, (--(--list.end())).pNode) << "----end.pNode should be n2"; + ASSERT_EQ(n1, (--(list.end())).pNode) << "--end.pNode should be n1"; + ASSERT_EQ(5, *(list.begin())) << "begin()-element should have been 5"; + ASSERT_EQ(4, *(++(list.begin()))) << "++begin()-element should have been 4"; + ASSERT_EQ(5, *(list.begin()++)) << "begin()-element should have been 5"; + ASSERT_EQ(4, *(--list.end())) << "--end()-element should have been 4"; + ASSERT_EQ(5, *(--(--list.end()))) << "----end()-element should have been 5"; + } + + // add more elements + { + SCOPED_TRACE("add #3"); + n3 = list.addFront(6); // new node is at head + + // list: 6->5->4 + ASSERT_NE(0, (int)list.head()) << "head is NULL although list has items"; + ASSERT_NE(0, (int)list.head()->next()) << "head->next is NULL"; + ASSERT_NE(0, (int)list.head()->next()->next()) << "head->next->next is NULL"; + + ASSERT_EQ(n3, list.head()) << "n3 should be head"; + ASSERT_EQ(n1, list.tail()) << "n1 should be tail"; + ASSERT_EQ(n2, list.head()->next()) << "n2 should be head->next"; + ASSERT_EQ(n2, list.tail()->prev()) << "n2 should be tail->prev"; + ASSERT_EQ(n3, n2->prev()) << "n3 should be n2->prev"; + ASSERT_EQ(list.tail(), n2->next()) << "n2->next should be tail"; + ASSERT_EQ(list.end().pNode, list.tail()->next()) << "tail->next should be end()"; + ASSERT_EQ(list.end().pNode, list.head()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(6, list.head()->element) << "head element should have been 6"; + ASSERT_EQ(5, list.head()->next()->element) << "head->next element should have been 5"; + ASSERT_EQ(4, list.head()->next()->next()->element) << "head->next->next element should have been 4"; + ASSERT_EQ(3, list.size()) << "size should have been 3"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + + // iterators + ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + ASSERT_EQ(n3, (list.begin()).pNode) << "begin.pNode should be n3"; + ASSERT_EQ(n3, ((list.begin())++).pNode) << "begin++.pNode should be n3"; + ASSERT_EQ(n3, ((list.begin())--).pNode) << "begin--.pNode should be n3"; + ASSERT_EQ(n2, (++(list.begin())).pNode) << "++begin.pNode should be n2"; + ASSERT_EQ(n1, (++(++(list.begin()))).pNode) << "++++begin.pNode should be pNode"; + ASSERT_EQ(list.end(), (++(++(++list.begin())))) << "++++++begin() not end()"; + + ASSERT_EQ(n3, (--(--(--list.end()))).pNode) << "------end.pNode should be n3"; + ASSERT_EQ(n2, (--(--list.end())).pNode) << "----end.pNode should be n2"; + ASSERT_EQ(n1, (--list.end()).pNode) << "--end.pNode should be pNode"; + ASSERT_EQ(list.begin(), --(--(--list.end()))) << "------end should be begin"; + ASSERT_EQ(6, *(list.begin())) << "begin()-element should have been 6"; + ASSERT_EQ(6, *(list.begin()++)) << "begin()++-element should have been 6"; + ASSERT_EQ(6, *(list.begin()--)) << "begin()-- element should have been 6"; + ASSERT_EQ(5, *(++(list.begin()))) << "++begin()-element should have been 5"; + ASSERT_EQ(4, *(++(++(list.begin())))) << "++++begin()-element should have been 4"; + ASSERT_EQ(4, *(--list.end())) << "--end()-element should have been 4"; + ASSERT_EQ(5, *(--(--list.end()))) << "----end()-element should have been 5"; + ASSERT_EQ(6, *(--(--(--list.end())))) << "------end()-element should have been 6"; + } + + } + + void addBack(){ + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + ASSERT_EQ(0, (int)list.tail()) << "head should be NULL"; + + node_t *n1, *n2, *n3; + + // add head + { + SCOPED_TRACE("add #1"); + n1 = list.addBack(4); // new node is at head + // list: 4 + ASSERT_NE(0, (int)list.head()) << "head is NULL"; + ASSERT_NE(0, (int)list.tail()) << "tail is NULL"; + ASSERT_EQ(list.head(), list.tail()) << "head != tail"; + + ASSERT_EQ(list.head(), n1) << "head() not n1"; + ASSERT_EQ(list.tail(), n1) << "tail() not n1"; + ASSERT_NE(0, (int)n1) << "head is NULL although list has items"; + ASSERT_EQ(list.end().pNode, n1->next()) << "head's next should be end()"; + ASSERT_EQ(list.end().pNode, n1->prev()) << "head's prev should be end()"; + ASSERT_EQ(1, list.size()) << "size should have been 1"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + ASSERT_EQ(4, n1->element) << "head-element should have been 4"; + + // iterators + ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + ASSERT_EQ(list.tail(), list.begin().pNode) << "tail() not equal to begin().pNode"; + ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + ASSERT_EQ(list.end(), ++list.begin()) << "++begin() and end() not same"; + ASSERT_EQ(list.begin(), --list.end()) << "--end() and begin() not same"; + ASSERT_NE(list.end(), list.begin()++) << "begin()++ and end() same"; + ASSERT_NE(list.begin(), list.end()--) << "end()-- and begin() same"; + ASSERT_EQ(list.head(), (list.begin()++).pNode) << "begin()++.pNode not head()"; + ASSERT_EQ(list.tail(), (--list.end()).pNode) << "--end().pNode not tail()"; + ASSERT_EQ(list.end().pNode, (++list.begin()).pNode) << "begin++.pNode should be end()"; + ASSERT_EQ(list.begin().pNode, (--list.end()).pNode) << "--end.pNode should be begin()"; + ASSERT_EQ(4, *list.begin()) << "begin()-element should have been 4"; + ASSERT_EQ(4, *(--list.end())) << "--end()-element should have been 4"; + } + + // add 2nd element + { + SCOPED_TRACE("add #2"); + n2 = list.addBack(5); // new node is at head + + // list: 4->5 + ASSERT_NE(0, (int)list.head()) << "head is NULL although list has items"; + ASSERT_EQ(list.head(), n1) << "n1 should be head"; + ASSERT_EQ(list.tail(), n2) << "n2 should be tail"; + ASSERT_EQ(list.tail(), list.head()->next()) << "head->next should be tail"; + + + ASSERT_NE(list.end().pNode, list.head()->next()) << "head->next is end()"; + ASSERT_EQ(n2, list.head()->next()) << "head->next should be n2"; + ASSERT_EQ(list.head(), n2->prev()) << "n2->prev should be head"; + + ASSERT_EQ(list.end().pNode, list.tail()->next()) << "tail->next should be end()"; + ASSERT_EQ(list.end().pNode, n2->prev()->prev()) << "n2->perv->prev should be end()"; + ASSERT_EQ(4, list.head()->element) << "head element should have been 4"; + ASSERT_EQ(5, list.tail()->element) << "tail element should have been 5"; + ASSERT_EQ(2, list.size()) << "size should have been 2"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + + // iterators + ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + ASSERT_EQ(list.tail(), (--(list.end())).pNode) << "tail() not equal to --end().pNode"; + ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + ASSERT_EQ(list.end(), ++(++(list.begin()))) << "++++begin() not end()"; + ASSERT_EQ(list.begin(), --(--(list.end()))) << "----end() not begin()"; + ASSERT_EQ(n1, (list.begin()++).pNode) << "begin++.pNode should be n1"; + ASSERT_EQ(n2, (++(list.begin())).pNode) << "++begin.pNode should be n2"; + ASSERT_EQ(n1, (--(--list.end())).pNode) << "----end.pNode should be n1"; + ASSERT_EQ(n2, (--(list.end())).pNode) << "--end.pNode should be n2"; + ASSERT_EQ(4, *(list.begin())) << "begin()-element should have been 4"; + ASSERT_EQ(5, *(++(list.begin()))) << "++begin() element should have been 5"; + ASSERT_EQ(4, *(list.begin()++)) << "begin()++ element should have been 4"; + ASSERT_EQ(5, *(--list.end())) << "--end()-element should have been 5"; + ASSERT_EQ(4, *(--(--list.end()))) << "----end()-element should have been 4"; + } + + // add more elements + { + SCOPED_TRACE("add #3"); + n3 = list.addBack(6); // new node is at head + + // list: 4->5->6 + ASSERT_NE(0, (int)list.head()) << "head is NULL although list has items"; + ASSERT_NE(list.end().pNode, list.head()->next()) << "head->next is end()"; + ASSERT_NE(list.end().pNode, list.head()->next()->next()) << "head->next->next is end()"; + + ASSERT_EQ(n3, list.tail()) << "n3 should be tail"; + ASSERT_EQ(n1, list.head()) << "n1 should be head"; + ASSERT_EQ(n2, list.head()->next()) << "n2 should be head->next"; + ASSERT_EQ(n2, list.tail()->prev()) << "n2 should be tail->prev"; + ASSERT_EQ(n3, n2->next()) << "n3 should be n2->next"; + ASSERT_EQ(list.head(), n2->prev()) << "n2->prev should be head"; + ASSERT_EQ(list.end().pNode, list.tail()->next()) << "tail->next should be end()"; + ASSERT_EQ(list.end().pNode, list.head()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(4, list.head()->element) << "head element should have been 4"; + ASSERT_EQ(5, list.head()->next()->element) << "head->next element should have been 5"; + ASSERT_EQ(6, list.head()->next()->next()->element) << "head->next->next element should have been 6"; + ASSERT_EQ(3, list.size()) << "size should have been 3"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + + // iterators + ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + ASSERT_EQ(n1, (list.begin()).pNode) << "begin.pNode should be n1"; + ASSERT_EQ(n1, ((list.begin())++).pNode) << "begin++.pNode should be n1"; + ASSERT_EQ(n1, ((list.begin())--).pNode) << "begin--.pNode should be n1"; + ASSERT_EQ(n2, (++(list.begin())).pNode) << "++begin.pNode should be n2"; + ASSERT_EQ(n3, (++(++(list.begin()))).pNode) << "++++begin.pNode should be n3"; + ASSERT_EQ(list.end(), (++(++(++list.begin())))) << "++++++begin() not end()"; + + ASSERT_EQ(n1, (--(--(--list.end()))).pNode) << "------end.pNode should be n1"; + ASSERT_EQ(n2, (--(--list.end())).pNode) << "----end.pNode should be n2"; + ASSERT_EQ(n3, (--list.end()).pNode) << "--end.pNode should be n3"; + ASSERT_EQ(list.begin(), --(--(--list.end()))) << "------end should be begin"; + ASSERT_EQ(4, *(list.begin())) << "begin()-element should have been 4"; + ASSERT_EQ(4, *(list.begin()++)) << "begin()++-element should have been 4"; + ASSERT_EQ(4, *(list.begin()--)) << "begin()-- element should have been 4"; + ASSERT_EQ(5, *(++(list.begin()))) << "++begin()-element should have been 5"; + ASSERT_EQ(6, *(++(++(list.begin())))) << "++++begin()-element should have been 6"; + ASSERT_EQ(6, *(--list.end())) << "--end()-element should have been 6"; + ASSERT_EQ(5, *(--(--list.end()))) << "----end()-element should have been 5"; + ASSERT_EQ(4, *(--(--(--list.end())))) << "------end()-element should have been 4"; + } + + } + + void insert(){ + IntList list; + node_t* n[] = { + list.addBack(0), + list.addBack(1), + list.addBack(2), + list.addBack(3) + }; + // list: 0--1--2--3 + + // insert before 0 + { + SCOPED_TRACE("insert before 0"); + node_t* n_1 = list.insert(n[0], -1); // insert -1 before 0 + ASSERT_EQ(5, list.size()) << "size not 5"; + ASSERT_EQ(-1, *list.begin()) << "begin() element not -1"; + ASSERT_EQ(n_1, list.begin().pNode) << "begin() node not n_1"; + } + // list: (-1)--0--1--2--3 + + // insert after 3 + { + SCOPED_TRACE("insert after 3"); + node_t* n4 = list.insert(n[3]->next(), 4); // insert 4 after 3 + ASSERT_EQ(6, list.size()) << "size not 6"; + ASSERT_EQ(4, *--list.end()) << "--end() element not 4"; + ASSERT_EQ(n4, (--list.end()).pNode) << "--end() node not n4"; + } + // list: (-1)--0--1--2--3--4 + + // insert before 2 + { + SCOPED_TRACE("insert before 2"); + node_t* n10 = list.insert(n[2], 10); // insert 10 before 2 + ASSERT_EQ(7, list.size()) << "size not 7"; + ASSERT_EQ(10, * ++ ++ ++ list.begin()) << "n10 element not 10 from begin"; + ASSERT_EQ(n10, (-- -- -- -- list.end()).pNode) << "node not n10 from end"; + } + // list: (-1)--0--1--10--2--3--4 + + } + + void addBefore(){ + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + // add before NULL + node_t* pNode = list.addBefore(5, NULL); // should fail + ASSERT_EQ(NULL, pNode) << "added before NULL"; + + // one-item list + node_t* n0 = list.addFront(0); + ASSERT_EQ(0, n0->element) << "wrong element"; + node_t* n1 = list.addBefore(1, n0); + // list: 1->0 + ASSERT_EQ(n1, list.head()) << "n1 should be head"; + ASSERT_EQ(n0, list.tail()) << "n0 should be tail"; + ASSERT_EQ(1, list.head()->element) << "wrong head element"; + ASSERT_EQ(0, list.tail()->element) << "wrong tail element"; + ASSERT_EQ(n0, n1->next()) << "n1->next should be n0"; + ASSERT_EQ(n1, n0->prev()) << "n0->prev should be n1"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + ASSERT_EQ(list.end().pNode, n1->prev()) << "n1->prev should be end()"; + ASSERT_EQ(list.end().pNode, n0->next()) << "n0->next should be end()"; + + // add more items + const int testSize = 5; + node_t* n[testSize]; + n[0] = n0; + n[1] = n1; + for (int i = 2; i < testSize; i++){ + n[i] = list.addBefore(i, n[i - 1]); + } + // list: 4->3->2->1->0 + ASSERT_EQ(5, list.size()) << "size should be 5"; + ASSERT_EQ(n[4], list.head()) << "4 should be at head"; + ASSERT_EQ(n[3], list.head()->next()) << "3 should be 4->next"; + ASSERT_EQ(n[2], list.head()->next()->next()) << "2 should be 3->next"; + ASSERT_EQ(n[1], list.head()->next()->next()->next()) << "1 should be 2->next"; + ASSERT_EQ(n[0], list.head()->next()->next()->next()->next()) << "0 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(n[0], list.tail()) << "0 should be at tail"; + ASSERT_EQ(n[1], list.tail()->prev()) << "1 should be 0->prev"; + ASSERT_EQ(n[2], list.tail()->prev()->prev()) << "2 should be 1->prev"; + ASSERT_EQ(n[3], list.tail()->prev()->prev()->prev()) << "3 should be 2->prev"; + ASSERT_EQ(n[4], list.tail()->prev()->prev()->prev()->prev()) << "4 should be 3->prev"; + ASSERT_EQ(list.end().pNode, list.tail()->prev()->prev()->prev()->prev()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(4, list.head()->element) << "4 should be at head"; + ASSERT_EQ(3, list.head()->next()->element) << "3 should be 4->next"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "2 should be 3->next"; + ASSERT_EQ(1, list.head()->next()->next()->next()->element) << "1 should be 2->next"; + ASSERT_EQ(0, list.head()->next()->next()->next()->next()->element) << "0 should be 1->next"; + + // add in the middle + node_t* n10 = list.addBefore(10, n[2]); + // list: 4->3->10->2->1->0 + ASSERT_EQ(6, list.size()) << "size should be 6"; + ASSERT_EQ(n[4], list.head()) << "4 should be at head"; + ASSERT_EQ(n[3], list.head()->next()) << "3 should be 4->next"; + ASSERT_EQ(n10, list.head()->next()->next()) << "10 should be 3->next"; + ASSERT_EQ(n[2], list.head()->next()->next()->next()) << "2 should be 10->next"; + ASSERT_EQ(n[1], list.head()->next()->next()->next()->next()) << "1 should be 10->next"; + ASSERT_EQ(n[0], list.head()->next()->next()->next()->next()->next()) << "0 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(n[0], list.tail()) << "0 should be at tail"; + ASSERT_EQ(n[1], list.tail()->prev()) << "1 should be 0->prev"; + ASSERT_EQ(n[2], list.tail()->prev()->prev()) << "2 should be 1->prev"; + ASSERT_EQ(n10, list.tail()->prev()->prev()->prev()) << "10 should be 2->prev"; + ASSERT_EQ(n[3], list.tail()->prev()->prev()->prev()->prev()) << "3 should be 10->prev"; + ASSERT_EQ(n[4], list.tail()->prev()->prev()->prev()->prev()->prev()) << "4 should be 3->prev"; + ASSERT_EQ(list.end().pNode, list.tail()->prev()->prev()->prev()->prev()->prev()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(4, list.head()->element) << "4 should be at head"; + ASSERT_EQ(3, list.head()->next()->element) << "3 should be 4->next"; + ASSERT_EQ(10, list.head()->next()->next()->element) << "10 should be 3->next"; + ASSERT_EQ(2, list.head()->next()->next()->next()->element) << "2 should be 10->next"; + ASSERT_EQ(1, list.head()->next()->next()->next()->next()->element) << "1 should be 10->next"; + ASSERT_EQ(0, list.head()->next()->next()->next()->next()->next()->element) << "0 should be 1->next"; + + } + void addAfter(){ + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + // add before NULL + node_t* pNode = list.addBefore(5, NULL); // should fail + ASSERT_EQ(NULL, pNode) << "added before NULL"; + + // one-item list + node_t* n4 = list.addFront(4); + ASSERT_EQ(4, n4->element) << "wrong element"; + node_t* n3 = list.addAfter(3, n4); + // list: 4->3 + ASSERT_EQ(n4, list.head()) << "n1 should be head"; + ASSERT_EQ(n3, list.tail()) << "n0 should be tail"; + ASSERT_EQ(4, list.head()->element) << "wrong head element"; + ASSERT_EQ(3, list.tail()->element) << "wrong tail element"; + ASSERT_EQ(n3, n4->next()) << "n4->next should be n3"; + ASSERT_EQ(n4, n3->prev()) << "n3->prev should be n4"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + ASSERT_EQ(list.end().pNode, n4->prev()) << "n4->prev should be end()"; + ASSERT_EQ(list.end().pNode, n3->next()) << "n3->next should be end()"; + + // add more items + const int testSize = 5; + node_t* n[testSize]; + n[4] = n4; + n[3] = n3; + for (int i = 2; i >= 0; i--){ + n[i] = list.addAfter(i, n[i + 1]); + } + // list: 4->3->2->1->0 + ASSERT_EQ(5, list.size()) << "size should be 5"; + ASSERT_EQ(n[4], list.head()) << "4 should be at head"; + ASSERT_EQ(n[3], list.head()->next()) << "3 should be 4->next"; + ASSERT_EQ(n[2], list.head()->next()->next()) << "2 should be 3->next"; + ASSERT_EQ(n[1], list.head()->next()->next()->next()) << "1 should be 2->next"; + ASSERT_EQ(n[0], list.head()->next()->next()->next()->next()) << "0 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(n[0], list.tail()) << "0 should be at tail"; + ASSERT_EQ(n[1], list.tail()->prev()) << "1 should be 0->prev"; + ASSERT_EQ(n[2], list.tail()->prev()->prev()) << "2 should be 1->prev"; + ASSERT_EQ(n[3], list.tail()->prev()->prev()->prev()) << "3 should be 2->prev"; + ASSERT_EQ(n[4], list.tail()->prev()->prev()->prev()->prev()) << "4 should be 3->prev"; + ASSERT_EQ(list.end().pNode, list.tail()->prev()->prev()->prev()->prev()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(4, list.head()->element) << "4 should be at head"; + ASSERT_EQ(3, list.head()->next()->element) << "3 should be 4->next"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "2 should be 3->next"; + ASSERT_EQ(1, list.head()->next()->next()->next()->element) << "1 should be 2->next"; + ASSERT_EQ(0, list.head()->next()->next()->next()->next()->element) << "0 should be 1->next"; + + // add in the middle + node_t* n10 = list.addAfter(10, n[3]); + // list: 4->3->10->2->1->0 + ASSERT_EQ(6, list.size()) << "size should be 6"; + ASSERT_EQ(n[4], list.head()) << "4 should be at head"; + ASSERT_EQ(n[3], list.head()->next()) << "3 should be 4->next"; + ASSERT_EQ(n10, list.head()->next()->next()) << "10 should be 3->next"; + ASSERT_EQ(n[2], list.head()->next()->next()->next()) << "2 should be 10->next"; + ASSERT_EQ(n[1], list.head()->next()->next()->next()->next()) << "1 should be 10->next"; + ASSERT_EQ(n[0], list.head()->next()->next()->next()->next()->next()) << "0 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(n[0], list.tail()) << "0 should be at tail"; + ASSERT_EQ(n[1], list.tail()->prev()) << "1 should be 0->prev"; + ASSERT_EQ(n[2], list.tail()->prev()->prev()) << "2 should be 1->prev"; + ASSERT_EQ(n10, list.tail()->prev()->prev()->prev()) << "10 should be 2->prev"; + ASSERT_EQ(n[3], list.tail()->prev()->prev()->prev()->prev()) << "3 should be 10->prev"; + ASSERT_EQ(n[4], list.tail()->prev()->prev()->prev()->prev()->prev()) << "4 should be 3->prev"; + ASSERT_EQ(list.end().pNode, list.tail()->prev()->prev()->prev()->prev()->prev()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(4, list.head()->element) << "4 should be at head"; + ASSERT_EQ(3, list.head()->next()->element) << "3 should be 4->next"; + ASSERT_EQ(10, list.head()->next()->next()->element) << "10 should be 3->next"; + ASSERT_EQ(2, list.head()->next()->next()->next()->element) << "2 should be 10->next"; + ASSERT_EQ(1, list.head()->next()->next()->next()->next()->element) << "1 should be 10->next"; + ASSERT_EQ(0, list.head()->next()->next()->next()->next()->next()->element) << "0 should be 1->next"; + + } + + + void remove(){ + int e; + bool ok; + node_t *n1, *n2, *n3, *n4, *n5; + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + { + SCOPED_TRACE("removing from empty list"); + ok = list.remove(list.head(), e); // should fail + ASSERT_EQ(false, ok) << "remove() succeeded on empty list"; + } + + { + SCOPED_TRACE("removing the only node"); + n1 = list.addFront(1); + ok = list.remove(n1, e); + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(1, e) << "wrong element in removed node"; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(true, list.empty()) << "list should be empty"; + + // reinsert a node + list.addFront(1); + ASSERT_EQ(1, list.size()) << "size should be 1"; + ASSERT_EQ(false, list.empty()) << "list should be non-empty"; + } + + { + // list: 1 + SCOPED_TRACE("remove the second node of a size-2 list"); + n1 = list.addFront(2); // list: 2->1 + ok = list.remove(n1, e); // list: 1 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(2, e) << "wrong element in removed node"; + ASSERT_EQ(1, list.size()) << "size should be zero"; + ASSERT_EQ(false, list.empty()) << "list should be empty"; + ASSERT_EQ(1, list.head()->element) << "head-element should be 1"; + } + + { + // list: 1 + SCOPED_TRACE("remove the last node of a size-3 list"); + n1 = list.head(); // list: 1 + n2 = list.addFront(2); // list: 2->1 + n3 = list.addFront(3); // list: 3->2->1 + + ok = list.remove(n1, e); // remove 1; list: 3->2 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(1, e) << "wrong element in removed node"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + ASSERT_EQ(false, list.empty()) << "list should not be empty"; + + ASSERT_EQ(n3, list.head()) << "wrong pointer at list head"; + ASSERT_EQ(n2, list.tail()) << "wrong pointer at list tail"; + ASSERT_EQ(n2, list.head()->next()) << "wrong pointer at list head->next"; + ASSERT_EQ(n3, list.tail()->prev()) << "wrong pointer at list tail->prev"; + ASSERT_EQ(3, list.head()->element) << "head-element should be 1"; + ASSERT_EQ(2, list.head()->next()->element) << "head-element should be 2"; + + } + + { + // list: 3->2 + SCOPED_TRACE("remove a middle node"); + n3 = list.head(); // list: 3->2 + n2 = n3->next(); // + n4 = list.addFront(4); // list: 4->3->2 + n5 = list.addFront(5); // list: 5->4->3->2 + + + // try to remove 4 + ok = list.remove(n4, e); // remove 4; list: 5->3->2 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(4, e) << "removed element should be 4"; + ASSERT_EQ(3, list.size()) << "size should be 3"; + ASSERT_EQ(false, list.empty()) << "list should not be empty"; + + ASSERT_EQ(n5, list.head()) << "wrong pointer at list head"; + ASSERT_EQ(n3, list.head()->next()) << "wrong pointer at list head->next"; + ASSERT_EQ(n2, list.head()->next()->next()) << "wrong pointer at list head->next->next"; + ASSERT_EQ(list.end().pNode, n2->next()) << "last->next should be end()"; + + ASSERT_EQ(n2, list.tail()) << "wrong pointer at list tail"; + ASSERT_EQ(n3, list.tail()->prev()) << "wrong pointer at list tail->prev"; + ASSERT_EQ(n5, list.tail()->prev()->prev()) << "wrong pointer at list tail->prev->prev"; + ASSERT_EQ(list.end().pNode, n5->prev()) << "head->prev should be end()"; + + ASSERT_EQ(5, list.head()->element) << "head-element should be 5"; + ASSERT_EQ(3, list.head()->next()->element) << "head-element should be 3"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "head-element should be 2"; + + // now try to remove 3 + // list: 5->3->2 + ok = list.remove(n3, e); // remove 3; list: 5->2 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(3, e) << "removed element should be 3"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + ASSERT_EQ(false, list.empty()) << "list should not be empty"; + + ASSERT_EQ(n5, list.head()) << "wrong pointer at list head"; + ASSERT_EQ(n2, list.head()->next()) << "wrong pointer at list head->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()) << "last->next should be end()"; + ASSERT_EQ(n2, list.tail()) << "wrong pointer at list tail"; + ASSERT_EQ(n5, list.tail()->prev()) << "wrong pointer at list tail->prev"; + ASSERT_EQ(list.end().pNode, list.tail()->prev()->prev()) << "head->prev should be end()"; + + ASSERT_EQ(5, list.head()->element) << "head-element should be 5"; + ASSERT_EQ(2, list.head()->next()->element) << "head-next element should be 2"; + } + } + void head(){ + int e; + //IntList list = * static_cast(createList()); + IntList list; + node_t* pNode = list.head(); + ASSERT_EQ(0, (int)pNode) << "head must be null at the beginning"; + + list.addFront(4); + pNode = list.head(); + ASSERT_NE(0, (int)pNode) << "head is NULL although list has items"; + ASSERT_EQ(list.end().pNode, pNode->next()) << "head's next should be end()"; + ASSERT_EQ(list.head(), list.tail()) << "tail should be = head"; + ASSERT_EQ(1, list.size()) << "size should have been 1"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + ASSERT_EQ(4, pNode->element) << "head-element should have been 4"; + + list.remove(pNode, e); + pNode = list.head(); + ASSERT_EQ(0, (int)pNode) << "head should have been null"; + } + + void tail(){ + int e; + IntList list; + node_t* pNode = list.tail(); + ASSERT_EQ(0, (int)pNode) << "tail must be null at the beginning"; + + list.addFront(4); + pNode = list.tail(); + ASSERT_NE(0, (int)pNode) << "tail is NULL although list has items"; + ASSERT_EQ(list.end().pNode, pNode->next()) << "tail's next should be end()"; + ASSERT_EQ(list.end().pNode, pNode->prev()) << "tail's prev should be end()"; + ASSERT_EQ(list.head(), list.tail()) << "tail should be = head"; + ASSERT_EQ(1, list.size()) << "size should have been 1"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + ASSERT_EQ(4, pNode->element) << "tail-element should have been 4"; + + list.remove(pNode, e); + pNode = list.tail(); + ASSERT_EQ(0, (int)pNode) << "tail should have been null"; + } + + void clear(){ + IntList list; + int srcArr[] = { 10, 20, 30, 40 }; + int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + //ASSERT_NE(0, (int)list.head()) << "list head is NULL"; + ASSERT_EQ(n, list.size()) << "list size should have been " << n; + + list.clear(); + ASSERT_EQ(0, list.size()) << "size not zero after clear"; + ASSERT_EQ(true, list.empty()) << "size not zero after clear"; + ASSERT_EQ(NULL, list.head()) << "head not NULL after clear"; + ASSERT_EQ(NULL, list.tail()) << "tail not NULL after clear"; + } + + }; + + ADD_TEST_F(DoublyLinkedListTest, insert); + ADD_TEST_F(DoublyLinkedListTest, addFront); + ADD_TEST_F(DoublyLinkedListTest, addBack); + ADD_TEST_F(DoublyLinkedListTest, addBefore); + ADD_TEST_F(DoublyLinkedListTest, addAfter); + ADD_TEST_F(DoublyLinkedListTest, remove); + ADD_TEST_F(DoublyLinkedListTest, head); + ADD_TEST_F(DoublyLinkedListTest, tail); + + ADD_TEST_F(DoublyLinkedListTest, fromArray); + ADD_TEST_F(DoublyLinkedListTest, elements); + ADD_TEST_F(DoublyLinkedListTest, relements); + ADD_TEST_F(DoublyLinkedListTest, clear); + ADD_TEST_F(DoublyLinkedListTest, toString); + +} // namespace ds + +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.iterator.h b/YASI_12/ds.iterator.h new file mode 100644 index 0000000..a65c02b --- /dev/null +++ b/YASI_12/ds.iterator.h @@ -0,0 +1,45 @@ +#pragma once +#include "common.h" +#include "utils.h" +#include "ds.node.h" + +using namespace std; + +namespace yasi{ + namespace ds{ + + // node iterator base + template + class NodeIteratorBase{ + typedef NodeIteratorBase self; + public: + virtual ~NodeIteratorBase(){ pNode = NULL; } + Node* pNode; + + NodeIteratorBase() : pNode(NULL){} + NodeIteratorBase(Node* pNode) : pNode(pNode){} + NodeIteratorBase(const self& other) : pNode(other.pNode){} + // operators + E& operator* () const { if (pNode) 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; } + }; + + // node iterator + template // E is the value type + class NodeIterator : public NodeIteratorBase{ + typedef NodeIteratorBase base; + typedef NodeIterator self; + public: + virtual ~NodeIterator(){ } + + NodeIterator() {} + NodeIterator(Node* pNode) : base(pNode){} + NodeIterator(const self& other) : base(other.pNode){} + // operators + 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.kvpair.h b/YASI_12/ds.kvpair.h new file mode 100644 index 0000000..3118427 --- /dev/null +++ b/YASI_12/ds.kvpair.h @@ -0,0 +1,37 @@ +#pragma once +#include "common.h" +#include "ds.comparable.h" +#include + +namespace yasi{ + namespace ds{ + using namespace std; + + // the key-value pair class + template + class KVPair :public IComparable< KVPair >{ + typedef KVPair self; + public: + Key key; + Value value; + + virtual ~KVPair(){} + KVPair(){} + KVPair(Key k, Value v) : key(k), value(v){} + explicit KVPair(Key k) : key(k){} + KVPair(const self& other) :key(other.key), value(other.value){} + self& operator= (const self& other){ key = other.key; value = other.value; return *this; } + inline bool operator==(const self& other) const override{ return key == other.key; } + inline bool operator< (const self& other) const override{ return key < other.key; } + inline bool operator<= (const self& other) const override{ return key <= other.key; } + inline bool operator!=(const self& other) const { return !((*this) == other); } + }; + // override for printing + template + std::ostream& operator<< (std::ostream& out, const KVPair& kv) { + out << kv.key; + return out; + } + + } +} \ No newline at end of file diff --git a/YASI_12/ds.list.h b/YASI_12/ds.list.h new file mode 100644 index 0000000..bfdd8f2 --- /dev/null +++ b/YASI_12/ds.list.h @@ -0,0 +1,26 @@ +#pragma once +#include "common.h" +#include "utils.h" +#include "ds.iterator.h" +using namespace std; + +namespace yasi{ + +namespace ds{ + +// list interface +template +class IList{ +public: + virtual ~IList(){} + virtual void push(const E&) = 0; + virtual void pop() = 0; + virtual const E& top() const = 0; + virtual int size() const = 0; + virtual bool empty() const = 0; + virtual Iter begin() const = 0; + virtual Iter end() const = 0; +}; + +} // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.mutableheap.h b/YASI_12/ds.mutableheap.h new file mode 100644 index 0000000..3c9342d --- /dev/null +++ b/YASI_12/ds.mutableheap.h @@ -0,0 +1,90 @@ +#pragma once +#include "ds.binaryheap.h" +#include "gtest\gtest.h" +#include "ds.kvpair.h" +#include "utils.h" + +namespace yasi{ + namespace ds{ + using namespace std; + + template + class MutableHeap : public BinaryHeap < KVPair > { + ////////// enable testing /////////// + friend class MutableHeapTest; + typedef KVPair HeapElement; + typedef BinaryHeap Heap; + typedef typename Heap::HeapNode Node; + + public: + virtual ~MutableHeap(){} + MutableHeap(){} + MutableHeap(const HeapElement* pArr, const int numElems) : Heap(pArr, numElems){} + void updateKey(Node* pNode, const Key newKey){ + if (pNode){ + HeapElement newElem(newKey); + if (pNode->element != newElem){ + // need update + if (newElem < pNode->element){ + // need bubble-up + ((HeapElement*) & pNode->element)->key = newKey; // updated + bubbleUp(pNode); + } + else{ + // need bubble-down + ((HeapElement*) & pNode->element)->key = newKey; // updated + bubbleDown(pNode); + } + } + } + } + }; + + class MutableHeapTest : public ::testing::Test{ + typedef KVPair Pair; + typedef MutableHeap MHeap; + public: + void updateKey(){ + MHeap h; + MHeap::HeapNode* n[5]; + for (int i = 0; i < 5; i++){ + n[i] = h.insert(Pair(i)); // both key and value are i + } + + string str; + h.toString(str); // should be 0 1 2 3 4 + string strBefore = strPurge(str, "| "); + string expectedStrBefore = "01234"; + ASSERT_EQ(expectedStrBefore, strBefore) << "heap toString() mismatch"; + + // now update key 3 to 9 + h.updateKey(n[3], 9); + // heap: 0 1 2 9 4 + h.toString(str); + string strAfter = strPurge(str, "| "); + string expectedStrAfter = "01294"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 3-->9"; + + // now update key 2 to -8 + h.updateKey(n[2], -8); + // heap: -8 1 0 9 4 + h.toString(str); + strAfter = strPurge(str, "| "); + expectedStrAfter = "-81094"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 2-->-8"; + + // now update key 9 to -9 + h.updateKey(n[3], -9); + // heap: -9 -8 0 1 4 + h.toString(str); + strAfter = strPurge(str, "| "); + expectedStrAfter = "-9-8014"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 9-->-9"; + + } + }; + + ADD_TEST_F(MutableHeapTest, updateKey); + + } // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.node.h b/YASI_12/ds.node.h new file mode 100644 index 0000000..24acbd9 --- /dev/null +++ b/YASI_12/ds.node.h @@ -0,0 +1,22 @@ +#pragma once +#include "common.h" +namespace yasi{ + namespace ds{ + // node base + template + class NodeBase{ + typedef NodeBase self; + public: + 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; + //} + void setElement(const E& e){ this->element = e; } + }; + + } +} \ No newline at end of file diff --git a/YASI_12/ds.priorityqueue.h b/YASI_12/ds.priorityqueue.h new file mode 100644 index 0000000..0618db2 --- /dev/null +++ b/YASI_12/ds.priorityqueue.h @@ -0,0 +1,331 @@ +#pragma once +#include "common.h" +#include "gtest\gtest.h" +#include "utils.h" +#include "ds.list.h" +#include "ds.binaryheap.h" +#include "ds.aux.hastree.h" +#include "ds.BSTDictionary.h" // for mutable priority queue +#include + +using namespace std; + +namespace yasi{ + +namespace ds{ + +template +class IPriorityQueue { +public: + virtual ~IPriorityQueue(){} + virtual const E& top() const = 0; // gives the top element; behavior undefined when heap empty + virtual void pop() = 0; // gives the top element and removes it from the heap; behavior undefined when heap empty + virtual void push(const E&) = 0; // adds an element to the heap + virtual int size() const = 0; + virtual bool empty() const = 0; + virtual E removeTop() = 0; // removes and returns the top-priority element +}; + +// class E must implement IComparable interface +template > +class PriorityQueue : public virtual IPriorityQueue < E >, public ds::aux::HasTree { + /////////// enable testing //////////// + friend class PriorityQueueTest; + +protected: + Heap h; +public: + typedef typename Heap::HeapNode Node; + + const typename Heap::Tree& tree() const{ return h.tree(); } + + PriorityQueue(){ + + } + virtual ~PriorityQueue(){ clear(); } + void push(const E& e) override { h.push(e); } + void pop() override { h.pop(); } + E removeTop() override { E e = (E)h.top(); h.pop(); return e; } + const E& top() const override { return h.top(); } + int size() const override { return h.size(); } + bool empty() const { return h.empty(); } + string toStringBFS(){ return h.toStringBFS(); } + void clear(){ h.clear(); } + // for advanced modification, expose the heap + Heap& heap() { return h; } +}; + + + class PriorityQueueTest :public yasi::Test{ + typedef PriorityQueue pq_t; + public: + void all(){ + //PriorityQueue< stack > pqq; + int t; + pq_t pq; + + ASSERT_EQ(0, pq.size()) << "size not zero"; + ASSERT_EQ(true, pq.empty()) << "pq not empty"; + + pq.push(t = 5); + pq.push(t = 2); + pq.push(t = 10); + pq.push(t = 8); + pq.push(t = 5); + pq.push(t = 3); + + ASSERT_EQ(6, pq.size()) << "size not 6"; + ASSERT_EQ(false, pq.empty()) << "pq empty"; + ASSERT_EQ(2, pq.top()) << "top not 2"; + + pq.pop(); + ASSERT_EQ(5, pq.size()) << "size not 5"; + ASSERT_EQ(3, pq.top()) << "top not 3"; + + pq.pop(); + ASSERT_EQ(4, pq.size()) << "size not 4"; + ASSERT_EQ(5, pq.top()) << "top not 5"; + + pq.pop(); + ASSERT_EQ(3, pq.size()) << "size not 3"; + ASSERT_EQ(5, pq.top()) << "top not 5"; + + pq.push(7); + pq.push(13); + ASSERT_EQ(5, pq.size()) << "size not 5"; + ASSERT_EQ(5, pq.top()) << "top not 5"; + + pq.pop(); + ASSERT_EQ(4, pq.size()) << "size not 4"; + ASSERT_EQ(7, pq.top()) << "top not 7"; + + pq.pop(); + ASSERT_EQ(3, pq.size()) << "size not 3"; + ASSERT_EQ(8, pq.top()) << "top not 8"; + + pq.pop(); + ASSERT_EQ(2, pq.size()) << "size not 2"; + ASSERT_EQ(10, pq.top()) << "top not 10"; + + pq.pop(); + ASSERT_EQ(1, pq.size()) << "size not 1"; + ASSERT_EQ(13, pq.top()) << "top not 13"; + + pq.pop(); + ASSERT_EQ(0, pq.size()) << "size not 0"; + ASSERT_EQ(true, pq.empty()) << "pq not empty"; + } + + void maxPriorityQueue(){ + // pq of numeric string, max-pq + PriorityQueue > pq; + + pq.push("10"); + pq.push("5"); + pq.push("20"); + pq.push("15"); + pq.push("25"); + { + /* heap: + 25 + 20 10 + 5 15 + */ + SCOPED_TRACE("max-pq #1"); + string actual = strPurge(pq.toStringBFS(), "| "); + ASSERT_EQ("252010515", actual) << "bad max pq"; + } + // add some more items + { + pq.push("12"); + pq.push("30"); + pq.push("3"); + pq.push("40"); + /* pq: + 40 + 30 25 + 20 15 10 12 + 3 5 + */ + SCOPED_TRACE("max-pq #2"); + string actual = strPurge(pq.toStringBFS(), "| "); + ASSERT_EQ("4030252015101235", actual) << "bad max pq after add"; + } + // now check by popping + int actual[9]; + for (int i = 0; !pq.empty(); i++){ + actual[i] = atoi(pq.removeTop().c_str()); + } + const int expected[] = {40, 30, 25, 20, 15, 12, 10, 5, 3}; + ASSERT_EQ(true, arrcmp(actual, expected, ARR_LENGTH(expected))) << "wrong pop sequence from max heap"; + } + }; + + ADD_TEST_F(PriorityQueueTest, all); + ADD_TEST_F(PriorityQueueTest, maxPriorityQueue); + + +/////////////////////////////////// +////// mutable priority queue ///// +/////////////////////////////////// +template +class MutablePriorityQueue { + /////////// enable testing //////////// + friend class MutablePriorityQueueTest; + + typedef MutableHeap < Key, Value > HeapType; + typedef KVPair Pair; +public: + typedef typename HeapType::HeapNode Node; + +protected: + // value-to-heapnode lookup + typedef Node* DictValue; + typedef Value DictKey; + typedef BSTDictionary Dict; + Dict d; + + // the internal priority queue + PriorityQueue < Pair, HeapType > pq; +protected: + DictValue findNode(const Value& v) const { + DictValue* pValue = d.find(v); // d.find() returns a ptr to value, and NULL if not found + return pValue ? *pValue : NULL; + } + +public: + typedef typename HeapType::HeapNode Node; + + MutablePriorityQueue(){ } + virtual ~MutablePriorityQueue(){ pq.clear(); } + + // modify existing priority queue methods to support key update + // input arg kv is an instance of the KVPair class + void push(const Key& k, const Value& v) { + insert(kv.key, kv.value); + } + void pop() { + if (!empty()){ + removeTop(); + } + } + + // on empty queue, behavior undefined + Value removeTop() { + Pair kv; + if (!empty()){ + kv = (Pair)pq.top(); + + // remove the element from dictionary + d.remove(kv.value); + + // now pop from heap + pq.pop(); + + } + // done + return kv.value; + } + + // undefined on empty queue + const Value& top() const { return pq.top(); } + int size() const { return pq.size(); } + bool empty() const { return pq.empty(); } + string toStringBFS(){ return pq.toStringBFS(); } + + + // new methods on top of PriorityQueue + Node* insert(const Key& k, const Value& v) { + Node* pNode = pq.heap().insert(Pair(k, v)); + // add the node to dictionary + // with key = kv.value + d.insert(v, pNode); + return pNode; + } + Node* insert(const Key& k) { return insert(k, k); } + //Node* insert(const Pair& e) { return h.insert(e); } + + // update the key in a HeapNode pointer + void updateKey(Node* pNode, const Key newKey){ if(pNode) pq.heap().updateKey(pNode, newKey); } + // update the key of a value; if values are not unique, behavior is undefined + void updateKey(const Value& v, const Key newKey){ + Node* pNode = findNode(v); + if(pNode) updateKey(pNode, newKey); + } +}; + +class MutablePriorityQueueTest : public yasi::Test{ + typedef MutablePriorityQueue MPQ; + typedef MPQ::Node Node; +public: + void dictionary(){ + // insert key-values and check the dictionary + MPQ pq; + Node* n[5]; + for (int i = 0; i < 5; i++){ + n[i] = pq.insert(i, i*10); // key i, value i*10 + } + ASSERT_EQ(pq.size(), pq.d.size()) << "dictionary and pq size not same"; + + // now check dictionary + string strDictBfs = pq.d.toStringBFS(); + string strDictPreorder = pq.d.toStringPreOrder(); + for (int i = 0; i < 5; i++){ + // lookup node by value + Node* pNode = pq.findNode(i*10); + ASSERT_NE(0, (int)pNode) << "pq node NULL"; + ASSERT_EQ(pNode, n[i]) << "dictionary entry mismatch"; + } + + // remove a node + int topValue = pq.removeTop(); + ASSERT_EQ(0, (int) pq.findNode(topValue)) << "removed node still in dictionary"; + ASSERT_EQ(pq.size(), pq.d.size()) << "dictionary and pq size not same"; + + } + void updateKey(){ + MPQ pq; + MPQ::Node* n[5]; + for (int i = 0; i < 5; i++){ + n[i] = pq.insert(i); // both key and value are i + } + + string str; + str = pq.toStringBFS(); // should be 0 1 2 3 4 + string strBefore = strPurge(str, "| "); + string expectedStrBefore = "01234"; + ASSERT_EQ(expectedStrBefore, strBefore) << "heap toString() mismatch"; + + // now update key 3 to 9 + pq.updateKey(3, 9); // testing updateKey(value, key) method + // heap: 0 1 2 9 4 + str = pq.toStringBFS(); + string strAfter = strPurge(str, "| "); + string expectedStrAfter = "01294"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 3-->9"; + + // now update key 2 to -8 + pq.updateKey(n[2], -8); // testing updateKey(node, key) method + // heap: -8 1 0 9 4 + str = pq.toStringBFS(); + strAfter = strPurge(str, "| "); + expectedStrAfter = "-81094"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 2-->-8"; + + // now update key 9 (which has value 3) to -9 + pq.updateKey(3, -9); // testing updateKey(value, key) method + // heap: -9 -8 0 1 4 + str = pq.toStringBFS(); + strAfter = strPurge(str, "| "); + expectedStrAfter = "-9-8014"; + ASSERT_EQ(expectedStrAfter, strAfter) << "bad heap after update 9-->-9"; + + } +}; + +ADD_TEST_F(MutablePriorityQueueTest, dictionary); +ADD_TEST_F(MutablePriorityQueueTest, updateKey); + +} // namespace ds + +}// namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.singlylinkedlist.h b/YASI_12/ds.singlylinkedlist.h new file mode 100644 index 0000000..a76d74f --- /dev/null +++ b/YASI_12/ds.singlylinkedlist.h @@ -0,0 +1,855 @@ +#pragma once +#include "common.h" +#include "utils.h" +#include "ds.list.h" +#include "ds.node.h" +#include "ds.iterator.h" +#include +using namespace std; + +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 +class ISinglyLinkedList:virtual public IList{ +public: + virtual ~ISinglyLinkedList(){} + virtual Node* head() const = 0; + virtual Node* addFront(const E& e) = 0; + virtual Node* addAfter(const E&, Node*) = 0; + virtual Node* addBefore(const E&, Node*) = 0; + virtual bool remove(Node*, E& e) = 0; + virtual bool remove(Node*) = 0; + virtual void elements(E**) = 0; + virtual string toString() const = 0; + virtual const E& front() const = 0; +}; + + +// singly linked list iterator +// iterator +template< class E, class Node> +class SLLIterator : public NodeIterator{ + typedef SLLIterator self; + typedef NodeIterator 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; } +}; + + +// singly linke list class +// virtual inheritance needed because +// (1) the IDoublyLinkedList will also inherit from ISinglyLinkedList, and +// (2) DoublyLinkedList will inherit from SinglyLinkedList +template, class Iter = SLLIterator > +class SinglyLinkedList : public virtual ISinglyLinkedList < E, Node, Iter > { + ///////////////// enable testing//////////////// + friend class SinglyLinkedListTest; + +protected: + // typedefs + typedef Node node_t; + typedef Iter iterator; + + //node_t* pHead; // should be obsolete + // make the list circular, so that we can make the iterator end() + node_t* pHorizon; // (last element)-->horizon-->head + + + int _size; + + E _defaultElem; + + + // returns the node p such that pNode = p->next() + // it is virtual because we will allow DoublyLinkedList to override this method + virtual node_t* before(node_t* pNode){ + if (pNode == NULL ){ + return NULL; + } + else{ + // start from the horizon + node_t* pParent = pHorizon; + while (pParent != NULL && + pParent->next() != pNode){ + pParent = pParent->next(); + } + // either found, or not + if (pParent == NULL){ + // list exhausted + // something wrong, maybe this node is not in the list + return NULL; + } + else{ + return pParent; + } + } + } + virtual node_t* head() const override{ + if (_size == 0) return NULL; + else{ + return pHorizon->next(); + } + } + +public: + // typedef SLLIterator iterator; + SinglyLinkedList(){ + //pHead = NULL; + pHorizon = new node_t(); + pHorizon->setNext(pHorizon); // unit loop: horizon--horizon--horizon + } + SinglyLinkedList(const E* pArr, const int numElems) : SinglyLinkedList(){ + //pHead = NULL; + fromArray(pArr, numElems); + } + virtual ~SinglyLinkedList(){ + this->clear(); + DELETE_SAFE(pHorizon); + } + + ///////////////////////////////////////////////////////////// + //////////// Implement ISinglyLinkedList methods //////////// + ///////////////////////////////////////////////////////////// + virtual node_t* addFront(const E& e) override{ + return addAfter(e, pHorizon); + // node_t* pNode = new node_t(e); // dynamic memory allocation + //if (head() == NULL){ + // // add the first node + // head() = pNode; // set at head + // _size = 1; + //} + //else{ + // // add it before the head + // pNode->setNext(head()); + // head() = pNode; + // _size++; + //} + //return pNode; + } + virtual node_t* addAfter(const E& e, node_t* pBefore) override{ + if (pBefore == NULL) return NULL; + + node_t* pAfter = pBefore->next(); + node_t* pNode = new node_t(e, pAfter); // dynamic memory allocation + pBefore->setNext(pNode); + _size++; + return pNode; + } + virtual node_t* addBefore(const E& e, node_t* pAfter) override{ + if (pAfter == NULL) return NULL; + + if (pAfter == head()) + return addFront(e); // add before head + + node_t* pBefore = before(pAfter); + return addAfter(e, pBefore); + //if (pBefore){ + // node_t* pNode = new node_t(e, pAfter); // dynamic memory allocation + // pBefore->setNext(pNode); + // _size++; + // return pNode; + //} + //else return NULL; + } + + + virtual bool remove(node_t* pNode, E& e) override{ + if (pNode == NULL || pNode == pHorizon) return false; + + e = pNode->element; + // removing from the middle + node_t* pAfter = pNode->next(); + node_t* pBefore = before(pNode); + // pBefore should be non-null + if (pBefore){ + pBefore->setNext(pAfter); + _size--; + DELETE_SAFE(pNode); // free memory + return true; + } + else{ + // something is wrong; pNode may not be in the list + return false; + } + } + virtual bool remove(node_t* pNode) override{ + E e; + return remove(pNode, e); + } + + // actually, it never returns false + // the order of elements are the reverse of the order they were inserted + // *ppArr must contain enough memory + void elements(E** ppArr) override{ + int i = 0; + for (iterator n = begin(); n != end(); n++){ + (*ppArr)[i++] = *n; + } + } + string toString() const override{ + std::stringstream buf; + for (iterator n = begin(); n != end(); n++){ + buf << *n << "->"; + } + return buf.str(); + } + + //////////// Done Implementing ISinglyLinkedList methods //////////// + + //////////////////////////////////////// + /////////// IList methods ////////////// + //////////////////////////////////////// + inline int size() const override{ + return _size; + } + inline bool empty() const override{ + return _size == 0 (pHorizon == pHorizon->next() ); + } + virtual void push(const E& e) override{ + this->addFront(e); + } + virtual void pop() override{ + if(size() > 0 ) + this->remove(begin().pNode); + } + virtual inline const E& top() const override{ + return *begin(); + } + virtual inline const E& front() const override{ + return top(); + } + + ///////////// iterators ///////////// + iterator begin() const{ + return pHorizon->next(); + } + iterator end() const{ + return pHorizon; + } + + + + ///// non-interface methods + // actually, it never returns false + // the order of elements are the same as their insertion order + // *ppArr must contain enough memory + virtual void relements(E** ppArr) { + int i = size() - 1; + for (iterator n = begin(); n != end(); n++){ + E e = *n; + (*ppArr)[i] = e; + i--; + } + } + void clear(){ + iterator e = end(); + for (iterator n = begin(); n != end(); ){ + node_t* pNode = (n++).pNode; + DELETE_SAFE(pNode); + } + _size = 0; + pHorizon->setNext(pHorizon); + //pHead = NULL; + } + + virtual bool fromArray(const E* pArr, const int numElems){ + if (!pArr || (numElems <= 0)) return false; + + if( !empty() ) clear(); + for (int i = 0; i < numElems; i++){ + addFront(pArr[i]); + } + _size = numElems; // set the size + return true; + } + + +}; + + +class SinglyLinkedListTest : public yasi::Test{ + 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){ + E* pElems = new int[n]; + int numElems; + SinglyLinkedList >& list = *pList; + list.elements(&pElems); + ASSERT_EQ(true, arrcmp(srcArr, pElems, n)) << "arrays are not the same" << endl + << "actual: " << arrToString(srcArr, n) << endl + << "expected: " << arrToString(pElems, n); + DELETE_ARR_SAFE(pElems); + } + + + void head(){ + int e; + //IntList list = * static_cast(createList()); + IntList list; + node_t* pNode = list.head(); + ASSERT_EQ(0, (int)pNode) << "head must be null at the beginning"; + + list.addFront(4); + pNode = list.head(); + ASSERT_NE(0, (int)pNode) << "head is NULL although list has items"; + ASSERT_EQ(list.end().pNode, pNode->next()) << "head's next should be end()"; + ASSERT_EQ(1, list.size()) << "size should have been 1"; + ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + ASSERT_EQ(4, pNode->element) << "head-element should have been 4"; + + list.remove(pNode, e); + pNode = list.head(); + ASSERT_EQ(0, (int)pNode) << "head should have been null"; + } + + void clear(){ + int e, temp; + bool ok; + node_t *n1, *n2, *n3, *n4, *n5; + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + { + SCOPED_TRACE("clear empty list"); + list.clear(); + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + } + + { + SCOPED_TRACE("clear list with the only node"); + n1 = list.addFront(1); + list.clear(); + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(true, list.empty()) << "list should be empty"; + ASSERT_EQ(NULL, (int)list.head()) << "head not NULL"; + + } + + { + // list: 1 + SCOPED_TRACE("clear list with several nodes"); + list.addFront(temp = 2); + list.addFront(temp = 3); + list.addFront(temp = 4); + list.addFront(temp = 5); + list.clear(); + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(true, list.empty()) << "list should be empty"; + ASSERT_EQ(NULL, (int)list.head()) << "head not NULL"; + } + + } + + void addBefore(){ + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + // add before NULL + node_t* pNode = list.addBefore(5, NULL); // should fail + ASSERT_EQ(NULL, (int) pNode) << "added before NULL"; + + // one-item list + node_t* n0 = list.addFront(0); + ASSERT_EQ(0, n0->element) << "wrong element"; + node_t* n1 = list.addBefore(1, n0); + // list: 1->0 + ASSERT_EQ(n1, list.head()) << "node is not at head"; + ASSERT_EQ(1, list.head()->element) << "wrong element"; + ASSERT_EQ(n0, n1->next()) << "n1->next should be n0"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + + // add more items + const int testSize = 5; + node_t* n[testSize]; + n[0] = n0; + n[1] = n1; + for (int i = 2; i < testSize; i++){ + n[i] = list.addBefore(i, n[i-1]); + } + // list: 4->3->2->1->0 + ASSERT_EQ(5, list.size()) << "size should be 5"; + ASSERT_EQ(n[4], list.head()) << "4 should be at head"; + ASSERT_EQ(n[3], list.head()->next()) << "3 should be 4->next"; + ASSERT_EQ(n[2], list.head()->next()->next()) << "2 should be 3->next"; + ASSERT_EQ(n[1], list.head()->next()->next()->next()) << "1 should be 2->next"; + ASSERT_EQ(n[0], list.head()->next()->next()->next()->next()) << "0 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(4, list.head()->element) << "4 should be at head"; + ASSERT_EQ(3, list.head()->next()->element) << "3 should be 4->next"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "2 should be 3->next"; + ASSERT_EQ(1, list.head()->next()->next()->next()->element) << "1 should be 2->next"; + ASSERT_EQ(0, list.head()->next()->next()->next()->next()->element) << "0 should be 1->next"; + + // add in the middle + node_t* n10 = list.addBefore(10, n[2]); + // list: 4->3->10->2->1->0 + ASSERT_EQ(6, list.size()) << "size should be 6"; + ASSERT_EQ(n[4], list.head()) << "4 should be at head"; + ASSERT_EQ(n[3], list.head()->next()) << "3 should be 4->next"; + ASSERT_EQ(n10, list.head()->next()->next()) << "10 should be 3->next"; + ASSERT_EQ(n[2], list.head()->next()->next()->next()) << "2 should be 10->next"; + ASSERT_EQ(n[1], list.head()->next()->next()->next()->next()) << "1 should be 10->next"; + ASSERT_EQ(n[0], list.head()->next()->next()->next()->next()->next()) << "0 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(4, list.head()->element) << "4 should be at head"; + ASSERT_EQ(3, list.head()->next()->element) << "3 should be 4->next"; + ASSERT_EQ(10, list.head()->next()->next()->element) << "10 should be 3->next"; + ASSERT_EQ(2, list.head()->next()->next()->next()->element) << "2 should be 10->next"; + ASSERT_EQ(1, list.head()->next()->next()->next()->next()->element) << "1 should be 10->next"; + ASSERT_EQ(0, list.head()->next()->next()->next()->next()->next()->element) << "0 should be 1->next"; + + } + void addAfter(){ + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + // add after NULL + node_t* pNode = list.addAfter(5, NULL); // should fail + ASSERT_EQ(NULL, pNode) << "added after NULL"; + + // one-item list + node_t* n0 = list.addFront(0); + ASSERT_EQ(0, n0->element) << "wrong element"; + node_t* n1 = list.addAfter(1, n0); + // list: 0->1 + ASSERT_EQ(n0, list.head()) << "node is not at head"; + ASSERT_EQ(0, list.head()->element) << "wrong element"; + ASSERT_EQ(n1, n0->next()) << "n0->next should be n1"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + + // add more items + const int testSize = 5; + node_t* n[testSize]; + n[0] = n0; + n[1] = n1; + for (int i = 2; i < testSize; i++){ + n[i] = list.addAfter(i, n[i - 1]); + } + // list: 0->1->2->3->4 + ASSERT_EQ(5, list.size()) << "size should be 5"; + ASSERT_EQ(n[0], list.head()) << "0 should be at head"; + ASSERT_EQ(n[1], list.head()->next()) << "1 should be 4->next"; + ASSERT_EQ(n[2], list.head()->next()->next()) << "2 should be 3->next"; + ASSERT_EQ(n[3], list.head()->next()->next()->next()) << "3 should be 2->next"; + ASSERT_EQ(n[4], list.head()->next()->next()->next()->next()) << "4 should be 1->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(0, list.head()->element) << "0 should be at head"; + ASSERT_EQ(1, list.head()->next()->element) << "1 should be 4->next"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "2 should be 3->next"; + ASSERT_EQ(3, list.head()->next()->next()->next()->element) << "3 should be 2->next"; + ASSERT_EQ(4, list.head()->next()->next()->next()->next()->element) << "4 should be 1->next"; + + { + SCOPED_TRACE("add in the middle"); + node_t* n10 = list.addAfter(10, n[2]); + // list: 0->1->2->10->3->4 + ASSERT_EQ(6, list.size()) << "size should be 6"; + ASSERT_EQ(n[0], list.head()) << "0 should be at head"; + ASSERT_EQ(n[1], list.head()->next()) << "1 should be 0->next"; + ASSERT_EQ(n[2], list.head()->next()->next()) << "2 should be 1->next"; + ASSERT_EQ(n10, list.head()->next()->next()->next()) << "10 should be 2->next"; + ASSERT_EQ(n[3], list.head()->next()->next()->next()->next()) << "3 should be 10->next"; + ASSERT_EQ(n[4], list.head()->next()->next()->next()->next()->next()) << "4 should be 3->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()->next()->next()->next()) << "tail->next should be end()"; + + ASSERT_EQ(0, list.head()->element) << "0 should be at head"; + ASSERT_EQ(1, list.head()->next()->element) << "1 should be 0->next"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "2 should be 2->next"; + ASSERT_EQ(10, list.head()->next()->next()->next()->element) << "10 should be 2->next"; + ASSERT_EQ(3, list.head()->next()->next()->next()->next()->element) << "3 should be 10->next"; + ASSERT_EQ(4, list.head()->next()->next()->next()->next()->next()->element) << "4 should be 3->next"; + } + } + + void addFront(){ + + 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() and end() not same for empty list"; + + //node_t *pNode, *pNext, *pNext2; + + //// add head + //{ + // SCOPED_TRACE("add #1"); + // pNode = list.addFront(4); // new node is at head + // ASSERT_EQ(list.head(), pNode) << "addFront() does not return correct node"; + // ASSERT_NE(0, (int)pNode) << "head is NULL although list has items"; + // ASSERT_EQ(list.end().pNode, pNode->next()) << "head's next should be end()"; + // ASSERT_EQ(1, list.size()) << "size should have been 1"; + // ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + // ASSERT_EQ(4, pNode->element) << "head-element should have been 4"; + // + // // iterators + // ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + // ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + // ASSERT_EQ(list.end(), ++list.begin()) << "++begin() and end() not same"; + // ASSERT_NE(list.end(), list.begin()++) << "begin()++ and end() same"; + // ASSERT_EQ(list.head(), (list.begin()++).pNode) << "begin()++.pNode not head()"; + // ASSERT_EQ(list.end().pNode, (++list.begin()).pNode) << "begin++.pNode should be end()"; + // ASSERT_EQ(4, *list.begin()) << "begin()-element should have been 4"; + //} + + //// add 2nd element + //{ + // SCOPED_TRACE("add #2"); + // pNext = list.addFront(5); // new node is at head + + // ASSERT_NE(0, (int)list.head()) << "head is NULL although list has items"; + // ASSERT_EQ(list.head(), pNext) << "addFront() does not return correct node"; + + // ASSERT_NE(list.end().pNode, list.head()->next()) << "head->next is end() although list has 2 items"; + // ASSERT_EQ(list.head()->next(), pNode) << "addFront() did not put the new node at head"; + + // ASSERT_EQ(5, list.head()->element) << "head element should have been 5"; + // ASSERT_EQ(list.end().pNode, list.head()->next()->next()) << "head->next->next should be end()"; + // ASSERT_EQ(2, list.size()) << "size should have been 2"; + // ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + + // ASSERT_EQ(5, list.head()->element) << "head-element should have been 5"; + // ASSERT_EQ(4, list.head()->next()->element) << "head->next element should have been 4"; + + // // iterators + // ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + // ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + // ASSERT_EQ(list.end(), ++ (++(list.begin()))) << "++++begin() not end()"; + // ASSERT_EQ(pNext, (list.begin()++).pNode) << "begin++.pNode should be pNext"; + // ASSERT_EQ(pNode, (++list.begin()).pNode) << "++begin.pNode should be pNode"; + // ASSERT_EQ(5, *(list.begin())) << "begin()-element should have been 5"; + // ASSERT_EQ(4, *(++(list.begin()))) << "begin()-element should have been 4"; + // ASSERT_EQ(5, *(list.begin()++)) << "begin()-element should have been 5"; + //} + + //// add more elements + //{ + // SCOPED_TRACE("add #3"); + // pNext2 = list.addFront(6); // new node is at head + + // ASSERT_EQ(pNext2, list.head()) << "addFront() does not return correct node"; + // ASSERT_EQ(pNext, list.head()->next()) << "addFront() did not put the new node at head"; + // ASSERT_EQ(pNode, list.head()->next()->next()) << "addFront() did not put the new node at head"; + // ASSERT_EQ(list.end().pNode, list.head()->next()->next()->next()) << "last->next should be end()"; + + // ASSERT_NE(0, (int)list.head()) << "head is NULL although list has items"; + // ASSERT_NE(0, (int)list.head()->next()) << "head->next is NULL"; + // ASSERT_NE(0, (int)list.head()->next()->next()) << "head->next->next is NULL"; + + // ASSERT_EQ(6, list.head()->element) << "head element should have been 6"; + // ASSERT_EQ(5, list.head()->next()->element) << "head->next element should have been 5"; + // ASSERT_EQ(4, list.head()->next()->next()->element) << "head->next->next element should have been 4"; + // ASSERT_EQ(3, list.size()) << "size should have been 3"; + // ASSERT_EQ(false, list.empty()) << "non-empty list is showing empty"; + + // // iterators + // ASSERT_EQ(list.head(), list.begin().pNode) << "head() not equal to begin().pNode"; + // ASSERT_NE(list.begin(), list.end()) << "begin() and end() same for non-empty list"; + // ASSERT_EQ(pNext2, (list.begin()).pNode) << "begin.pNode should be pNext2"; + // ASSERT_EQ(pNext2, ((list.begin())++).pNode) << "begin++.pNode should be pNext2"; + // ASSERT_EQ(pNext, (++(list.begin())).pNode) << "++begin.pNode should be pNext"; + // ASSERT_EQ(pNode, (++(++(list.begin()))).pNode) << "++++begin.pNode should be pNode"; + // ASSERT_EQ(list.end(), (++(++( ++list.begin())))) << "++++++begin() not end()"; + // ASSERT_EQ(6, *(list.begin())) << "begin()-element should have been 6"; + // ASSERT_EQ(6, *(list.begin()++)) << "begin()++-element should have been 6"; + // ASSERT_EQ(5, *(++(list.begin()))) << "++begin()-element should have been 5"; + // ASSERT_EQ(4, *(++(++(list.begin())))) << "++++begin()-element should have been 4"; + //} + + + } + + void remove(){ + int e; + bool ok; + node_t *n1, *n2, *n3, *n4, *n5; + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + { + SCOPED_TRACE("removing from empty list"); + ok = list.remove(list.head(), e); // should fail + ASSERT_EQ(false, ok) << "remove() succeeded on empty list"; + } + + { + SCOPED_TRACE("removing the only node"); + n1 = list.addFront(1); + ok = list.remove(n1, e); + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(1, e) << "wrong element in removed node"; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(true, list.empty()) << "list should be empty"; + + // reinsert a node + list.addFront(1); + ASSERT_EQ(1, list.size()) << "size should be 1"; + ASSERT_EQ(false, list.empty()) << "list should be non-empty"; + } + + { + // list: 1 + SCOPED_TRACE("remove the first node of a size-2 list"); + n1 = list.addFront(2); // list: 2->1 + ok = list.remove(n1, e); // list: 1 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(2, e) << "wrong element in removed node"; + ASSERT_EQ(1, list.size()) << "size should be zero"; + ASSERT_EQ(false, list.empty()) << "list should be empty"; + ASSERT_EQ(1, list.head()->element) << "head-element should be 1"; + ASSERT_EQ(list.end().pNode, list.head()->next()) << "head-next should be end()"; + } + + { + // list: 1 + SCOPED_TRACE("remove the second node of a size-2 list"); + n1 = list.addFront(2); // list: 2->1 + ok = list.remove(n1, e); // list: 1 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(2, e) << "wrong element in removed node"; + ASSERT_EQ(1, list.size()) << "size should be zero"; + ASSERT_EQ(false, list.empty()) << "list should be empty"; + ASSERT_EQ(1, list.head()->element) << "head-element should be 1"; + } + + { + // list: 1 + SCOPED_TRACE("remove the last node of a size-3 list"); + n1 = list.head(); // list: 1 + n2 = list.addFront(2); // list: 2->1 + n3 = list.addFront(3); // list: 3->2->1 + + ok = list.remove(n1, e); // remove 1; list: 3->2 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(1, e) << "wrong element in removed node"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + ASSERT_EQ(false, list.empty()) << "list should not be empty"; + + ASSERT_EQ(n3, list.head()) << "wrong pointer at list head"; + ASSERT_EQ(n2, list.head()->next()) << "wrong pointer at list head->next"; + ASSERT_EQ(3, list.head()->element) << "head-element should be 1"; + ASSERT_EQ(2, list.head()->next()->element) << "head-element should be 2"; + + } + + { + // list: 3->2 + SCOPED_TRACE("remove a middle node"); + n3 = list.head(); // list: 3->2 + n2 = n3->next(); // + n4 = list.addFront(4); // list: 4->3->2 + n5 = list.addFront(5); // list: 5->4->3->2 + + + // try to remove 4 + ok = list.remove(n4, e); // remove 4; list: 5->3->2 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(4, e) << "removed element should be 4"; + ASSERT_EQ(3, list.size()) << "size should be 3"; + ASSERT_EQ(false, list.empty()) << "list should not be empty"; + + ASSERT_EQ(n5, list.head()) << "wrong pointer at list head"; + ASSERT_EQ(n3, list.head()->next()) << "wrong pointer at list head->next"; + ASSERT_EQ(n2, list.head()->next()->next()) << "wrong pointer at list head->next->next"; + ASSERT_EQ(list.end().pNode, n2->next()) << "last->next should be end()"; + ASSERT_EQ(5, list.head()->element) << "head-element should be 5"; + ASSERT_EQ(3, list.head()->next()->element) << "head-element should be 3"; + ASSERT_EQ(2, list.head()->next()->next()->element) << "head-element should be 2"; + + // now try to remove 3 + // list: 5->3->2 + ok = list.remove(n3, e); // remove 3; list: 5->2 + ASSERT_EQ(true, ok) << "remove failed"; + ASSERT_EQ(3, e) << "removed element should be 3"; + ASSERT_EQ(2, list.size()) << "size should be 2"; + ASSERT_EQ(false, list.empty()) << "list should not be empty"; + + ASSERT_EQ(n5, list.head()) << "wrong pointer at list head"; + ASSERT_EQ(n2, list.head()->next()) << "wrong pointer at list head->next"; + ASSERT_EQ(list.end().pNode, list.head()->next()->next()) << "last->next should be end()"; + ASSERT_EQ(5, list.head()->element) << "head-element should be 5"; + ASSERT_EQ(2, list.head()->next()->element) << "head-next element should be 2"; + } + } + + void fromArray(){ + IntList list; + int srcArr[] = { 10, 20, 30, 40 }; + int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + ASSERT_NE(0, (int)list.head()) << "head is NULL"; + ASSERT_EQ(4, (int)list.size()) << "size is not 4"; + int j = n - 1; + + for (IntIterator it = list.begin(); it != list.end(); it++ ){ + ASSERT_EQ(srcArr[j], *it) << "element mismatch at item " << n - j - 1 << " in list"; + j--; + } + } + + void elements(){ + IntList list; + int srcArr[] = {10, 20, 30, 40}; + int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + //ASSERT_NE(0, (int)list.head()) << "list head is NULL"; + ASSERT_EQ(n, list.size()) << "list size should have been " << n; + // since the elements are given in reverse order + int* pRev = new int[n]; + arrrev(srcArr, n, &pRev); + ASSERT_NE(0, (int)pRev) << "array reverse failed"; + { + SCOPED_TRACE("elements"); + checkListElements(&list, pRev, n); + } + DELETE_ARR_SAFE(pRev); + } + + void relements(){ + IntList list; + int srcArr[] = { 10, 20, 30, 40 }; + const int n = ARR_LENGTH(srcArr); + + list.fromArray(srcArr, n); // build list + ASSERT_EQ(n, list.size()) << "list size should have been " << n; + + int* pRelems = new int[n]; + list.relements( &pRelems); + // pRelems should be in the same order as srcArr + ASSERT_EQ(true, arrcmp(srcArr, pRelems, n)) << "arrays are not the same" << endl + << "actual: " << arrToString(pRelems, n) << endl + << "expected: " << arrToString(srcArr, n); + DELETE_ARR_SAFE(pRelems); + } + + void toString(){ + int srcArr[] = { 1, 2, 3, 4 }; + const string expected = "4->3->2->1->"; + IntList list(srcArr, ARR_LENGTH(srcArr)); + string actual = list.toString(); + ASSERT_EQ(expected, actual) << "toString() is wrong"; + } + + void before(){ + node_t *n1, *n2, *n3, *n4; + IntList list; + ASSERT_EQ(0, list.size()) << "size should be zero"; + ASSERT_EQ(0, (int)list.head()) << "head should be NULL"; + + // test empty list + n1 = list.before(list.head()); // should be NULL + ASSERT_EQ(NULL, n1) << "n1 should be NULL"; + + // test within list + n1 = list.addFront(1); + n2 = list.addFront(2); + n3 = list.addFront(3); + n4 = list.addFront(4); // list: 4->3->2->1 + + ASSERT_EQ(list.end().pNode, list.before(n4)) << "before(4) not end()"; + ASSERT_EQ(n4, list.before(n3)) << "before(3) not 4"; + ASSERT_EQ(n3, list.before(n2)) << "before(2) not 3"; + ASSERT_EQ(n2, list.before(n1)) << "before(1) not 2"; + + } + +}; + + +ADD_TEST_F(SinglyLinkedListTest, addFront); +ADD_TEST_F(SinglyLinkedListTest, addBefore); +ADD_TEST_F(SinglyLinkedListTest, addAfter); +ADD_TEST_F(SinglyLinkedListTest, before); +ADD_TEST_F(SinglyLinkedListTest, remove); +ADD_TEST_F(SinglyLinkedListTest, head); +ADD_TEST_F(SinglyLinkedListTest, fromArray); +ADD_TEST_F(SinglyLinkedListTest, elements); +ADD_TEST_F(SinglyLinkedListTest, relements); +ADD_TEST_F(SinglyLinkedListTest, toString); +ADD_TEST_F(SinglyLinkedListTest, clear); + +} // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/ds.tree.h b/YASI_12/ds.tree.h new file mode 100644 index 0000000..0e0bbe0 --- /dev/null +++ b/YASI_12/ds.tree.h @@ -0,0 +1,67 @@ +#pragma once +#include "common.h" +#include "ds.node.h" +#include +using namespace std; + +namespace yasi{ + +namespace ds{ + +// tree node interface +//template +//class ITreeNode{ +//public: +// virtual ~ITreeNode(){} +// // virtual E element() const = 0; // returns the element stored at the node +// virtual void setElement(const E& e) = 0; // sets an element +//}; +// tree node class +template +class TreeNode : public NodeBase{ +protected: +public: + + TreeNode(){} + TreeNode(const E& e) : NodeBase(e){} + //E element const{ + // return _element; + //} + //void setElement(const E& e){ + // element = e; + //} + virtual ~TreeNode(){ + + } +}; +// interface for tree +template +class ITree{ +public: + virtual ~ITree(){} + // preorder traversal by default + virtual string toString() const = 0; + virtual int size() const = 0; + virtual bool empty() const = 0; + virtual bool isInternal(const Node*) const = 0; + virtual bool isExternal(const Node*) const = 0; + virtual bool isRoot(const Node*) const = 0; + + virtual bool remove(Node*, E&) = 0; + virtual bool remove(Node*) = 0; + virtual bool removeNode(Node*, E&) = 0; + virtual bool removeNode(Node*) = 0; + virtual Node* root() const = 0; + virtual Node* addRoot(const E&) = 0; + virtual int numChildren(const Node*) const = 0; + + virtual void clear() = 0; + +}; + + + + + +} // namespace ds +} // namespace yasi \ No newline at end of file diff --git a/YASI_12/main.cpp b/YASI_12/main.cpp new file mode 100644 index 0000000..137f34a --- /dev/null +++ b/YASI_12/main.cpp @@ -0,0 +1,67 @@ +#include "common.h" +#include "ds.singlylinkedlist.h" +#include "ds.doublylinkedlist.h" +#include "ds.arraybinarytree.h" +#include "ds.binaryheap.h" +#include "ds.binarytree.h" +#include "ds.binarysearchtree.h" +#include "ds.priorityqueue.h" +#include "ds.BSTDictionary.h" + +//#include "Sorter.h" + +#include +#include +#include +#include +#include +#include +using namespace yasi; +using namespace yasi::ds; +using namespace std; + +void testSort(); + +int main(int argc, char** argv){ + // enable dumping memory leaks + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + + ::testing::InitGoogleTest(&argc, argv); // init google test + + int retVal = RUN_ALL_TESTS(); // run all test cases + +// _CrtDumpMemoryLeaks(); + + cout << endl; + cout << "Press any key..." << endl; + getchar(); + return retVal; + //testSort(); + //testArrayBinaryTree(); +} + + +//void testSort(){ +// Sorter> sorter; +// const int arrSize = 5; +// vector unsortedArr, sortedArr; +// back_insert_iterator> sortedArrBegin(sortedArr); +// // init unsorted arr +// srand((unsigned int) time( NULL)); +// for (int i = 0; i < arrSize; i++){ +// unsortedArr.push_back(rand() % 100); +// } +// +// // now sort +// string algName("Bubble Sort"); +// cout << "Sort using " << algName << "\n"; +// sorter.bubbleSort(unsortedArr, sortedArr ); +// // print unsorted +// cout << "Unsorted: "; +// copy(unsortedArr.begin(), unsortedArr.end(), ostream_iterator(cout, " ")); +// cout << "\n"; +// // print sorted +// cout << "Sorted: "; +// copy(sortedArr.begin(), sortedArr.end(), ostream_iterator(cout, " ")); +// cout << "\n"; +//} \ No newline at end of file diff --git a/YASI_12/stl.iterator.h b/YASI_12/stl.iterator.h new file mode 100644 index 0000000..0ddc060 --- /dev/null +++ b/YASI_12/stl.iterator.h @@ -0,0 +1,968 @@ +/* +* +* Copyright (c) 1994 +* Hewlett-Packard Company +* +* Permission to use, copy, modify, distribute and sell this software +* and its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and +* that both that copyright notice and this permission notice appear +* in supporting documentation. Hewlett-Packard Company makes no +* representations about the suitability of this software for any +* purpose. It is provided "as is" without express or implied warranty. +* +* +* Copyright (c) 1996-1998 +* Silicon Graphics Computer Systems, Inc. +* +* Permission to use, copy, modify, distribute and sell this software +* and its documentation for any purpose is hereby granted without fee, +* provided that the above copyright notice appear in all copies and +* that both that copyright notice and this permission notice appear +* in supporting documentation. Silicon Graphics makes no +* representations about the suitability of this software for any +* purpose. It is provided "as is" without express or implied warranty. +*/ + +/* NOTE: This is an internal header file, included by other STL headers. +* You should not attempt to use it directly. +*/ + +#ifndef __SGI_STL_INTERNAL_ITERATOR_H +#define __SGI_STL_INTERNAL_ITERATOR_H + +__STL_BEGIN_NAMESPACE + + +template +class back_insert_iterator { +protected: + _Container* container; +public: + typedef _Container container_type; + typedef output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + explicit back_insert_iterator(_Container& __x) : container(&__x) {} + back_insert_iterator<_Container>& + operator=(const typename _Container::value_type& __value) { + container->push_back(__value); + return *this; + } + back_insert_iterator<_Container>& operator*() { return *this; } + back_insert_iterator<_Container>& operator++() { return *this; } + back_insert_iterator<_Container>& operator++(int) { return *this; } +}; + +#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION + +template +inline output_iterator_tag +iterator_category(const back_insert_iterator<_Container>&) +{ + return output_iterator_tag(); +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +template +inline back_insert_iterator<_Container> back_inserter(_Container& __x) { + return back_insert_iterator<_Container>(__x); +} + +template +class front_insert_iterator { +protected: + _Container* container; +public: + typedef _Container container_type; + typedef output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + explicit front_insert_iterator(_Container& __x) : container(&__x) {} + front_insert_iterator<_Container>& + operator=(const typename _Container::value_type& __value) { + container->push_front(__value); + return *this; + } + front_insert_iterator<_Container>& operator*() { return *this; } + front_insert_iterator<_Container>& operator++() { return *this; } + front_insert_iterator<_Container>& operator++(int) { return *this; } +}; + +#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION + +template +inline output_iterator_tag +iterator_category(const front_insert_iterator<_Container>&) +{ + return output_iterator_tag(); +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +template +inline front_insert_iterator<_Container> front_inserter(_Container& __x) { + return front_insert_iterator<_Container>(__x); +} + +template +class insert_iterator { +protected: + _Container* container; + typename _Container::iterator iter; +public: + typedef _Container container_type; + typedef output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + insert_iterator(_Container& __x, typename _Container::iterator __i) + : container(&__x), iter(__i) {} + insert_iterator<_Container>& + operator=(const typename _Container::value_type& __value) { + iter = container->insert(iter, __value); + ++iter; + return *this; + } + insert_iterator<_Container>& operator*() { return *this; } + insert_iterator<_Container>& operator++() { return *this; } + insert_iterator<_Container>& operator++(int) { return *this; } +}; + +#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION + +template +inline output_iterator_tag +iterator_category(const insert_iterator<_Container>&) +{ + return output_iterator_tag(); +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +template +inline +insert_iterator<_Container> inserter(_Container& __x, _Iterator __i) +{ + typedef typename _Container::iterator __iter; + return insert_iterator<_Container>(__x, __iter(__i)); +} + +#ifndef __STL_LIMITED_DEFAULT_TEMPLATES +template +#else +template +#endif +class reverse_bidirectional_iterator { + typedef reverse_bidirectional_iterator<_BidirectionalIterator, _Tp, + _Reference, _Distance> _Self; +protected: + _BidirectionalIterator current; +public: + typedef bidirectional_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Tp* pointer; + typedef _Reference reference; + + reverse_bidirectional_iterator() {} + explicit reverse_bidirectional_iterator(_BidirectionalIterator __x) + : current(__x) {} + _BidirectionalIterator base() const { return current; } + _Reference operator*() const { + _BidirectionalIterator __tmp = current; + return *--__tmp; + } +#ifndef __SGI_STL_NO_ARROW_OPERATOR + pointer operator->() const { return &(operator*()); } +#endif /* __SGI_STL_NO_ARROW_OPERATOR */ + _Self& operator++() { + --current; + return *this; + } + _Self operator++(int) { + _Self __tmp = *this; + --current; + return __tmp; + } + _Self& operator--() { + ++current; + return *this; + } + _Self operator--(int) { + _Self __tmp = *this; + ++current; + return __tmp; + } +}; + +#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION + +template + inline bidirectional_iterator_tag + iterator_category(const reverse_bidirectional_iterator<_BidirectionalIterator, + _Tp, _Reference, + _Distance>&) +{ + return bidirectional_iterator_tag(); +} + +template + inline _Tp* + value_type(const reverse_bidirectional_iterator<_BidirectionalIterator, _Tp, + _Reference, _Distance>&) +{ + return (_Tp*)0; +} + +template + inline _Distance* + distance_type(const reverse_bidirectional_iterator<_BidirectionalIterator, + _Tp, + _Reference, _Distance>&) +{ + return (_Distance*)0; +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +template +inline bool operator==( + const reverse_bidirectional_iterator<_BiIter, _Tp, _Ref, _Distance>& __x, + const reverse_bidirectional_iterator<_BiIter, _Tp, _Ref, _Distance>& __y) +{ + return __x.base() == __y.base(); +} + +#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER + +template +inline bool operator!=( + const reverse_bidirectional_iterator<_BiIter, _Tp, _Ref, _Distance>& __x, + const reverse_bidirectional_iterator<_BiIter, _Tp, _Ref, _Distance>& __y) +{ + return !(__x == __y); +} + +#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ + + +#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION + +// This is the new version of reverse_iterator, as defined in the +// draft C++ standard. It relies on the iterator_traits template, +// which in turn relies on partial specialization. The class +// reverse_bidirectional_iterator is no longer part of the draft +// standard, but it is retained for backward compatibility. + +template +class reverse_iterator +{ +protected: + _Iterator current; +public: + typedef typename iterator_traits<_Iterator>::iterator_category + iterator_category; + typedef typename iterator_traits<_Iterator>::value_type + value_type; + typedef typename iterator_traits<_Iterator>::difference_type + difference_type; + typedef typename iterator_traits<_Iterator>::pointer + pointer; + typedef typename iterator_traits<_Iterator>::reference + reference; + + typedef _Iterator iterator_type; + typedef reverse_iterator<_Iterator> _Self; + +public: + reverse_iterator() {} + explicit reverse_iterator(iterator_type __x) : current(__x) {} + + reverse_iterator(const _Self& __x) : current(__x.current) {} +#ifdef __STL_MEMBER_TEMPLATES + template + reverse_iterator(const reverse_iterator<_Iter>& __x) + : current(__x.base()) {} +#endif /* __STL_MEMBER_TEMPLATES */ + + iterator_type base() const { return current; } + reference operator*() const { + _Iterator __tmp = current; + return *--__tmp; + } +#ifndef __SGI_STL_NO_ARROW_OPERATOR + pointer operator->() const { return &(operator*()); } +#endif /* __SGI_STL_NO_ARROW_OPERATOR */ + + _Self& operator++() { + --current; + return *this; + } + _Self operator++(int) { + _Self __tmp = *this; + --current; + return __tmp; + } + _Self& operator--() { + ++current; + return *this; + } + _Self operator--(int) { + _Self __tmp = *this; + ++current; + return __tmp; + } + + _Self operator+(difference_type __n) const { + return _Self(current - __n); + } + _Self& operator+=(difference_type __n) { + current -= __n; + return *this; + } + _Self operator-(difference_type __n) const { + return _Self(current + __n); + } + _Self& operator-=(difference_type __n) { + current += __n; + return *this; + } + reference operator[](difference_type __n) const { return *(*this + __n); } +}; + +template +inline bool operator==(const reverse_iterator<_Iterator>& __x, + const reverse_iterator<_Iterator>& __y) { + return __x.base() == __y.base(); +} + +template +inline bool operator<(const reverse_iterator<_Iterator>& __x, + const reverse_iterator<_Iterator>& __y) { + return __y.base() < __x.base(); +} + +#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER + +template +inline bool operator!=(const reverse_iterator<_Iterator>& __x, + const reverse_iterator<_Iterator>& __y) { + return !(__x == __y); +} + +template +inline bool operator>(const reverse_iterator<_Iterator>& __x, + const reverse_iterator<_Iterator>& __y) { + return __y < __x; +} + +template +inline bool operator<=(const reverse_iterator<_Iterator>& __x, + const reverse_iterator<_Iterator>& __y) { + return !(__y < __x); +} + +template +inline bool operator>=(const reverse_iterator<_Iterator>& __x, + const reverse_iterator<_Iterator>& __y) { + return !(__x < __y); +} + +#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ + +template +inline typename reverse_iterator<_Iterator>::difference_type +operator-(const reverse_iterator<_Iterator>& __x, +const reverse_iterator<_Iterator>& __y) { + return __y.base() - __x.base(); +} + +template +inline reverse_iterator<_Iterator> +operator+(typename reverse_iterator<_Iterator>::difference_type __n, +const reverse_iterator<_Iterator>& __x) { + return reverse_iterator<_Iterator>(__x.base() - __n); +} + +#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +// This is the old version of reverse_iterator, as found in the original +// HP STL. It does not use partial specialization. + +#ifndef __STL_LIMITED_DEFAULT_TEMPLATES +template +#else +template +#endif +class reverse_iterator { + typedef reverse_iterator<_RandomAccessIterator, _Tp, _Reference, _Distance> + _Self; +protected: + _RandomAccessIterator current; +public: + typedef random_access_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Tp* pointer; + typedef _Reference reference; + + reverse_iterator() {} + explicit reverse_iterator(_RandomAccessIterator __x) : current(__x) {} + _RandomAccessIterator base() const { return current; } + _Reference operator*() const { return *(current - 1); } +#ifndef __SGI_STL_NO_ARROW_OPERATOR + pointer operator->() const { return &(operator*()); } +#endif /* __SGI_STL_NO_ARROW_OPERATOR */ + _Self& operator++() { + --current; + return *this; + } + _Self operator++(int) { + _Self __tmp = *this; + --current; + return __tmp; + } + _Self& operator--() { + ++current; + return *this; + } + _Self operator--(int) { + _Self __tmp = *this; + ++current; + return __tmp; + } + _Self operator+(_Distance __n) const { + return _Self(current - __n); + } + _Self& operator+=(_Distance __n) { + current -= __n; + return *this; + } + _Self operator-(_Distance __n) const { + return _Self(current + __n); + } + _Self& operator-=(_Distance __n) { + current += __n; + return *this; + } + _Reference operator[](_Distance __n) const { return *(*this + __n); } +}; + +template + inline random_access_iterator_tag + iterator_category(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>&) +{ + return random_access_iterator_tag(); +} + +template + inline _Tp* value_type(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>&) +{ + return (_Tp*)0; +} + +template + inline _Distance* + distance_type(const reverse_iterator<_RandomAccessIterator, + _Tp, _Reference, _Distance>&) +{ + return (_Distance*)0; +} + + +template + inline bool + operator==(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) +{ + return __x.base() == __y.base(); +} + +template + inline bool + operator<(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) +{ + return __y.base() < __x.base(); +} + +#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER + +template + inline bool + operator!=(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) { + return !(__x == __y); +} + +template + inline bool + operator>(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) { + return __y < __x; +} + +template + inline bool + operator<=(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) { + return !(__y < __x); +} + +template + inline bool + operator>=(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) { + return !(__x < __y); +} + +#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ + +template + inline _Distance + operator-(const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __x, + const reverse_iterator<_RandomAccessIterator, _Tp, + _Reference, _Distance>& __y) +{ + return __y.base() - __x.base(); +} + +template +inline reverse_iterator<_RandAccIter, _Tp, _Ref, _Dist> +operator+(_Dist __n, +const reverse_iterator<_RandAccIter, _Tp, _Ref, _Dist>& __x) +{ + return reverse_iterator<_RandAccIter, _Tp, _Ref, _Dist>(__x.base() - __n); +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +// istream_iterator and ostream_iterator look very different if we're +// using new, templatized iostreams than if we're using the old cfront +// version. + +#ifdef __STL_USE_NEW_IOSTREAMS + +template , +class _Dist = ptrdiff_t> +class istream_iterator { +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef basic_istream<_CharT, _Traits> istream_type; + + typedef input_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Dist difference_type; + typedef const _Tp* pointer; + typedef const _Tp& reference; + + istream_iterator() : _M_stream(0), _M_ok(false) {} + istream_iterator(istream_type& __s) : _M_stream(&__s) { _M_read(); } + + reference operator*() const { return _M_value; } + pointer operator->() const { return &(operator*()); } + + istream_iterator& operator++() { + _M_read(); + return *this; + } + istream_iterator operator++(int) { + istream_iterator __tmp = *this; + _M_read(); + return __tmp; + } + + bool _M_equal(const istream_iterator& __x) const + { + return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); + } + +private: + istream_type* _M_stream; + _Tp _M_value; + bool _M_ok; + + void _M_read() { + _M_ok = (_M_stream && *_M_stream) ? true : false; + if (_M_ok) { + *_M_stream >> _M_value; + _M_ok = *_M_stream ? true : false; + } + } +}; + +template +inline bool +operator==(const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __x, +const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __y) { + return __x._M_equal(__y); +} + +#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER + +template +inline bool +operator!=(const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __x, +const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __y) { + return !__x._M_equal(__y); +} + +#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ + +template > +class ostream_iterator { +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef basic_ostream<_CharT, _Traits> ostream_type; + + typedef output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + ostream_iterator(ostream_type& __s) : _M_stream(&__s), _M_string(0) {} + ostream_iterator(ostream_type& __s, const _CharT* __c) + : _M_stream(&__s), _M_string(__c) {} + ostream_iterator<_Tp>& operator=(const _Tp& __value) { + *_M_stream << __value; + if (_M_string) *_M_stream << _M_string; + return *this; + } + ostream_iterator<_Tp>& operator*() { return *this; } + ostream_iterator<_Tp>& operator++() { return *this; } + ostream_iterator<_Tp>& operator++(int) { return *this; } +private: + ostream_type* _M_stream; + const _CharT* _M_string; +}; + +// The default template argument is declared in iosfwd + +// We do not read any characters until operator* is called. The first +// time operator* is called, it calls getc. Subsequent calls to getc +// return a cached character, and calls to operator++ use snextc. Before +// operator* or operator++ has been called, _M_is_initialized is false. +template +class istreambuf_iterator + : public iterator +{ +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef typename _Traits::int_type int_type; + typedef basic_streambuf<_CharT, _Traits> streambuf_type; + typedef basic_istream<_CharT, _Traits> istream_type; + +public: + istreambuf_iterator(streambuf_type* __p = 0) { this->_M_init(__p); } + istreambuf_iterator(istream_type& __is) { this->_M_init(__is.rdbuf()); } + + char_type operator*() const + { + return _M_is_initialized ? _M_c : _M_dereference_aux(); + } + + istreambuf_iterator& operator++() { this->_M_nextc(); return *this; } + istreambuf_iterator operator++(int) { + if (!_M_is_initialized) + _M_postincr_aux(); + istreambuf_iterator __tmp = *this; + this->_M_nextc(); + return __tmp; + } + + bool equal(const istreambuf_iterator& __i) const { + return this->_M_is_initialized && __i._M_is_initialized + ? this->_M_eof == __i._M_eof + : this->_M_equal_aux(__i); + } + +private: + void _M_init(streambuf_type* __p) { + _M_buf = __p; + _M_eof = !__p; + _M_is_initialized = _M_eof; + } + + char_type _M_dereference_aux() const; + bool _M_equal_aux(const istreambuf_iterator&) const; + void _M_postincr_aux(); + + void _M_nextc() { + int_type __c = _M_buf->snextc(); + _M_c = traits_type::to_char_type(__c); + _M_eof = traits_type::eq_int_type(__c, traits_type::eof()); + _M_is_initialized = true; + } + + void _M_getc() const { + int_type __c = _M_buf->sgetc(); + _M_c = traits_type::to_char_type(__c); + _M_eof = traits_type::eq_int_type(__c, traits_type::eof()); + _M_is_initialized = true; + } + +private: + streambuf_type* _M_buf; + mutable _CharT _M_c; + mutable bool _M_eof : 1; + mutable bool _M_is_initialized : 1; +}; + +template +_CharT istreambuf_iterator<_CharT, _Traits>::_M_dereference_aux() const +{ + this->_M_getc(); + return _M_c; +} + +template +bool istreambuf_iterator<_CharT, _Traits> +::_M_equal_aux(const istreambuf_iterator& __i) const +{ + if (!this->_M_is_initialized) + this->_M_getc(); + if (!__i._M_is_initialized) + __i._M_getc(); + + return this->_M_eof == __i._M_eof; +} + +template +void istreambuf_iterator<_CharT, _Traits>::_M_postincr_aux() +{ + this->_M_getc(); +} + +template +inline bool operator==(const istreambuf_iterator<_CharT, _Traits>& __x, + const istreambuf_iterator<_CharT, _Traits>& __y) { + return __x.equal(__y); +} + +#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER + +template +inline bool operator!=(const istreambuf_iterator<_CharT, _Traits>& __x, + const istreambuf_iterator<_CharT, _Traits>& __y) { + return !__x.equal(__y); +} + +#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ + +// The default template argument is declared in iosfwd +template +class ostreambuf_iterator + : public iterator +{ +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef typename _Traits::int_type int_type; + typedef basic_streambuf<_CharT, _Traits> streambuf_type; + typedef basic_ostream<_CharT, _Traits> ostream_type; + +public: + ostreambuf_iterator(streambuf_type* __buf) : _M_buf(__buf), _M_ok(__buf) {} + ostreambuf_iterator(ostream_type& __o) + : _M_buf(__o.rdbuf()), _M_ok(__o.rdbuf() != 0) {} + + ostreambuf_iterator& operator=(char_type __c) { + _M_ok = _M_ok && !traits_type::eq_int_type(_M_buf->sputc(__c), + traits_type::eof()); + return *this; + } + + ostreambuf_iterator& operator*() { return *this; } + ostreambuf_iterator& operator++() { return *this; } + ostreambuf_iterator& operator++(int) { return *this; } + + bool failed() const { return !_M_ok; } + +private: + streambuf_type* _M_buf; + bool _M_ok; +}; + +#else /* __STL_USE_NEW_IOSTREAMS */ + +template class istream_iterator; + +template +inline bool operator==(const istream_iterator<_Tp, _Dist>&, + const istream_iterator<_Tp, _Dist>&); + +template +class istream_iterator { +#ifdef __STL_TEMPLATE_FRIENDS + template + friend bool operator==(const istream_iterator<_T1, _D1>&, + const istream_iterator<_T1, _D1>&); +#else /* __STL_TEMPLATE_FRIENDS */ + friend bool __STD_QUALIFIER + operator== __STL_NULL_TMPL_ARGS(const istream_iterator&, + const istream_iterator&); +#endif /* __STL_TEMPLATE_FRIENDS */ + +protected: + istream* _M_stream; + _Tp _M_value; + bool _M_end_marker; + void _M_read() { + _M_end_marker = (*_M_stream) ? true : false; + if (_M_end_marker) *_M_stream >> _M_value; + _M_end_marker = (*_M_stream) ? true : false; + } +public: + typedef input_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Dist difference_type; + typedef const _Tp* pointer; + typedef const _Tp& reference; + + istream_iterator() : _M_stream(&cin), _M_end_marker(false) {} + istream_iterator(istream& __s) : _M_stream(&__s) { _M_read(); } + reference operator*() const { return _M_value; } +#ifndef __SGI_STL_NO_ARROW_OPERATOR + pointer operator->() const { return &(operator*()); } +#endif /* __SGI_STL_NO_ARROW_OPERATOR */ + istream_iterator<_Tp, _Dist>& operator++() { + _M_read(); + return *this; + } + istream_iterator<_Tp, _Dist> operator++(int) { + istream_iterator<_Tp, _Dist> __tmp = *this; + _M_read(); + return __tmp; + } +}; + +#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION + +template +inline input_iterator_tag +iterator_category(const istream_iterator<_Tp, _Dist>&) +{ + return input_iterator_tag(); +} + +template +inline _Tp* +value_type(const istream_iterator<_Tp, _Dist>&) { return (_Tp*)0; } + +template +inline _Dist* +distance_type(const istream_iterator<_Tp, _Dist>&) { return (_Dist*)0; } + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +template +inline bool operator==(const istream_iterator<_Tp, _Distance>& __x, + const istream_iterator<_Tp, _Distance>& __y) { + return (__x._M_stream == __y._M_stream && + __x._M_end_marker == __y._M_end_marker) || + __x._M_end_marker == false && __y._M_end_marker == false; +} + +#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER + +template +inline bool operator!=(const istream_iterator<_Tp, _Distance>& __x, + const istream_iterator<_Tp, _Distance>& __y) { + return !(__x == __y); +} + +#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ + +template +class ostream_iterator { +protected: + ostream* _M_stream; + const char* _M_string; +public: + typedef output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + ostream_iterator(ostream& __s) : _M_stream(&__s), _M_string(0) {} + ostream_iterator(ostream& __s, const char* __c) + : _M_stream(&__s), _M_string(__c) {} + ostream_iterator<_Tp>& operator=(const _Tp& __value) { + *_M_stream << __value; + if (_M_string) *_M_stream << _M_string; + return *this; + } + ostream_iterator<_Tp>& operator*() { return *this; } + ostream_iterator<_Tp>& operator++() { return *this; } + ostream_iterator<_Tp>& operator++(int) { return *this; } +}; + +#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION + +template +inline output_iterator_tag +iterator_category(const ostream_iterator<_Tp>&) { + return output_iterator_tag(); +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +#endif /* __STL_USE_NEW_IOSTREAMS */ + +__STL_END_NAMESPACE + +#endif /* __SGI_STL_INTERNAL_ITERATOR_H */ + +// Local Variables: +// mode:C++ +// End: \ No newline at end of file diff --git a/YASI_12/test.h b/YASI_12/test.h new file mode 100644 index 0000000..c73e708 --- /dev/null +++ b/YASI_12/test.h @@ -0,0 +1,27 @@ +#pragma once +#include "gtest\gtest.h" + +namespace yasi{ + + // shorthand for adding a testcase in a test fixture + #define ADD_TEST_F(testcase, test) TEST_F(testcase, test){ test(); } + + class Test : public ::testing::Test{ + protected: + _CrtMemState t1, t2, t3; + + virtual void setUp(){ + _CrtMemCheckpoint(&t1); + } + virtual void tearDown(){ + _CrtMemCheckpoint(&t2); + if (_CrtMemDifference(&t3, &t1, &t2)) { + // _CrtMemDumpStatistics(&t3); + // _CrtDumpMemoryLeaks(); + _CrtMemDumpAllObjectsSince(&t1); + } + } + public: + virtual ~Test(){} + }; +} diff --git a/YASI_12/utils.cpp b/YASI_12/utils.cpp new file mode 100644 index 0000000..e69de29 diff --git a/YASI_12/utils.h b/YASI_12/utils.h new file mode 100644 index 0000000..a43d40f --- /dev/null +++ b/YASI_12/utils.h @@ -0,0 +1,172 @@ +#pragma once +#include "common.h" +#include +using namespace std; + +namespace yasi{ + +// compares two arrays + template + bool arrcmp(const T* pArr1, const T* pArr2, const int numElems) { + if (pArr1 && pArr2 && (numElems > 0)){ + for (int i = 0; i < numElems; i++){ + if (pArr1[i] != pArr2[i]) return false; + } + return true; + } + else + return false; + } + + template + void arrrev(const T* pSrcArr, const int numElems, T** pDestArr) { + if (pSrcArr && (numElems > 0)){ + for (int i = 0; i < numElems; i++){ + (*pDestArr)[numElems - i - 1] = pSrcArr[i]; + } + } + } + + template +string arrToString(const T* pArr, const int numElems){ + stringstream buf; + if (pArr == NULL || numElems <= 0){ + return ""; + } + buf << "{"; + for (int i = 0; i < numElems; i++){ + buf << pArr[i]; + if (i < numElems - 1) + buf << ","; + } + buf << "}"; + return buf.str(); +} + +// removes certain chars from a string +string strPurge(const string str, const char* removeChars){ + string trimmed = ""; + int numChars = strlen(removeChars); + if (numChars == 0) return str; + + for (int i = 0; i < str.length(); i++){ + bool good = true; + // try every bad char + for (int j = 0; j < numChars; j++){ + char c = removeChars[j]; + if (str[i] == c) { + // match with a bad char + good = false; + break; + } + } + // still good + if ( good ) + trimmed += str[i]; + } + return trimmed; +} + +///////////// enable in compile-time a certain code block depending on boolean predicate ///////////// +///////////// also, check if a certain class contains a certain function /////////////////////////////// +//////////////////////// see http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence#264088 +////////////////////////////////////////////////////////////////////////////// +template +struct enable_if { + typedef T type; +}; + +template +struct enable_if { }; + +//#define HAS_MEM_FUNC(func, structName ) \ +// template \ +// struct structName { \ +// typedef char yes[1]; \ +// typedef char no [2]; \ +// template struct type_check; \ +// template static yes &chk(type_check *); \ +// template static no &chk(...); \ +// static bool const value = sizeof(chk(0)) == sizeof(yes); \ +// } + +//// usage: check if a tempalte class T contains the toString() method +/* +HAS_MEM_FUNC(toString, has_to_string); + +template +typename enable_if::value, std::string>::type + doSomething(T * t) { + // something when T has toString ... + return t->toString(); +} + +template +typename enable_if::value, std::string>::type + doSomething(T * t) { + // something when T doesnt have toString ... + return "T::toString() does not exist."; +} + +*/ + +// test + +class UtilsTest : public Test{ +public: + void arrrev(){ + // regular + int srcArr[] = { 10, 20, 30 }; // odd number of entries + int destArr[] = { 30, 20, 10 }; + int* pRev = new int[3]; + yasi::arrrev(srcArr, 3, &pRev); + ASSERT_NE(0, (int)pRev) << "reversed array is NULL"; + for (int i = 0; i < 3; i++){ + ASSERT_EQ(destArr[i], pRev[i]) << "element mismatch at reverse index " << i; + } + DELETE_ARR_SAFE(pRev); + + int srcArr2[] = { 10, 20, 30, 40 }; // even number of entries + int destArr2[] = { 40, 30, 20, 10 }; + pRev = new int[4]; + yasi::arrrev(srcArr2, 4, &pRev); + ASSERT_NE(0, (int)pRev) << "reversed array is NULL"; + for (int i = 0; i < 4; i++){ + ASSERT_EQ(destArr2[i], pRev[i]) << "element mismatch at reverse index " << i; + } + DELETE_ARR_SAFE(pRev); + + // bad input + int* pNull = NULL; + yasi::arrrev(pNull, 3, &pRev); + ASSERT_EQ(0, (int)pRev) << "worked on a NULL pointer"; + yasi::arrrev(srcArr, 0, &pRev); + ASSERT_EQ(0, (int)pRev) << "worked on zero size"; + DELETE_ARR_SAFE(pRev); + yasi::arrrev(srcArr, -1, &pRev); + ASSERT_EQ(0, (int)pRev) << "worked on negativesize"; + //DELETE_ARR_SAFE(pRev); + } + void strPurge(){ + string str = "abedabecaebc"; + + // purge chars present + string str2 = yasi::strPurge(str, "de"); + ASSERT_EQ("ababcabc", str2) << "failed"; + + // purge chars absent + str2 = yasi::strPurge(str, "x"); + ASSERT_EQ(str, str2) << "failed"; + + // no chars given + str2 = yasi::strPurge(str, ""); + ASSERT_EQ(str, str2) << "failed"; + } +}; + +ADD_TEST_F(UtilsTest, arrrev); +ADD_TEST_F(UtilsTest, strPurge); + +} // namespace yasi \ No newline at end of file