diff --git a/CMakeLists.txt b/CMakeLists.txt index bc11aebd..169476c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,7 @@ SET (REACTPHYSICS3D_SOURCES "src/containers/Stack.h" "src/containers/LinkedList.h" "src/containers/List.h" + "src/containers/Map.h" ) # Create the library diff --git a/src/containers/List.h b/src/containers/List.h index 176766a5..6391d52b 100644 --- a/src/containers/List.h +++ b/src/containers/List.h @@ -30,6 +30,7 @@ #include "configuration.h" #include "memory/MemoryAllocator.h" #include +#include namespace reactphysics3d { @@ -61,6 +62,102 @@ class List { public: + /// Class Iterator + /** + * This class represents an iterator for the List + */ + class Iterator { + + private: + + size_t mCurrentIndex; + T* mBuffer; + size_t mSize; + + public: + + // Iterator traits + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + using iterator_category = std::bidirectional_iterator_tag; + + /// Constructor + Iterator() = default; + + /// Constructor + Iterator(T* buffer, size_t index, size_t size) + :mCurrentIndex(index), mBuffer(buffer), mSize(size) { + + } + + /// Copy constructor + Iterator(const Iterator& it) + :mCurrentIndex(it.mCurrentIndex), mBuffer(it.mBuffer), mSize(it.size) { + + } + + /// Deferencable + reference operator*() const { + assert(mCurrentIndex >= 0 && mCurrentIndex < mSize); + return mBuffer[mCurrentIndex]; + } + + /// Deferencable + pointer operator->() const { + assert(mCurrentIndex >= 0 && mCurrentIndex < mSize); + return &(mBuffer[mCurrentIndex]); + } + + /// Post increment (it++) + Iterator& operator++() { + assert(mCurrentIndex < mSize - 1); + mCurrentIndex++; + return *this; + } + + /// Pre increment (++it) + Iterator operator++(int number) { + assert(mCurrentIndex < mSize - 1); + Iterator tmp = *this; + mCurrentIndex++; + return tmp; + } + + /// Post decrement (it--) + Iterator& operator--() { + assert(mCurrentIndex > 0); + mCurrentIndex--; + return *this; + } + + /// Pre decrement (--it) + Iterator operator--(int number) { + assert(mCurrentIndex > 0); + Iterator tmp = *this; + mCurrentIndex--; + return tmp; + } + + /// Equality operator (it == end()) + bool operator==(const Iterator& iterator) const { + assert(mCurrentIndex >= 0 && mCurrentIndex <= mSize); + + // If both iterators points to the end of the list + if (mCurrentIndex == mSize && iterator.mCurrentIndex == iterator.mSize) { + return true; + } + + return &(mBuffer[mCurrentIndex]) == &(iterator.mBuffer[mCurrentIndex]); + } + + /// Inequality operator (it != end()) + bool operator!=(const Iterator& iterator) const { + return !(*this == iterator); + } + }; + // -------------------- Methods -------------------- // /// Constructor @@ -151,7 +248,7 @@ class List { } } - /// Append another list to the current one + /// Append another list at the end of the current one void addRange(const List& list) { // If we need to allocate more memory @@ -180,7 +277,7 @@ class List { mSize = 0; } - /// Return the number of elments in the list + /// Return the number of elements in the list size_t size() const { return mSize; } @@ -202,14 +299,38 @@ class List { return (static_cast(mBuffer)[index]); } + /// Overloaded equality operator + bool operator==(const List& list) const { + + if (mSize != list.mSize) return false; + + T* items = static_cast(mBuffer); + for (int i=0; i < mSize; i++) { + if (items[i] != list[i]) { + return false; + } + } + + return true; + } + + /// Overloaded not equal operator + bool operator!=(const List& list) const { + + return !((*this) == list); + } + /// Overloaded assignment operator List& operator=(const List& list) { - // Clear all the elements - clear(); + if (this != &list) { - // Add all the elements of the list to the current one - addRange(list); + // Clear all the elements + clear(); + + // Add all the elements of the list to the current one + addRange(list); + } return *this; } diff --git a/src/containers/Map.h b/src/containers/Map.h new file mode 100644 index 00000000..5510b98d --- /dev/null +++ b/src/containers/Map.h @@ -0,0 +1,532 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2016 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_MAP_H +#define REACTPHYSICS3D_MAP_H + +// Libraries +#include "memory/MemoryAllocator.h" +#include "mathematics/mathematics_functions.h" +#include +#include + +namespace reactphysics3d { + +// Class Map +/** + * This class represents a simple generic associative map + */ +template +class Map { + + private: + + /// An entry of the map + struct Entry { + + size_t hashCode; // Hash code of the entry + int next; // Index of the next entry + std::pair* keyValue; // Pointer to the pair with key and value + + /// Constructor + Entry() { + next = -1; + keyValue = nullptr; + } + + /// Destructor + ~Entry() { + + assert(keyValue == nullptr); + } + + }; + + // -------------------- Constants -------------------- // + + /// Number of prime numbers in array + static constexpr int NB_PRIMES = 70; + + /// Array of prime numbers for the size of the map + static const int PRIMES[NB_PRIMES]; + + /// Largest prime number + static int LARGEST_PRIME; + + // -------------------- Attributes -------------------- // + + /// Current number of used entries in the map + int mNbUsedEntries; + + /// Number of free entries among the used ones + int mNbFreeEntries; + + /// Current capacity of the map + int mCapacity; + + /// Array with all the buckets + int* mBuckets; + + /// Array with all the entries + Entry* mEntries; + + /// Memory allocator + MemoryAllocator& mAllocator; + + /// Index to the fist free entry + int mFreeIndex; + + // -------------------- Methods -------------------- // + + /// Initialize the map + void initialize(int capacity) { + + // Compute the next larger prime size + mCapacity = getPrimeSize(capacity); + + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + + // Initialize the buckets and entries + for (int i=0; i mCapacity); + assert(isPrimeNumber(newCapacity)); + + // Allocate memory for the buckets + int* newBuckets = static_cast(mAllocator.allocate(newCapacity * sizeof(int))); + + // Allocate memory for the entries + Entry* newEntries = static_cast(mAllocator.allocate(newCapacity * sizeof(Entry))); + + // Initialize the new buckets + for (int i=0; i(&newEntries[i])) Entry(); + } + + // For each used entry + for (int i=0; i 0) { + + size_t hashCode = std::hash()(key); + int bucket = hashCode % mCapacity; + + for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + if (mEntries[i].hashCode == hashCode && mEntries[i].keyValue->first == key) { + return i; + } + } + } + + return -1; + } + + /// Return the prime number that is larger or equal to the number in parameter + /// for the size of the map + static int getPrimeSize(int number) { + + // Check if the next larger prime number is in the precomputed array of primes + for (int i = 0; i < NB_PRIMES; i++) { + if (PRIMES[i] >= number) return PRIMES[i]; + } + + // Manually compute the next larger prime number + for (int i = (number | 1); i < std::numeric_limits::max(); i+=2) { + + if (isPrimeNumber(i)) { + return i; + } + } + + return number; + } + + /// Clear and reset the map + void reset() { + + // If elements have been allocated + if (mCapacity > 0) { + + // Clear the list + clear(); + + // Destroy the entries + for (int i=0; i < mCapacity; i++) { + mEntries[i].~Entry(); + } + + mAllocator.release(mBuckets, mCapacity * sizeof(int)); + mAllocator.release(mEntries, mCapacity * sizeof(Entry)); + + mNbUsedEntries = 0; + mNbFreeEntries = 0; + mCapacity = 0; + mBuckets = nullptr; + mEntries = nullptr; + mFreeIndex = -1; + } + } + + public: + + // -------------------- Methods -------------------- // + + /// Constructor + Map(MemoryAllocator& allocator, size_t capacity = 0) + : mNbUsedEntries(0), mNbFreeEntries(0), mCapacity(0), mBuckets(nullptr), + mEntries(nullptr), mAllocator(allocator), mFreeIndex(-1) { + + // If the largest prime has not been computed yet + if (LARGEST_PRIME == -1) { + + // Compute the largest prime number (largest map capacity) + LARGEST_PRIME = getPrimeSize(PRIMES[NB_PRIMES - 1] + 2); + } + + if (capacity > 0) { + + initialize(capacity); + } + } + + /// Copy constructor + Map(const Map& map) + :mNbUsedEntries(map.mNbUsedEntries), mNbFreeEntries(map.mNbFreeEntries), mCapacity(map.mCapacity), + mAllocator(map.mAllocator), mFreeIndex(map.mFreeIndex) { + + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + + // Copy the buckets + std::memcpy(mBuckets, map.mBuckets, mCapacity * sizeof(int)); + + // Copy the entries + std::memcpy(mEntries, map.mEntries, mCapacity * sizeof(Entry)); + } + + /// Destructor + ~Map() { + + reset(); + } + + /// Allocate memory for a given number of elements + void reserve(size_t capacity) { + + if (capacity <= mCapacity) return; + + if (capacity > LARGEST_PRIME && LARGEST_PRIME > mCapacity) { + capacity = LARGEST_PRIME; + } + else { + capacity = getPrimeSize(capacity); + } + + expand(capacity); + } + + /// Return true if the map contains an item with the given key + bool containsKey(const K& key) const { + return findEntry(key) != -1; + } + + /// Add an element into the map + void add(const std::pair& keyValue) { + + if (mCapacity == 0) { + initialize(0); + } + + // Compute the hash code of the key + size_t hashCode = std::hash()(keyValue.first); + + // Compute the corresponding bucket index + int bucket = hashCode % mCapacity; + + // Check if the item is already in the map + for (int i = mBuckets[bucket]; i >= 0; i = mEntries[i].next) { + + // If there is already an item with the same key in the map + if (mEntries[i].hashCode == hashCode && mEntries[i].keyValue->first == keyValue.first) { + + throw std::runtime_error("The key and value pair already exists in the map"); + } + } + + size_t entryIndex; + + // If there are free entries to use + if (mNbFreeEntries > 0) { + assert(mFreeIndex >= 0); + entryIndex = mFreeIndex; + mFreeIndex = mEntries[entryIndex].next; + mNbFreeEntries--; + } + else { + + // If we need to allocator more entries + if (mNbUsedEntries == mCapacity) { + + // Allocate more memory + reserve(mCapacity * 2); + + // Recompute the bucket index + bucket = hashCode % mCapacity; + } + + entryIndex = mNbUsedEntries; + mNbUsedEntries++; + } + + assert(mEntries[entryIndex].keyValue == nullptr); + mEntries[entryIndex].hashCode = hashCode; + mEntries[entryIndex].next = mBuckets[bucket]; + mEntries[entryIndex].keyValue = static_cast*>(mAllocator.allocate(sizeof(std::pair))); + assert(mEntries[entryIndex].keyValue != nullptr); + new (mEntries[entryIndex].keyValue) std::pair(keyValue); + mBuckets[bucket] = entryIndex; + } + + /// Remove the element from the map with a given key + bool remove(const K& key) { + + if (mCapacity > 0) { + + size_t hashcode = std::hash()(key); + int bucket = hashcode % mCapacity; + int last = -1; + for (int i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].next) { + + if (mEntries[i].hashCode == hashcode && mEntries[i].keyValue->first == key) { + + if (last < 0 ) { + mBuckets[bucket] = mEntries[i].next; + } + else { + mEntries[last].next = mEntries[i].next; + } + + // Release memory for the key/value pair if any + if (mEntries[i].keyValue != nullptr) { + mEntries[i].keyValue->~pair(); + mAllocator.release(mEntries[i].keyValue, sizeof(std::pair)); + mEntries[i].keyValue = nullptr; + } + mEntries[i].hashCode = -1; + mEntries[i].next = mFreeIndex; + mFreeIndex = i; + mNbFreeEntries++; + + return true; + } + } + } + + return false; + } + + /// Clear the list + void clear() { + + if (mNbUsedEntries > 0) { + + for (int i=0; i < mCapacity; i++) { + mBuckets[i] = -1; + mEntries[i].next = -1; + if (mEntries[i].keyValue != nullptr) { + mEntries[i].keyValue->~pair(); + mAllocator.release(mEntries[i].keyValue, sizeof(std::pair)); + mEntries[i].keyValue = nullptr; + } + } + + mFreeIndex = -1; + mNbUsedEntries = 0; + mNbFreeEntries = 0; + } + + assert(size() == 0); + } + + /// Return the number of elements in the map + int size() const { + return mNbUsedEntries - mNbFreeEntries; + } + + /// Return the capacity of the map + int capacity() const { + return mCapacity; + } + + /// Overloaded index operator + V& operator[](const K& key) { + + int entry = -1; + + if (mCapacity > 0) { + entry = findEntry(key); + } + + if (entry == -1) { + throw std::runtime_error("No item with given key has been found in the map"); + } + + assert(mEntries[entry].keyValue != nullptr); + + return mEntries[entry].keyValue->second; + } + + /// Overloaded index operator + const V& operator[](const K& key) const { + + int entry = -1; + + if (mCapacity > 0) { + entry = findEntry(key); + } + + if (entry == -1) { + throw std::runtime_error("No item with given key has been found in the map"); + } + + return mEntries[entry]; + } + + /// Overloaded equality operator + bool operator==(const Map& map) const { + + // TODO : Implement this + return false; + } + + /// Overloaded not equal operator + bool operator!=(const Map& map) const { + + return !((*this) == map); + } + + /// Overloaded assignment operator + Map& operator=(const Map& map) { + + // Check for self assignment + if (this != &map) { + + // Reset the map + reset(); + + if (map.mCapacity > 0) { + + // Compute the next larger prime size + mCapacity = getPrimeSize(map.mCapacity); + + // Allocate memory for the buckets + mBuckets = static_cast(mAllocator.allocate(mCapacity * sizeof(int))); + + // Allocate memory for the entries + mEntries = static_cast(mAllocator.allocate(mCapacity * sizeof(Entry))); + + // Copy the buckets + std::memcpy(mBuckets, map.mBuckets, mCapacity * sizeof(int)); + + // Copy the entries + std::memcpy(mEntries, map.mEntries, mCapacity * sizeof(Entry)); + + mNbUsedEntries = map.mNbUsedEntries; + mNbFreeEntries = map.mNbFreeEntries; + mFreeIndex = map.mFreeIndex; + } + } + + return *this; + } +}; + +template +const int Map::PRIMES[NB_PRIMES] = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559}; + +template +int Map::LARGEST_PRIME = -1; + +} + +#endif diff --git a/src/mathematics/mathematics_functions.cpp b/src/mathematics/mathematics_functions.cpp index 9fa13083..7e4a1427 100755 --- a/src/mathematics/mathematics_functions.cpp +++ b/src/mathematics/mathematics_functions.cpp @@ -383,4 +383,28 @@ Vector3 reactphysics3d::projectPointOntoPlane(const Vector3& point, const Vector return point - unitPlaneNormal.dot(point - planePoint) * unitPlaneNormal; } +// Return true if the given number is prime +bool reactphysics3d::isPrimeNumber(int number) { + + // If it's a odd number + if ((number & 1) != 0) { + + int limit = static_cast(std::sqrt(number)); + + for (int divisor = 3; divisor <= limit; divisor += 2) { + + // If we have found a divisor + if ((number % divisor) == 0) { + + // It is not a prime number + return false; + } + } + + return true; + } + + return number == 2; +} + diff --git a/src/mathematics/mathematics_functions.h b/src/mathematics/mathematics_functions.h index eda0fdd3..a72b0077 100755 --- a/src/mathematics/mathematics_functions.h +++ b/src/mathematics/mathematics_functions.h @@ -124,6 +124,9 @@ List clipPolygonWithPlanes(const List& polygonVertices, const /// Project a point onto a plane that is given by a point and its unit length normal Vector3 projectPointOntoPlane(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint); +/// Return true if the given number is prime +bool isPrimeNumber(int number); + } diff --git a/test/main.cpp b/test/main.cpp index 26d62803..8bb62ea4 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -39,6 +39,8 @@ #include "tests/collision/TestDynamicAABBTree.h" #include "tests/collision/TestHalfEdgeStructure.h" #include "tests/collision/TestTriangleVertexArray.h" +#include "tests/containers/TestList.h" +#include "tests/containers/TestMap.h" using namespace reactphysics3d; @@ -46,6 +48,11 @@ int main() { TestSuite testSuite("ReactPhysics3D Tests"); + // ---------- Containers tests ---------- // + + testSuite.addTest(new TestList("List")); + testSuite.addTest(new TestMap("Map")); + // ---------- Mathematics tests ---------- // testSuite.addTest(new TestVector2("Vector2")); diff --git a/test/tests/containers/TestList.h b/test/tests/containers/TestList.h new file mode 100644 index 00000000..ba1fbf61 --- /dev/null +++ b/test/tests/containers/TestList.h @@ -0,0 +1,331 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2016 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef TEST_LIST_H +#define TEST_LIST_H + +// Libraries +#include "Test.h" +#include "containers/List.h" +#include "memory/DefaultAllocator.h" + +/// Reactphysics3D namespace +namespace reactphysics3d { + +// Class TestList +/** + * Unit test for the List class + */ +class TestList : public Test { + + private : + + // ---------- Atributes ---------- // + + DefaultAllocator mAllocator; + + public : + + // ---------- Methods ---------- // + + /// Constructor + TestList(const std::string& name) : Test(name) { + + } + + /// Run the tests + void run() { + + testConstructors(); + testAddRemoveClear(); + testAssignment(); + testIndexing(); + testEquality(); + testReserve(); + testIteration(); + } + + void testConstructors() { + + // ----- Constructors ----- // + + List list1(mAllocator); + test(list1.capacity() == 0); + test(list1.size() == 0); + + List list2(mAllocator, 100); + test(list2.capacity() == 100); + test(list2.size() == 0); + + List list3(mAllocator); + list3.add(1); + list3.add(2); + list3.add(3); + test(list3.capacity() == 4); + test(list3.size() == 3); + + // ----- Copy Constructors ----- // + + List list4(list1); + test(list4.capacity() == 0); + test(list4.size() == 0); + + List list5(list3); + test(list5.capacity() == list3.size()); + test(list5.size() == list3.size()); + for (uint i=0; i list6(mAllocator, 20); + test(list6.capacity() == 20); + for (uint i=0; i<20; i++) { + list6.add("test"); + } + test(list6.capacity() == 20); + list6.add("test"); + test(list6.capacity() == 40); + } + + void testAddRemoveClear() { + + // ----- Test add() ----- // + + List list1(mAllocator); + list1.add(4); + test(list1.size() == 1); + test(list1[0] == 4); + list1.add(9); + test(list1.size() == 2); + test(list1[0] == 4); + test(list1[1] == 9); + + const int arraySize = 15; + int arrayTest[arraySize] = {3, 145, -182, 34, 12, 95, -1834, 4143, -111, -111, 4343, 234, 22983, -3432, 753}; + List list2(mAllocator); + for (uint i=0; i list3(mAllocator); + list3.add(1); + list3.add(2); + list3.add(3); + list3.add(4); + + list3.remove(3); + test(list3.size() == 3); + test(list3.capacity() == 4); + test(list3[0] = 1); + test(list3[1] = 2); + test(list3[2] = 3); + + list3.remove(1); + test(list3.size() == 2); + test(list3.capacity() == 4); + test(list3[0] = 1); + test(list3[1] = 3); + + list3.remove(0); + test(list3.size() == 1); + test(list3.capacity() == 4); + test(list3[0] = 3); + + list3.remove(0); + test(list3.size() == 0); + test(list3.capacity() == 4); + + // ----- Test addRange() ----- // + + List list4(mAllocator); + list4.add(1); + list4.add(2); + list4.add(3); + + List list5(mAllocator); + list5.add(4); + list5.add(5); + + List list6(mAllocator); + list6.addRange(list5); + test(list6.size() == list5.size()); + test(list6[0] == 4); + test(list6[1] == 5); + + list4.addRange(list5); + test(list4.size() == 3 + list5.size()); + test(list4[0] == 1); + test(list4[1] == 2); + test(list4[2] == 3); + test(list4[3] == 4); + test(list4[4] == 5); + + // ----- Test clear() ----- // + + List list7(mAllocator); + list7.add("test1"); + list7.add("test2"); + list7.add("test3"); + list7.clear(); + test(list7.size() == 0); + list7.add("new"); + test(list7.size() == 1); + test(list7[0] == "new"); + } + + void testAssignment() { + + List list1(mAllocator); + list1.add(1); + list1.add(2); + list1.add(3); + + List list2(mAllocator); + list2.add(5); + list2.add(6); + + List list3(mAllocator); + List list4(mAllocator); + list4.add(1); + list4.add(2); + + List list5(mAllocator); + list5.add(1); + list5.add(2); + list5.add(3); + + list3 = list2; + test(list2.size() == list3.size()); + test(list2[0] == list3[0]); + test(list2[1] == list3[1]); + + list4 = list1; + test(list4.size() == list1.size()) + test(list4[0] = list1[0]); + test(list4[1] = list1[1]); + test(list4[2] = list1[2]); + + list5 = list2; + test(list5.size() == list2.size()); + test(list5[0] = list2[0]); + test(list5[1] = list2[1]); + } + + void testIndexing() { + + List list1(mAllocator); + list1.add(1); + list1.add(2); + list1.add(3); + + test(list1[0] == 1); + test(list1[1] == 2); + test(list1[2] == 3); + + list1[0] = 6; + list1[1] = 7; + list1[2] = 8; + + test(list1[0] == 6); + test(list1[1] == 7); + test(list1[2] == 8); + + const int a = list1[0]; + const int b = list1[1]; + test(a == 6); + test(b == 7); + + list1[0]++; + list1[1]++; + test(list1[0] == 7); + test(list1[1] == 8); + } + + void testEquality() { + + List list1(mAllocator); + list1.add(1); + list1.add(2); + list1.add(3); + + List list2(mAllocator); + list2.add(1); + list2.add(2); + + List list3(mAllocator); + list3.add(1); + list3.add(2); + list3.add(3); + + List list4(mAllocator); + list4.add(1); + list4.add(5); + list4.add(3); + + test(list1 == list1); + test(list1 != list2); + test(list1 == list3); + test(list1 != list4); + test(list2 != list4); + } + + void testReserve() { + + List list1(mAllocator); + list1.reserve(10); + test(list1.size() == 0); + test(list1.capacity() == 10); + list1.add(1); + list1.add(2); + test(list1.capacity() == 10); + test(list1.size() == 2); + test(list1[0] == 1); + test(list1[1] == 2); + + list1.reserve(1); + test(list1.capacity() == 10); + + list1.reserve(100); + test(list1.capacity() == 100); + test(list1[0] == 1); + test(list1[1] == 2); + } + + void testIteration() { + // TODO : Implement this + } + + }; + +} + +#endif diff --git a/test/tests/containers/TestMap.h b/test/tests/containers/TestMap.h new file mode 100644 index 00000000..d7e084dc --- /dev/null +++ b/test/tests/containers/TestMap.h @@ -0,0 +1,319 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2016 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef TEST_MAP_H +#define TEST_MAP_H + +// Libraries +#include "Test.h" +#include "containers/Map.h" +#include "memory/DefaultAllocator.h" + +/// Reactphysics3D namespace +namespace reactphysics3d { + +// Class TestMap +/** + * Unit test for the Map class + */ +class TestMap : public Test { + + private : + + // ---------- Atributes ---------- // + + DefaultAllocator mAllocator; + + public : + + // ---------- Methods ---------- // + + /// Constructor + TestMap(const std::string& name) : Test(name) { + + } + + /// Run the tests + void run() { + + testConstructors(); + testReserve(); + testAddRemoveClear(); + testContainsKey(); + testIndexing(); + testEquality(); + testAssignment(); + testIteration(); + } + + void testConstructors() { + + // ----- Constructors ----- // + + Map map1(mAllocator); + test(map1.capacity() == 0); + test(map1.size() == 0); + + Map map2(mAllocator, 100); + test(map2.capacity() >= 100); + test(map2.size() == 0); + + // ----- Copy Constructors ----- // +/* + Map map3(map1); + test(map3.capacity() == map1.capacity()); + test(map3.size() == map1.size()); + + Map map4(mAllocator); + map4.add(std::make_pair(1, 10)); + map4.add(std::make_pair(2, 20)); + map4.add(std::make_pair(3, 30)); + test(map4.capacity() >= 3); + test(map4.size() == 3); + + Map map5(map4); + test(map5.capacity() == map4.capacity()); + test(map5.size() == map4.size()); + test(map5[1] == 10); + test(map5[2] == 20); + test(map5[3] == 30); + */ + } + + void testReserve() { + + Map map1(mAllocator); + map1.reserve(15); + test(map1.capacity() >= 15); + map1.add(std::make_pair(1, "test1")); + map1.add(std::make_pair(2, "test2")); + test(map1.capacity() >= 15); + + map1.reserve(10); + test(map1.capacity() >= 15); + + map1.reserve(100); + test(map1.capacity() >= 100); + test(map1[1] == "test1"); + test(map1[2] == "test2"); + } + + void testAddRemoveClear() { + + // ----- Test add() ----- // + + Map map1(mAllocator); + map1.add(std::make_pair(1, 10)); + map1.add(std::make_pair(8, 80)); + map1.add(std::make_pair(13, 130)); + test(map1[1] == 10); + test(map1[8] == 80); + test(map1[13] == 130); + test(map1.size() == 3); + + Map map2(mAllocator, 15); + for (int i = 0; i < 1000000; i++) { + map2.add(std::make_pair(i, i * 100)); + } + bool isValid = true; + for (int i = 0; i < 1000000; i++) { + if (map2[i] != i * 100) isValid = false; + } + test(isValid); + + map1.remove(1); + map1.add(std::make_pair(1, 10)); + test(map1.size() == 3); + test(map1[1] == 10); + + // ----- Test remove() ----- // + + map1.remove(1); + test(!map1.containsKey(1)); + test(map1.containsKey(8)); + test(map1.containsKey(13)); + test(map1.size() == 2); + + map1.remove(13); + test(!map1.containsKey(8)); + test(map1.containsKey(13)); + test(map1.size() == 1); + + map1.remove(8); + test(!map1.containsKey(8)); + test(map1.size() == 0); + + isValid = true; + for (int i = 0; i < 1000000; i++) { + map2.remove(i); + } + for (int i = 0; i < 1000000; i++) { + if (map2.containsKey(i)) isValid = false; + } + test(isValid); + test(map2.size() == 0); + + Map map3(mAllocator); + for (int i=0; i < 1000000; i++) { + map3.add(std::make_pair(i, i * 10)); + map3.remove(i); + } + + // ----- Test clear() ----- // + + Map map4(mAllocator); + map4.add(std::make_pair(2, 20)); + map4.add(std::make_pair(4, 40)); + map4.add(std::make_pair(6, 60)); + map4.clear(); + test(map4.size() == 0); + map4.add(std::make_pair(2, 20)); + test(map4.size() == 1); + test(map4[2] == 20); + map4.clear(); + test(map4.size() == 0); + + Map map5(mAllocator); + map5.clear(); + test(map5.size() == 0); + } + + void testContainsKey() { + + Map map1(mAllocator); + + test(!map1.containsKey(2)); + test(!map1.containsKey(4)); + test(!map1.containsKey(6)); + + map1.add(std::make_pair(2, 20)); + map1.add(std::make_pair(4, 40)); + map1.add(std::make_pair(6, 60)); + + test(map1.containsKey(2)); + test(map1.containsKey(4)); + test(map1.containsKey(6)); + + map1.remove(4); + test(!map1.containsKey(4)); + test(map1.containsKey(2)); + test(map1.containsKey(6)); + + map1.clear(); + test(!map1.containsKey(2)); + test(!map1.containsKey(6)); + } + + void testIndexing() { + + Map map1(mAllocator); + map1.add(std::make_pair(2, 20)); + map1.add(std::make_pair(4, 40)); + map1.add(std::make_pair(6, 60)); + test(map1[2] == 20); + test(map1[4] == 40); + test(map1[6] == 60); + + map1[2] = 10; + map1[4] = 20; + map1[6] = 30; + + test(map1[2] == 10); + test(map1[4] == 20); + test(map1[6] == 30); + } + + void testEquality() { + + Map map1(mAllocator, 10); + Map map2(mAllocator, 2); + + test(map1 == map2); + + map1.add(std::make_pair("a", 1)); + map1.add(std::make_pair("b", 2)); + map1.add(std::make_pair("c", 3)); + + map2.add(std::make_pair("a", 1)); + map2.add(std::make_pair("b", 2)); + map2.add(std::make_pair("c", 4)); + + test(map1 == map1); + test(map2 == map2); + test(map1 != map2); + + map2["c"] = 3; + + test(map1 == map2); + + Map map3(mAllocator); + map3.add(std::make_pair("a", 1)); + + test(map1 != map3); + test(map2 != map3); + } + + void testAssignment() { + + Map map1(mAllocator); + map1.add(std::make_pair(1, 3)); + map1.add(std::make_pair(2, 6)); + map1.add(std::make_pair(10, 30)); +/* + Map map2(mAllocator); + map2 = map1; + test(map2.size() == map1.size()); + test(map2[1] == 3); + test(map2[2] == 6); + test(map2[10] == 30); + + Map map3(mAllocator, 100); + map3 = map1; + test(map3.size() == map1.size()); + test(map3[1] == 3); + test(map3[2] == 6); + test(map3[10] == 30); + + Map map4(mAllocator); + map3 = map4; + test(map3.size() == 0); +*/ + Map map5(mAllocator); + map5.add(std::make_pair(7, 8)); + map5.add(std::make_pair(19, 70)); + map1 = map5; + test(map5.size() == map1.size()); + test(map1[7] == 8); + test(map1[19] == 70); + } + + void testIteration() { + + } + }; + +} + +#endif diff --git a/test/tests/mathematics/TestTransform.h b/test/tests/mathematics/TestTransform.h index 581b9c5e..815e3d54 100644 --- a/test/tests/mathematics/TestTransform.h +++ b/test/tests/mathematics/TestTransform.h @@ -61,13 +61,16 @@ class TestTransform : public Test { mIdentityTransform.setToIdentity(); - decimal sinA = sin(PI/8.0f); - decimal cosA = cos(PI/8.0f); - mTransform1 = Transform(Vector3(4, 5, 6), Quaternion(sinA, sinA, sinA, cosA)); + Vector3 unitVec(1, 1, 1); + unitVec.normalize(); - decimal sinB = sin(PI/3.0f); - decimal cosB = cos(PI/3.0f); - mTransform2 = Transform(Vector3(8, 45, -6), Quaternion(sinB, sinB, sinB, cosB)); + decimal sinA = std::sin(PI/8.0f); + decimal cosA = std::cos(PI/8.0f); + mTransform1 = Transform(Vector3(4, 5, 6), Quaternion(sinA * unitVec, cosA)); + + decimal sinB = std::sin(PI/3.0f); + decimal cosB = std::cos(PI/3.0f); + mTransform2 = Transform(Vector3(8, 45, -6), Quaternion(sinB * unitVec, cosB)); } /// Run the tests