Browse Source

Added an Algorithm API to the framework, which is useful for writing functional regression tests with less code.

David Piuva 2 years ago
parent
commit
c7066ae7fa
3 changed files with 149 additions and 5 deletions
  1. 139 0
      Source/DFPSR/api/algorithmAPI.h
  2. 4 5
      Source/DFPSR/includeFramework.h
  3. 6 0
      Source/test/tests/ListTest.cpp

+ 139 - 0
Source/DFPSR/api/algorithmAPI.h

@@ -0,0 +1,139 @@
+// zlib open source license
+//
+// Copyright (c) 2023 David Forsgren Piuva
+// 
+// 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 DFPSR_API_ALGORITHM
+#define DFPSR_API_ALGORITHM
+
+// TODO: Split Algorithm into smaller APIs for specific types and only have the methods that need more than one collection type in the exposed API.
+//       This allow using the algorithm API for a specific type without bloating the dependencies with all the types and their functions.
+#include "../collection/List.h"
+#include "../collection/Array.h"
+#include "../collection/Field.h"
+
+namespace dsr {
+
+// Returns true iff a and b are equal in length and content according to T's equality operator.
+template<typename T>
+bool operator==(const List<T>& a, const List<T>& b) {
+	if (a.length() != b.length()) return false;
+	for (int64_t i = 0; i < a.length(); i++) {
+		if (!(a[i] == b[i])) return false;
+	}
+	return true;
+}
+
+// Returns true iff a and b are equal in length and content according to T's equality operator.
+template<typename T>
+bool operator==(const Array<T>& a, const Array<T>& b) {
+	if (a.length() != b.length()) return false;
+	for (int64_t i = 0; i < a.length(); i++) {
+		if (!(a[i] == b[i])) return false;
+	}
+	return true;
+}
+
+// Returns true iff a and b have the same dimensions and content according to T's equality operator.
+template<typename T>
+bool operator==(const Field<T>& a, const Field<T>& b) {
+	if (a.width() != b.width()
+	 || a.height() != b.height()) return false;
+	for (int64_t y = 0; y < a.height(); y++) {
+		for (int64_t x = 0; x < a.width(); x++) {
+			if (!(a.unsafe_readAccess(IVector2D(x, y)) == b.unsafe_readAccess(IVector2D(x, y)))) return false;
+		}
+	}
+	return true;
+}
+
+// Returns false iff a and b are equal in length and content according to T's equality operator.
+template<typename T> bool operator!=(const List<T>& a, const List<T>& b) { return !(a == b); }
+
+// Returns false iff a and b are equal in length and content according to T's equality operator.
+template<typename T> bool operator!=(const Array<T>& a, const Array<T>& b) { return !(a == b); }
+
+// Returns false iff a and b have the same dimensions and content according to T's equality operator.
+template<typename T> bool operator!=(const Field<T>& a, const Field<T>& b) { return !(a == b); }
+
+// Internal helper function
+template<typename T>
+String& print_collection_1D_multiline(String& target, const T& collection, const ReadableString& indentation) {
+	string_append(target, indentation, U"{\n");
+	int64_t maxIndex = collection.length() - 1;
+	for (int64_t i = 0; i <= maxIndex; i++) {
+		string_toStreamIndented(target, collection[i], indentation + "\t");
+		if (i < maxIndex) {
+			string_append(target, U",");
+		}
+		string_append(target, U"\n");
+	}
+	string_append(target, indentation, U"}");
+	return target;
+}
+
+// Printing a generic List of elements for easy debugging.
+//   A new line is used after each element, because the element type might print using multiple lines and the list might be very long.
+//   No new line at the end, because the caller might want to add a comma before breaking the line.
+//   If string_toStreamIndented is defined for lists of a specific element type, this template function will be overridden by the more specific function.
+template<typename T>
+String& string_toStreamIndented(String& target, const List<T>& collection, const ReadableString& indentation) {
+	return print_collection_1D_multiline(target, collection, indentation);
+}
+
+// Printing a generic Array of elements for easy debugging, using the same syntax as when printing List.
+template<typename T>
+String& string_toStreamIndented(String& target, const Array<T>& collection, const ReadableString& indentation) {
+	return print_collection_1D_multiline(target, collection, indentation);
+}
+
+// Printing a generic Field of elements for easy debugging.
+template<typename T>
+String& string_toStreamIndented(String& target, const Field<T>& collection, const ReadableString& indentation) {
+	string_append(target, indentation, U"{\n");
+	int64_t maxX = collection.width() - 1;
+	int64_t maxY = collection.height() - 1;
+	for (int64_t y = 0; y <= maxY; y++) {
+		string_append(target, indentation, U"\t{");
+		for (int64_t x = 0; x <= maxX; x++) {
+			string_toStreamIndented(target, collection.unsafe_readAccess(IVector2D(x, y)), indentation + "\t\t");
+			if (x < maxX) {
+				string_append(target, U",");
+			}
+			string_append(target, U"\n");
+		}
+		if (y < maxY) {
+			// Comma separate rows on a separate line, to avoid having too much indentation.
+			string_append(target, U"\n", indentation, U"\t,");
+		}
+		string_append(target, U"\n", indentation, U"\t{\n");
+	}
+	string_append(target, indentation, U"}");
+	return target;
+}
+
+// TODO: Implement functional sort, concatenation, union, search and conversion algorithms for List, Array and Field.
+//       in a separate "algorithm" API that does not have to be exposed in headers when using the collection types.
+
+}
+
+#endif
+

+ 4 - 5
Source/DFPSR/includeFramework.h

@@ -7,10 +7,8 @@
 	#include "includeEssentials.h"
 
 	// Types needed to use the APIs
-	#include "math/includeMath.h"
-
-	// Additional functionality for convenience (not to be used in any API)
-	#include "collection/includeCollection.h" // Safer and easier to use than std collections
+	#include "math/includeMath.h" // Mathematical types
+	#include "collection/includeCollection.h" // Generic collections
 
 	// 2D API
 	#include "api/imageAPI.h" // Creating images and modifying pixels
@@ -23,10 +21,11 @@
 	#include "api/mediaMachineAPI.h" // A machine for running image functions
 	#include "api/fontAPI.h" // Printing text to images
 	// Convenient API
+	#include "api/algorithmAPI.h" // Functions for performing operations on whole collections
 	#include "api/timeAPI.h" // Methods for time and delays
 	#include "api/configAPI.h" // Making it easy to load your application's settings from configuration files
 
 	// TODO: Create more APIs
-	#include "gui/VisualTheme.h" // Place in the gui API
+	#include "gui/VisualTheme.h" // An unfinished system for changing how the GUI looks
 
 #endif

+ 6 - 0
Source/test/tests/ListTest.cpp

@@ -31,25 +31,30 @@ START_TEST(List)
 		ASSERT_EQUAL(myStrings[3], U"list");
 		ASSERT_EQUAL(myStrings.first(), U"is");
 		ASSERT_EQUAL(myStrings.last(), U"list");
+		ASSERT_EQUAL(myStrings, List<String>(U"is", U"this", U"a", U"list"));
 		myStrings.swap(0, 1);
 		ASSERT_EQUAL(myStrings.length(), 4);
 		ASSERT_EQUAL(myStrings[0], U"this");
 		ASSERT_EQUAL(myStrings[1], U"is");
 		ASSERT_EQUAL(myStrings[2], U"a");
 		ASSERT_EQUAL(myStrings[3], U"list");
+		ASSERT_EQUAL(myStrings, List<String>(U"this", U"is", U"a", U"list"));
 		List<String> myOtherStrings = myStrings;
 		myStrings.remove(1);
 		ASSERT_EQUAL(myStrings.length(), 3);
 		ASSERT_EQUAL(myStrings[0], U"this");
 		ASSERT_EQUAL(myStrings[1], U"a");
 		ASSERT_EQUAL(myStrings[2], U"list");
+		ASSERT_EQUAL(myStrings, List<String>(U"this", U"a", U"list"));
 		myStrings.remove(0);
 		ASSERT_EQUAL(myStrings.length(), 2);
 		ASSERT_EQUAL(myStrings[0], U"a");
 		ASSERT_EQUAL(myStrings[1], U"list");
+		ASSERT_EQUAL(myStrings, List<String>(U"a", U"list"));
 		myStrings.pop();
 		ASSERT_EQUAL(myStrings.length(), 1);
 		ASSERT_EQUAL(myStrings[0], U"a");
+		ASSERT_EQUAL(myStrings, List<String>(U"a"));
 		myStrings.clear();
 		ASSERT_EQUAL(myStrings.length(), 0);
 		ASSERT_EQUAL(myOtherStrings.length(), 4);
@@ -57,6 +62,7 @@ START_TEST(List)
 		ASSERT_EQUAL(myOtherStrings[1], U"is");
 		ASSERT_EQUAL(myOtherStrings[2], U"a");
 		ASSERT_EQUAL(myOtherStrings[3], U"list");
+		ASSERT_EQUAL(myOtherStrings, List<String>(U"this", U"is", U"a", U"list"));
 		myOtherStrings.clear();
 		ASSERT_EQUAL(myOtherStrings.length(), 0);
 	}