diff --git a/benchmarks/stdlib/collections/hashmap.c3 b/benchmarks/stdlib/collections/hashmap.c3 index e41bac5df..0a1264421 100644 --- a/benchmarks/stdlib/collections/hashmap.c3 +++ b/benchmarks/stdlib/collections/hashmap.c3 @@ -107,7 +107,8 @@ fn void hash_speeds_of_many_random_values() => @pool() foreach (&v : vwideints) *v = (uint128)random::next(&rand, uint.max); char[48][] zstrs = allocator::new_array(tmem, char[48], $arrsz)[:$arrsz]; - String[$arrsz] strs; + + String[] strs = mem::temp_array(String, $arrsz); foreach (x, &v : zstrs) { foreach (&c : (*v)[:random::next(&rand, 48)]) *c = (char)random::next(&rand, char.max); @@ -195,7 +196,7 @@ fn void random_access_string_keys() => @pool() v.tinit(); usz pseudo_checksum = 0; - String[5_000] saved; + String[] saved = mem::temp_array(String, 5_000); for (usz i = 0; i < saved.len; ++i) { diff --git a/benchmarks/stdlib/collections/linkedlist.c3 b/benchmarks/stdlib/collections/linkedlist.c3 new file mode 100644 index 000000000..e73977fd9 --- /dev/null +++ b/benchmarks/stdlib/collections/linkedlist.c3 @@ -0,0 +1,38 @@ +module linkedlist_benchmarks; + +import std::collections::linkedlist; + + +LinkedList{int} long_list; +const HAY = 2; +const NEEDLE = 1000; + +fn void bench_setup() @init +{ + set_benchmark_warmup_iterations(3); + set_benchmark_max_iterations(4096); + + int[*] haystack = { [0..999] = HAY }; + long_list = linkedlist::@new{int}(mem, haystack[..]); + long_list.push(NEEDLE); + long_list.push_all(haystack[..]); +} + + +// ============================================================================================== +module linkedlist_benchmarks @benchmark; + +String die_str = "Failed to find the value `1`. Is something broken?"; + + +fn void foreach_iterator() +{ + foreach (v : long_list.array_view()) if (v == NEEDLE) return; + runtime::@kill_benchmark(die_str); +} + +fn void foreach_r_iterator() +{ + foreach_r (v : long_list.array_view()) if (v == NEEDLE) return; + runtime::@kill_benchmark(die_str); +} diff --git a/benchmarks/stdlib/core/string_trim.c3 b/benchmarks/stdlib/core/string_trim.c3 index a909668f7..9156dbefa 100644 --- a/benchmarks/stdlib/core/string_trim.c3 +++ b/benchmarks/stdlib/core/string_trim.c3 @@ -19,7 +19,7 @@ macro void trim_bench($trim_str, String $target = WHITESPACE_TARGET) => @pool() $switch: $case $typeof($trim_str) == String: s1 = s2.trim($trim_str); - $case $typeof($$trim_str) == AsciiCharset: + $case $typeof($trim_str) == AsciiCharset: s1 = s2.trim_charset($trim_str); $default: $error "Unable to determine the right String `trim` operation to use."; $endswitch diff --git a/lib/std/collections/linkedlist.c3 b/lib/std/collections/linkedlist.c3 index b27c45cbc..43bf00398 100644 --- a/lib/std/collections/linkedlist.c3 +++ b/lib/std/collections/linkedlist.c3 @@ -2,13 +2,14 @@ // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::collections::linkedlist{Type}; +import std::io; const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); -struct Node @private +struct Node { - Node *next; - Node *prev; + Node* next; + Node* prev; Type value; } @@ -16,8 +17,31 @@ struct LinkedList { Allocator allocator; usz size; - Node *_first; - Node *_last; + Node* _first; + Node* _last; +} + +fn usz? LinkedList.to_format(&self, Formatter* f) @dynamic +{ + usz len = f.print("{ ")!; + for (Node* node = self._first; node != null; node.next) + { + len += f.printf(node.next ? "%s, " : "s", node.value)!; + } + return len + f.print(" }"); +} + +macro LinkedList @new(Allocator allocator, Type[] #default_values = {}) +{ + LinkedList new_list; + new_list.init(allocator); + new_list.push_all(#default_values); + return new_list; +} + +macro LinkedList @tnew(Type[] #default_values = {}) +{ + return @new(tmem, #default_values); } <* @@ -68,6 +92,11 @@ fn void LinkedList.push_front(&self, Type value) self.size++; } +fn void LinkedList.push_front_all(&self, Type[] value) +{ + foreach_r (v : value) self.push_front(v); +} + fn void LinkedList.push(&self, Type value) { Node *last = self._last; @@ -85,6 +114,11 @@ fn void LinkedList.push(&self, Type value) self.size++; } +fn void LinkedList.push_all(&self, Type[] value) +{ + foreach (v : value) self.push(v); +} + fn Type? LinkedList.peek(&self) => self.first() @inline; fn Type? LinkedList.peek_last(&self) => self.last() @inline; @@ -133,6 +167,7 @@ macro Node* LinkedList.node_at_index(&self, usz index) while (index--) node = node.next; return node; } + <* @require index < self.size *> @@ -141,6 +176,14 @@ fn Type LinkedList.get(&self, usz index) return self.node_at_index(index).value; } +<* + @require index < self.size +*> +fn Type* LinkedList.get_ref(&self, usz index) +{ + return &self.node_at_index(index).value; +} + <* @require index < self.size *> @@ -149,6 +192,26 @@ fn void LinkedList.set(&self, usz index, Type element) self.node_at_index(index).value = element; } +fn usz? LinkedList.index_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE) +{ + for (Node* node = self._first, usz i = 0; node != null; node = node.next, ++i) + { + if (node.value == t) return i; + } + return NOT_FOUND?; +} + +fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE) +{ + for (Node* node = self._last, usz i = self.size - 1; node != null; node = node.prev, --i) + { + if (node.value == t) return i; + if (i == 0) break; + } + return NOT_FOUND?; +} + + <* @require index < self.size *> @@ -334,3 +397,69 @@ fn void LinkedList.unlink(&self, Node* x) @private self.free_node(x); self.size--; } + + +macro bool LinkedList.eq(&self, other) @operator(==) @if(ELEMENT_IS_EQUATABLE) +{ + Node* node1 = self._first; + Node* node2 = other._first; + while (true) + { + if (!node1) return node2 == null; + if (!node2) return false; + if (node1.value != node2.value) return false; + node1 = node1.next; + node2 = node2.next; + } + return true; +} + +fn LinkedListArrayView LinkedList.array_view(&self) +{ + return { .list = self, .current_node = self._first }; +} + + +struct LinkedListArrayView +{ + LinkedList* list; + Node* current_node; + usz current_index; +} + +fn usz LinkedListArrayView.len(&self) @operator(len) => self.list.size; + +<* + @require index < self.list.size +*> +fn Type LinkedListArrayView.get(&self, usz index) @operator([]) +{ + return *self.get_ref(index); +} + +<* + @require index < self.list.size +*> +fn Type* LinkedListArrayView.get_ref(&self, usz index) @operator(&[]) +{ + if (index == self.list.size - 1) + { + self.current_node = self.list._last; + self.current_index = index; + } + + while (self.current_index != index) + { + switch + { + case index < self.current_index: // reverse iteration + self.current_node = self.current_node.prev; + self.current_index--; + case index > self.current_index: + self.current_node = self.current_node.next; + self.current_index++; + } + } + + return &self.current_node.value; +} diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index 4412019bd..3db3f9e99 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -387,7 +387,6 @@ macro bool @all(array, #predicate) @require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable" @require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side" @require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type." - *> macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...) @nodiscard { @@ -460,7 +459,6 @@ macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...) @require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable" @require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side" @require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type." - *> macro @tzip(left, right, #operation = ..., fill_with = ...) @nodiscard { diff --git a/lib/std/core/runtime_benchmark.c3 b/lib/std/core/runtime_benchmark.c3 index b57467afc..0ca6dcc9b 100644 --- a/lib/std/core/runtime_benchmark.c3 +++ b/lib/std/core/runtime_benchmark.c3 @@ -56,6 +56,7 @@ long cycle_stop @local; DString benchmark_log @local; bool benchmark_warming @local; uint this_iteration @local; +bool benchmark_stop @local; macro @start_benchmark() { @@ -69,6 +70,12 @@ macro @end_benchmark() cycle_stop = $$sysclock(); } +macro @kill_benchmark(String format, ...) +{ + @log_benchmark(format, $vasplat); + benchmark_stop = true; +} + macro @log_benchmark(msg, args...) => @pool() { if (benchmark_warming) return; @@ -133,6 +140,7 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @start_benchmark(); // can be overridden by calls inside the unit's func unit.func() @inline; + if (benchmark_stop) return false; if (benchmark_nano_seconds == (NanoDuration){}) @end_benchmark(); // only mark when it wasn't already by the unit.func diff --git a/releasenotes.md b/releasenotes.md index a02cfc128..1a22099f2 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -17,6 +17,8 @@ - Added generic `InterfaceList` to store a list of values that implement a specific interface - Added `path::home_directory`, `path::documents_directory`, `path::videos_directory`, `path::pictures_directory`, `path::desktop_directory`, `path::screenshots_directory`, `path::public_share_directory`, `path::templates_directory`, `path::saved_games_directory`, `path::music_directory`, `path::downloads_directory`. +- Add `LinkedList` array_view to support `[]` and `foreach`/`foreach_r`. #2438 +- Make `LinkedList` printable and add `==` operator. #2438 ## 0.7.5 Change list diff --git a/test/unit/stdlib/collections/linkedlist.c3 b/test/unit/stdlib/collections/linkedlist.c3 index 910fa6d6e..bfbfc8554 100644 --- a/test/unit/stdlib/collections/linkedlist.c3 +++ b/test/unit/stdlib/collections/linkedlist.c3 @@ -5,243 +5,252 @@ alias IntList = LinkedList{int}; fn void test_is_initialized() { - IntList test; - - assert(!test.is_initialized()); + IntList test; + test::@check(!test.is_initialized()); test.init(mem); - assert(test.is_initialized()); + test::@check(test.is_initialized()); test.free(); } -fn void test_push_front() +fn void test_push_front() => @pool() { - IntList list; - defer list.free(); + IntList list = linkedlist::@tnew{int}(); list.push_front(23); - assert(list.len() == 1); - assert(list.first()!! == 23); - assert(list.last()!! == 23); + test::eq(list.len(), 1); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, 23); list.push_front(55); - assert(list.len() == 2); - assert(list.last()!! == 23); - assert(list.first()!! == 55); + test::eq(list.len(), 2); + test::eq(list.last()!!, 23); + test::eq(list.first()!!, 55); } -fn void test_push() +fn void test_push() => @pool() { - IntList list; - defer list.free(); + IntList list = linkedlist::@tnew{int}(); list.push(23); - assert(list.len() == 1); - assert(list.first()!! == 23); - assert(list.last()!! == 23); + test::eq(list.len(), 1); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, 23); list.push(55); - assert(list.len() == 2); - assert(list.last()!! == 55); - assert(list.first()!! == 23); + test::eq(list.len(), 2); + test::eq(list.last()!!, 55); + test::eq(list.first()!!, 23); } -fn void test_get() +fn void test_get() => @pool() { - IntList list; - defer list.free(); - list.push(23); - list.push(55); - list.push(-3); - assert(list.get(2) == -3); - assert(list.get(1) == 55); - assert(list.get(0) == 23); + IntList list = linkedlist::@tnew{int}({ 23, 55, -3 }); + test::eq(list.get(2), -3); + test::eq(list.get(1), 55); + test::eq(list.get(0), 23); + test::eq(list.array_view()[0], 23); } -fn void test_insert() +fn void test_insert() => @pool() { - IntList list; - defer list.free(); - list.push(-3); - list.push(55); - list.push(23); + IntList list = linkedlist::@tnew{int}({ -3, 55, 23 }); list.insert_at(0, 1); list.insert_at(2, 11); list.insert_at(4, 111); list.insert_at(6, 1111); - assert(list.get(0) == 1); - assert(list.get(1) == -3); - assert(list.get(2) == 11); - assert(list.get(3) == 55); - assert(list.get(4) == 111); - assert(list.get(5) == 23); - assert(list.get(6) == 1111); + test::eq(list.get(0), 1); + test::eq(list.get(1), -3); + test::eq(list.get(2), 11); + test::eq(list.get(3), 55); + test::eq(list.get(4), 111); + test::eq(list.get(5), 23); + test::eq(list.get(6), 1111); } -fn void test_set() +fn void test_set() => @pool() { - IntList list; - defer list.free(); - list.push(-3); - list.push(55); - list.push(23); + IntList list = linkedlist::@tnew{int}({ -3, 55, 23 }); for (int i = 0; i < 3; i++) list.set(i, list.get(i) + 1); - assert(list.get(0) == -2); - assert(list.get(1) == 56); - assert(list.get(2) == 24); + test::eq(list.get(0), -2); + test::eq(list.get(1), 56); + test::eq(list.get(2), 24); } -fn void test_remove_at() +fn void test_remove_at() => @pool() { - IntList list; - defer list.free(); + IntList list = linkedlist::@tnew{int}(); for (int i = 0; i < 10; i++) list.push(i); list.remove_at(0); list.remove_at(1); list.remove_at(7); list.remove_at(5); - assert(list.get(0) == 1); - assert(list.get(1) == 3); - assert(list.get(5) == 8); - assert(list.get(4) == 6); + test::eq(list.get(0), 1); + test::eq(list.get(1), 3); + test::eq(list.get(5), 8); + test::eq(list.get(4), 6); } -fn void test_remove() +fn void test_remove() => @pool() { - IntList list; - defer list.free(); + IntList list = linkedlist::@tnew{int}(); list.push(2); for (int i = 0; i < 10; i++) list.push(5); list.push(2); list.remove(5); - assert(list.len() == 2); + test::eq(list.len(), 2); } -fn void test_remove_first_match() +fn void test_remove_first_match() // no @pool { IntList list; - defer list.free(); - list.push(23); - list.push(55); - list.push(-3); - assert(list.remove_first_match(23)); - assert(list.pop()!! == -3); - assert(list.pop()!! == 55); - assert(!list.len()); + defer list.free(); // left this to ensure .free() remains valid and functional + list.push_all({ 23, 55, -3 }); + test::@check(list.remove_first_match(23)); + test::eq(list.pop()!!, -3); + test::eq(list.pop()!!, 55); + test::@check(!list.len()); - list.push(23); - list.push(55); - list.push(-3); - assert(list.remove_first_match(55)); - assert(list.pop()!! == -3); - assert(list.pop()!! == 23); - assert(!list.len()); + list.push_all({ 23, 55, -3 }); + test::@check(list.remove_first_match(55)); + test::eq(list.pop()!!, -3); + test::eq(list.pop()!!, 23); + test::@check(!list.len()); - list.push(23); - list.push(55); - list.push(-3); - assert(list.remove_first_match(-3)); - assert(list.pop()!! == 55); - assert(list.pop()!! == 23); - assert(!list.len()); + list.push_all({ 23, 55, -3 }); + test::@check(list.remove_first_match(-3)); + test::eq(list.pop()!!, 55); + test::eq(list.pop()!!, 23); + test::@check(!list.len()); } -fn void test_remove_last_match() +fn void test_remove_last_match() => @pool() { - IntList list; - defer list.free(); - list.push(23); - list.push(55); - list.push(-3); - assert(list.remove_last_match(23)); - assert(list.pop()!! == -3); - assert(list.pop()!! == 55); - assert(!list.len()); + IntList list = linkedlist::@tnew{int}({ 23, 55, -3 }); + test::@check(list.remove_last_match(23)); + test::eq(list.pop()!!, -3); + test::eq(list.pop()!!, 55); + test::@check(!list.len()); - list.push(23); - list.push(55); - list.push(-3); - assert(list.remove_last_match(55)); - assert(list.pop()!! == -3); - assert(list.pop()!! == 23); - assert(!list.len()); + list.push_all({ 23, 55, -3 }); + test::@check(list.remove_last_match(55)); + test::eq(list.pop()!!, -3); + test::eq(list.pop()!!, 23); + test::@check(!list.len()); - list.push(23); - list.push(55); - list.push(-3); - assert(list.remove_last_match(-3)); - assert(list.pop()!! == 55); - assert(list.pop()!! == 23); - assert(!list.len()); + list.push_all({ 23, 55, -3 }); + test::@check(list.remove_last_match(-3)); + test::eq(list.pop()!!, 55); + test::eq(list.pop()!!, 23); + test::@check(!list.len()); } -fn void test_pop() +fn void test_pop() => @pool() { - IntList list; - defer list.free(); - list.push(23); + IntList list = linkedlist::@tnew{int}({ 23, 55, -3 }); + test::eq(list.len(), 3); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, -3); + test::eq(list.pop()!!, -3); + test::eq(list.len(), 2); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, 55); + test::eq(list.pop()!!, 55); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, 23); + test::eq(list.pop()!!, 23); + test::eq(list.len(), 0); + test::@check(@catch(list.pop())); + test::eq(list.len(), 0); list.push(55); - list.push(-3); - assert(list.len() == 3); - assert(list.first()!! == 23); - assert(list.last()!! == -3); - assert(list.pop()!! == -3); - assert(list.len() == 2); - assert(list.first()!! == 23); - assert(list.last()!! == 55); - assert(list.pop()!! == 55); - assert(list.first()!! == 23); - assert(list.last()!! == 23); - assert(list.pop()!! == 23); - assert(list.len() == 0); - assert(@catch(list.pop())); - assert(list.len() == 0); - list.push(55); - assert(list.len() == 1); + test::eq(list.len(), 1); } -fn void test_remove_last() +fn void test_remove_last() => @pool() { - IntList list; - defer list.free(); - list.push(23); + IntList list = linkedlist::@tnew{int}({ 23, 55, -3 }); + test::eq(list.len(), 3); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, -3); + test::@check(@ok(list.remove_last())); + test::eq(list.len(), 2); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, 55); + test::@check(@ok(list.remove_last())); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, 23); + test::@check(@ok(list.remove_last())); + test::eq(list.len(), 0); + test::@check(@catch(list.pop())); + test::eq(list.len(), 0); list.push(55); - list.push(-3); - assert(list.len() == 3); - assert(list.first()!! == 23); - assert(list.last()!! == -3); - assert(@ok(list.remove_last())); - assert(list.len() == 2); - assert(list.first()!! == 23); - assert(list.last()!! == 55); - assert(@ok(list.remove_last())); - assert(list.first()!! == 23); - assert(list.last()!! == 23); - assert(@ok(list.remove_last())); - assert(list.len() == 0); - assert(@catch(list.pop())); - assert(list.len() == 0); - list.push(55); - assert(list.len() == 1); + test::eq(list.len(), 1); } -fn void test_remove_first() +fn void test_remove_first() => @pool() { - IntList list; - defer list.free(); - list.push(23); + IntList list = linkedlist::@tnew{int}({ 23, 55, -3 }); + test::eq(list.len(), 3); + test::eq(list.first()!!, 23); + test::eq(list.last()!!, -3); + test::@check(@ok(list.remove_first())); + test::eq(list.len(), 2); + test::eq(list.last()!!, -3); + test::eq(list.first()!!, 55); + test::@check(@ok(list.remove_first())); + test::eq(list.last()!!, -3); + test::eq(list.first()!!, -3); + test::@check(@ok(list.remove_first())); + test::eq(list.len(), 0); + test::@check(@catch(list.remove_first())); + test::eq(list.len(), 0); list.push(55); - list.push(-3); - assert(list.len() == 3); - assert(list.first()!! == 23); - assert(list.last()!! == -3); - assert(@ok(list.remove_first())); - assert(list.len() == 2); - assert(list.last()!! == -3); - assert(list.first()!! == 55); - assert(@ok(list.remove_first())); - assert(list.last()!! == -3); - assert(list.first()!! == -3); - assert(@ok(list.remove_first())); - assert(list.len() == 0); - assert(@catch(list.remove_first())); - assert(list.len() == 0); - list.push(55); - assert(list.len() == 1); -} \ No newline at end of file + test::eq(list.len(), 1); +} + +fn void test_push_all_ordering() => @pool() +{ + IntList l = linkedlist::@tnew{int}({ 23, 45, 66 }); + test::eq(l.array_view()[0], 23); + test::eq(l.array_view()[1], 45); + test::eq(l.array_view()[2], 66); + + IntList l2 = linkedlist::@tnew{int}({ 23, 45, 66 }); + l2.push_front_all({ 3, 6 }); + test::eq(l2.array_view()[0], 3); + test::eq(l2.array_view()[1], 6); + test::eq(l2.array_view()[2], 23); + test::eq(l2.array_view()[3], 45); + test::eq(l2.array_view()[4], 66); +} + +fn void test_index_of() => @pool() +{ + IntList list = linkedlist::@tnew{int}({ 23, 55, 55, -3 }); + test::@error(list.index_of(20), NOT_FOUND); + test::eq(list.index_of(55)!!, 1); + test::eq(list.rindex_of(55)!!, 2); +} + +fn void test_operators() => @pool() +{ + IntList list = linkedlist::@tnew{int}({ 17, 109, 2, 8 }); + foreach (i, &e : list.array_view()) *e += 2; + test::eq(list.array_view()[0], 19); + test::eq(list.array_view()[1], 111); + test::eq(list.array_view()[2], 4); + test::eq(list.array_view()[3], 10); +} + +fn void test_iterator_set() => @pool() +{ + IntList list = linkedlist::@tnew{int}({ 17, 109, 2, 8 }); + IntList expected = linkedlist::@tnew{int}({1, 2, 3, 4}); + foreach (int i, &v : list.array_view()) *v = i + 1; + test::eq(list, expected); +} + +fn void test_iterator_reverse_set() => @pool() +{ + IntList list = linkedlist::@tnew{int}({ 17, 109, 2, 8 }); + IntList pushed = linkedlist::@tnew{int}(); + IntList expected = linkedlist::@tnew{int}({8, 2, 109, 17}); + foreach_r (int i, v : list.array_view()) pushed.push(v); + test::eq(pushed, expected); +} diff --git a/test/unit/stdlib/core/array.c3 b/test/unit/stdlib/core/array.c3 index ce80f9ac7..2ec4f408b 100644 --- a/test/unit/stdlib/core/array.c3 +++ b/test/unit/stdlib/core/array.c3 @@ -32,7 +32,7 @@ fn usz? ArrTestStruct.to_format(&self, Formatter* f) @dynamic } module array_test @test; -import std::collections::pair, std::collections::list; +import std::collections; fn void contains() @@ -86,6 +86,10 @@ fn void reduce() => @pool() List {String} l; l.push_all({ "abc", "adef", "ghi", "a12345" }); test::eq(array::@reduce(l, (usz)0, fn (acc, e, u) => acc + e.len + u), 0 + (3 + 0) + (4 + 1) + (3 + 2) + (6 + 3)); + + LinkedList {String} ll; + ll.push_all({ "abc", "adef", "ghi", "a12345" }); + test::eq(array::@reduce(ll.array_view(), (usz)0, fn (acc, e, u) => acc + e.len + u), 0 + (3 + 0) + (4 + 1) + (3 + 2) + (6 + 3)); } fn void sum() @@ -127,6 +131,10 @@ fn void indices_of() => @pool() List {String} l; l.push_all({ "abc", "adef", "ghi", "a12345" }); test::eq(array::@tindices_of(l, fn (e, u) => e.len <= 3), (usz[]){0, 2}); + + LinkedList {String} ll; + ll.push_all({ "abc", "adef", "ghi", "a12345" }); + test::eq(array::@tindices_of(ll.array_view(), fn (e, u) => e.len <= 3), (usz[]){0, 2}); } fn void filter() => @pool() @@ -143,6 +151,10 @@ fn void filter() => @pool() List {String} l; l.push_all({ "abc", "adef", "ghi", "a12345" }); test::eq(array::@tfilter(l, fn (e, u) => e.len <= 3), (String[]){ "abc", "ghi" }); + + LinkedList {String} ll; + ll.push_all({ "abc", "adef", "ghi", "a12345" }); + test::eq(array::@tfilter(ll.array_view(), fn (e, u) => e.len <= 3), (String[]){ "abc", "ghi" }); } fn void any_true() => @pool() @@ -159,6 +171,11 @@ fn void any_true() => @pool() l.push_all({ "abc", "adef", "ghi", "a12345" }); test::@check(array::@any(l, fn (e, u) => e.len > 5)); test::@check(!array::@any(l, fn (e, u) => e == "hi")); + + LinkedList {String} ll; + ll.push_all({ "abc", "adef", "ghi", "a12345" }); + test::@check(array::@any(ll.array_view(), fn (e, u) => e.len > 5)); + test::@check(!array::@any(ll.array_view(), fn (e, u) => e == "hi")); } fn void all_true() => @pool() @@ -175,6 +192,11 @@ fn void all_true() => @pool() l.push_all({ "abc", "adef", "ghi", "a12345" }); test::@check(array::@all(l, fn (e, u) => e.len > 1)); test::@check(!array::@all(l, fn (e, u) => e.len > 3)); + + LinkedList {String} ll; + ll.push_all({ "abc", "adef", "ghi", "a12345" }); + test::@check(array::@all(ll.array_view(), fn (e, u) => e.len > 1)); + test::@check(!array::@all(ll.array_view(), fn (e, u) => e.len > 3)); } @@ -365,3 +387,17 @@ fn void zip_into_list() => @pool() test::eq(l.len(), 4); foreach (i, c : l) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]); } + +fn void zip_into_linked_list() => @pool() +{ + LinkedList{char} l; + l.push_all({ '1', '2', '3', '4' }); + String[6] right = { "one", "two", "three", "four", "five", "six" }; + + char[] expected = { '4', '5', '8', '8' }; + + array::@zip_into(l.array_view(), right, fn (a, b) => a + (char)b.len); + + test::eq(l.len(), 4); + foreach (i, c : l.array_view()) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]); +}