Skip to content

Commit be4570e

Browse files
committed
Added essential algorithms for functional programming, which will make it easier to assert correctness in tests where performance is not critical.
1 parent 5587f6a commit be4570e

File tree

2 files changed

+355
-3
lines changed

2 files changed

+355
-3
lines changed

Source/DFPSR/api/algorithmAPI_List.h

Lines changed: 282 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace dsr {
3030

3131
// Returns true iff a and b are equal in length and content according to T's equality operator.
3232
template<typename T>
33-
bool operator==(const List<T>& a, const List<T>& b) {
33+
static bool operator==(const List<T>& a, const List<T>& b) {
3434
if (a.length() != b.length()) return false;
3535
for (intptr_t i = 0; i < a.length(); i++) {
3636
if (!(a[i] == b[i])) return false;
@@ -39,14 +39,14 @@ bool operator==(const List<T>& a, const List<T>& b) {
3939
}
4040

4141
// Returns false iff a and b are equal in length and content according to T's equality operator.
42-
template<typename T> bool operator!=(const List<T>& a, const List<T>& b) { return !(a == b); }
42+
template<typename T> static bool operator!=(const List<T>& a, const List<T>& b) { return !(a == b); }
4343

4444
// Printing a generic List of elements for easy debugging.
4545
// A new line is used after each element, because the element type might print using multiple lines and the list might be very long.
4646
// No new line at the end, because the caller might want to add a comma before breaking the line.
4747
// If string_toStreamIndented is defined for lists of a specific element type, this template function will be overridden by the more specific function.
4848
template<typename T>
49-
String& string_toStreamIndented(String& target, const List<T>& collection, const ReadableString& indentation) {
49+
static String& string_toStreamIndented(String& target, const List<T>& collection, const ReadableString& indentation) {
5050
string_append(target, indentation, U"{\n");
5151
intptr_t maxIndex = collection.length() - 1;
5252
for (intptr_t i = 0; i <= maxIndex; i++) {
@@ -60,6 +60,285 @@ String& string_toStreamIndented(String& target, const List<T>& collection, const
6060
return target;
6161
}
6262

63+
template <typename OUTPUT_TYPE, typename INPUT_TYPE>
64+
List<OUTPUT_TYPE> list_map(const List<INPUT_TYPE> &input, const TemporaryCallback<OUTPUT_TYPE(const INPUT_TYPE &element)> &f) {
65+
List<OUTPUT_TYPE> result;
66+
result.reserve(input.length());
67+
for (intptr_t e = 0; e < input.length(); e++) {
68+
result.push(f(input[e]));
69+
}
70+
return result;
71+
}
72+
73+
// TODO: Implement list_find_all, returning a list with indices to matching elements, using both == and a custom condition.
74+
75+
// Returns an index to the first element in list matching find, or -1 if none could be found.
76+
template <typename T>
77+
static intptr_t list_findFirst(const dsr::List<T> &list, const T &find) {
78+
for (intptr_t e = 0; e < list.length(); e++) {
79+
if (list[e] == find) {
80+
return e;
81+
}
82+
}
83+
return -1;
84+
}
85+
86+
// Returns an index to the first element in list where condition returns true, or -1 if the condition returned false for all elements.
87+
template <typename T>
88+
static intptr_t list_findFirst(const dsr::List<T> &list, const TemporaryCallback<bool(const T &element)> &condition) {
89+
for (intptr_t e = 0; e < list.length(); e++) {
90+
if (condition(list[e])) {
91+
return e;
92+
}
93+
}
94+
return -1;
95+
}
96+
97+
// Returns an index to the last element in list matching find, or -1 if none could be found.
98+
template <typename T>
99+
static intptr_t list_findLast(const dsr::List<T> &list, const T &find) {
100+
for (intptr_t e = list.length() - 1; e >= 0; e--) {
101+
if (list[e] == find) {
102+
return e;
103+
}
104+
}
105+
return -1;
106+
}
107+
108+
// Returns an index to the last element in list where condition returns true, or -1 if the condition returned false for all elements.
109+
template <typename T>
110+
static intptr_t list_findLast(const dsr::List<T> &list, const TemporaryCallback<bool(const T &element)> &condition) {
111+
for (intptr_t e = list.length() - 1; e >= 0; e--) {
112+
if (condition(list[e])) {
113+
return e;
114+
}
115+
}
116+
return -1;
117+
}
118+
119+
// Returns true iff find matches any element in list.
120+
template <typename T>
121+
static bool list_elementExists(const dsr::List<T> &list, const T &find) {
122+
return list_findFirst(list, find) != -1;
123+
}
124+
125+
// Returns true iff condition is satisfied for any element in list.
126+
template <typename T>
127+
static bool list_elementExists(const dsr::List<T> &list, const TemporaryCallback<bool(const T &element)> &condition) {
128+
return list_findFirst(list, condition) != -1;
129+
}
130+
131+
// Returns true iff find does not exist in list.
132+
template <typename T>
133+
static bool list_elementIsMissing(const dsr::List<T> &list, const T &find) {
134+
return list_findFirst(list, find) == -1;
135+
}
136+
137+
// Returns true iff condition is not satisfied for any element in list.
138+
template <typename T>
139+
static bool list_elementIsMissing(const dsr::List<T> &list, const TemporaryCallback<bool(const T &element)> &condition) {
140+
return list_findFirst(list, condition) == -1;
141+
}
142+
143+
// Inserts a single element at the end of targetList.
144+
// Just a simple wrapper over the push operation to allow keeping the style consistent.
145+
template <typename T>
146+
inline static void list_insert_last(dsr::List<T> &targetList, const T &element) {
147+
targetList.push(element);
148+
}
149+
150+
// TODO: Take the sorting order as a function argument.
151+
// TODO: Document
152+
template <typename T>
153+
static void list_insert_sorted_ascending(dsr::List<T> &targetList, const T &element) {
154+
targetList.push(element);
155+
intptr_t at = targetList.length() - 1;
156+
while (at > 0 && targetList[at] < targetList[at - 1]) {
157+
targetList.swap(at, at - 1);
158+
at--;
159+
}
160+
}
161+
162+
// TODO: Document
163+
template <typename T>
164+
static void list_append_last(dsr::List<T> &targetList, const dsr::List<T> &sourceList) {
165+
for (intptr_t e = 0; e < sourceList.length(); e++) {
166+
list_insert_last(targetList, sourceList[e]);
167+
}
168+
}
169+
170+
// TODO: Document
171+
template <typename T>
172+
static void list_append_sorted_ascending(dsr::List<T> &targetList, const dsr::List<T> &sourceList) {
173+
for (intptr_t e = 0; e < sourceList.length(); e++) {
174+
list_insert_sorted_ascending(targetList, sourceList[e]);
175+
}
176+
}
177+
178+
// TODO: Replace _last and _sorted with a template argument that can be passed from list_insertUnion.
179+
// TODO: Take a function for equality.
180+
// Pre-conditions:
181+
// All elements in targetList must be unique, or else they will remain duplicated.
182+
// Pushes element to targetList and return true iff list_elementIsMissing.
183+
template <typename T>
184+
static bool list_insertUnique_last(dsr::List<T> &targetList, const T &element) {
185+
if (list_elementIsMissing(targetList, element)) {
186+
targetList.push(element);
187+
return true;
188+
} else {
189+
return false;
190+
}
191+
}
192+
193+
// TODO: Take functions for both equality and sorting.
194+
// TODO: Assert that the original list is sorted in debug mode.
195+
// Pre-conditions:
196+
// All elements in targetList must be unique, or else they will remain duplicated.
197+
// targetList must be sorted in ascending order.
198+
// Pushes element to a sorted location in targetList and return true iff list_elementIsMissing.
199+
// Pre-condition; targetList is sorted according to the < operator when beginning the call.
200+
// Side-effect: targetList will remain sorted if it was sorted from the start.
201+
template <typename T>
202+
static bool list_insertUnique_sorted_ascending(dsr::List<T> &targetList, const T &element) {
203+
if (list_elementIsMissing(targetList, element)) {
204+
targetList.push(element);
205+
intptr_t at = targetList.length() - 1;
206+
while (at > 0 && targetList[at] < targetList[at - 1]) {
207+
targetList.swap(at, at - 1);
208+
at--;
209+
}
210+
return true;
211+
} else {
212+
return false;
213+
}
214+
}
215+
216+
// TODO: Having insert union without append is a bit strange, so implement a basic append function as well.
217+
218+
// TODO: Create a varargs version starting with nothing and adding unique elements from all lists before returning by value, so that duplicates in the first list are also reduced.
219+
// TODO: Take a function for equality.
220+
// Pre-conditions:
221+
// All elements in targetList must be unique, or else they will remain duplicated.
222+
// targetList and sourceList may not refer to the same list.
223+
// Pushes all elements in sourceList that does not already exist in targetList.
224+
// Returns true iff any element was pushed to targetList.
225+
template <typename T>
226+
static bool list_insertUnion_last(dsr::List<T> &targetList, const dsr::List<T> &sourceList) {
227+
bool result = false;
228+
for (intptr_t e = 0; e < sourceList.length(); e++) {
229+
// Must store the result in a new variable to avoid lazy evaluation with side-effects.
230+
bool newResult = list_insertUnique_last(targetList, sourceList[e]);
231+
result = result || newResult;
232+
}
233+
return result;
234+
}
235+
236+
// TODO: Create a varargs version starting with nothing and adding unique elements from all lists before returning by value, so that duplicates in the first list are also reduced.
237+
// TODO: Take functions for both equality and sorting.
238+
// TODO: Assert that the original list is sorted in debug mode.
239+
// Pre-conditions:
240+
// All elements in targetList must be unique, or else they will remain duplicated.
241+
// targetList must be sorted in ascending order.
242+
// targetList and sourceList may not refer to the same list.
243+
// Pushes all elements in sourceList that does not already exist in targetList.
244+
// Returns true iff any element was pushed to targetList.
245+
template <typename T>
246+
static bool list_insertUnion_sorted_ascending(dsr::List<T> &targetList, const dsr::List<T> &sourceList) {
247+
bool result = false;
248+
for (intptr_t e = 0; e < sourceList.length(); e++) {
249+
// Must store the result in a new variable to avoid lazy evaluation with side-effects.
250+
bool newResult = list_insertUnique_sorted_ascending(targetList, sourceList[e]);
251+
result = result || newResult;
252+
}
253+
return result;
254+
}
255+
256+
// Helper function for heapSort.
257+
template <typename T>
258+
static void impl_list_heapify(dsr::List<T>& targetList, intptr_t n, intptr_t i, const TemporaryCallback<bool(const T &leftSide, const T &rightSide)> &compare) {
259+
intptr_t largest = i;
260+
intptr_t l = 2 * i + 1;
261+
intptr_t r = 2 * i + 2;
262+
if (l < n && !compare(targetList[l], targetList[largest])) {
263+
largest = l;
264+
}
265+
if (r < n && !compare(targetList[r], targetList[largest])) {
266+
largest = r;
267+
}
268+
if (largest != i) {
269+
targetList.swap(i, largest);
270+
impl_list_heapify(targetList, n, largest, compare);
271+
}
272+
}
273+
274+
// Apply the heap-sort algorithm to targetList.
275+
// The compare function should return true when leftSide and rightSide are sorted correctly.
276+
// Pre-condition:
277+
// The compare function must return true when leftSide and rightSide are equal, because elements in the list might be identical.
278+
// Side-effects:
279+
// Overwrites the input with the result, by sorting it in-place.
280+
// The elements returned by reference in targetList is a permutation of the original elements,
281+
// where each neighboring pair of elements satisfy the compare condition.
282+
template <typename T>
283+
static void list_heapSort(List<T>& targetList, const TemporaryCallback<bool(const T &leftSide, const T &rightSide)> &compare) {
284+
intptr_t n = targetList.length();
285+
for (intptr_t i = n / 2 - 1; i >= 0; i--) {
286+
dsr::impl_list_heapify(targetList, n, i, compare);
287+
}
288+
for (intptr_t i = n - 1; i > 0; i--) {
289+
targetList.swap(0, i);
290+
dsr::impl_list_heapify(targetList, i, 0, compare);
291+
}
292+
}
293+
294+
// Using the < operator in T to heap-sort in-place with ascending order.
295+
// Useful for basic types where you don't want to write a custom comparison.
296+
// Side-effects:
297+
// Overwrites the input with the result, by sorting it in-place.
298+
// The elements returned by reference in targetList is a permutation of the original elements,
299+
// where each following element is larger or equal to the previous element.
300+
template <typename T>
301+
static void list_heapSort_ascending(List<T>& targetList) {
302+
dsr::list_heapSort<T>(targetList, [](const T &leftSide, const T &rightSide) -> bool { return leftSide <= rightSide; });
303+
}
304+
305+
// Using the > operator in T to heap-sort in-place with descending order.
306+
// Useful for basic types where you don't want to write a custom comparison.
307+
// Side-effects:
308+
// Overwrites the input with the result, by sorting it in-place.
309+
// The elements returned by reference in targetList is a permutation of the original elements,
310+
// where each following element is smaller or equal to the previous element.
311+
template <typename T>
312+
static void list_heapSort_descending(List<T>& targetList) {
313+
dsr::list_heapSort<T>(targetList, [](const T &leftSide, const T &rightSide) -> bool { return leftSide >= rightSide; });
314+
}
315+
316+
// TODO: Implement list_isSorted with a custom comparison.
317+
318+
template <typename T>
319+
static bool list_isSorted_ascending(const List<T>& sourceList) {
320+
for (intptr_t e = 0; e < sourceList.length() - 1; e++) {
321+
if (sourceList[e] > sourceList[e + 1]) {
322+
// Not sorted in ascending order.
323+
return false;
324+
}
325+
}
326+
// Sorted in ascending order.
327+
return true;
328+
}
329+
330+
template <typename T>
331+
static bool list_isSorted_descending(const List<T>& sourceList) {
332+
for (intptr_t e = 0; e < sourceList.length() - 1; e++) {
333+
if (sourceList[e] < sourceList[e + 1]) {
334+
// Not sorted in decending order.
335+
return false;
336+
}
337+
}
338+
// Sorted in decending order.
339+
return true;
340+
}
341+
63342
}
64343

65344
#endif
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
2+
#include "../testTools.h"
3+
#include "../../DFPSR/api/algorithmAPI_List.h"
4+
5+
START_TEST(ListAlgorithm)
6+
{ // List sorting with duplicate elements.
7+
List<int32_t> myList(5, 2, 18, 6, -1, 4, 6, -64, 2, 45);
8+
list_heapSort_ascending(myList);
9+
ASSERT_EQUAL(myList, List<int32_t>(-64, -1, 2, 2, 4, 5, 6, 6, 18, 45));
10+
list_heapSort_descending(myList);
11+
ASSERT_EQUAL(myList, List<int32_t>(45, 18, 6, 6, 5, 4, 2, 2, -1, -64));
12+
}
13+
{
14+
List<int32_t> unsortedSet(7, 5, 2, 4);
15+
ASSERT_EQUAL(list_elementExists(unsortedSet, 1), false);
16+
ASSERT_EQUAL(list_elementExists(unsortedSet, 2), true);
17+
ASSERT_EQUAL(list_elementExists(unsortedSet, 3), false);
18+
ASSERT_EQUAL(list_elementExists(unsortedSet, 4), true);
19+
ASSERT_EQUAL(list_elementExists(unsortedSet, 5), true);
20+
ASSERT_EQUAL(list_elementExists(unsortedSet, 6), false);
21+
ASSERT_EQUAL(list_elementExists(unsortedSet, 7), true);
22+
ASSERT_EQUAL(list_elementExists(unsortedSet, 8), false);
23+
// New value.
24+
ASSERT_EQUAL(list_insertUnique_last(unsortedSet, 3), true)
25+
ASSERT_EQUAL(unsortedSet, List<int32_t>(7, 5, 2, 4, 3));
26+
// Already exists.
27+
ASSERT_EQUAL(list_insertUnique_last(unsortedSet, 5), false)
28+
ASSERT_EQUAL(unsortedSet, List<int32_t>(7, 5, 2, 4, 3));
29+
// New value.
30+
ASSERT_EQUAL(list_insertUnique_last(unsortedSet, 6), true)
31+
ASSERT_EQUAL(unsortedSet, List<int32_t>(7, 5, 2, 4, 3, 6));
32+
}
33+
{
34+
List<int32_t> unsortedUnion(7, 5, 2, 4);
35+
// Nothing is inserted, because all inserted elements already exist.
36+
ASSERT_EQUAL(list_insertUnion_last(unsortedUnion, List<int32_t>(5, 2)), false);
37+
ASSERT_EQUAL(unsortedUnion, List<int32_t>(7, 5, 2, 4));
38+
// Unique values (3 and 6) are inserted at the end.
39+
ASSERT_EQUAL(list_insertUnion_last(unsortedUnion, List<int32_t>(3, 5, 6)), true);
40+
ASSERT_EQUAL(unsortedUnion, List<int32_t>(7, 5, 2, 4, 3, 6));
41+
}
42+
{
43+
List<int32_t> sortedSet(2, 4, 5, 7);
44+
ASSERT_EQUAL(list_elementExists(sortedSet, 1), false);
45+
ASSERT_EQUAL(list_elementExists(sortedSet, 2), true);
46+
ASSERT_EQUAL(list_elementExists(sortedSet, 3), false);
47+
ASSERT_EQUAL(list_elementExists(sortedSet, 4), true);
48+
ASSERT_EQUAL(list_elementExists(sortedSet, 5), true);
49+
ASSERT_EQUAL(list_elementExists(sortedSet, 6), false);
50+
ASSERT_EQUAL(list_elementExists(sortedSet, 7), true);
51+
ASSERT_EQUAL(list_elementExists(sortedSet, 8), false);
52+
// New value.
53+
ASSERT_EQUAL(list_insertUnique_sorted_ascending(sortedSet, 3), true)
54+
ASSERT_EQUAL(sortedSet, List<int32_t>(2, 3, 4, 5, 7));
55+
// Already exists.
56+
ASSERT_EQUAL(list_insertUnique_sorted_ascending(sortedSet, 5), false)
57+
ASSERT_EQUAL(sortedSet, List<int32_t>(2, 3, 4, 5, 7));
58+
// New value.
59+
ASSERT_EQUAL(list_insertUnique_sorted_ascending(sortedSet, 6), true)
60+
ASSERT_EQUAL(sortedSet, List<int32_t>(2, 3, 4, 5, 6, 7));
61+
}
62+
{ // Sorted unions, which are useful for comparing if two sets contain the same values.
63+
List<int32_t> sortedUnion(2, 4, 5, 7);
64+
// Nothing is inserted, because all inserted elements already exist.
65+
ASSERT_EQUAL(list_insertUnion_sorted_ascending(sortedUnion, List<int32_t>(5, 2)), false);
66+
ASSERT_EQUAL(sortedUnion, List<int32_t>(2, 4, 5, 7));
67+
// Unique values (3 and 6) are inserted in ascending order.
68+
ASSERT_EQUAL(list_insertUnion_sorted_ascending(sortedUnion, List<int32_t>(3, 5, 6)), true);
69+
ASSERT_EQUAL(sortedUnion, List<int32_t>(2, 3, 4, 5, 6, 7));
70+
}
71+
// TODO: Test with custom comparison functions.
72+
// TODO: Implement bruteforce testing.
73+
END_TEST

0 commit comments

Comments
 (0)