├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── CMakeLists.txt ├── Python ├── orderedtree.py └── tree.py ├── README.md ├── badcompile_test.cpp ├── c_list.c ├── c_list.h ├── c_list_test.cpp ├── confuzzle.c ├── confuzzle.h ├── confuzzle_test.cpp ├── examplescript.sh ├── fileio_test.cpp ├── graph.hpp ├── graph_test.cpp ├── llist.cpp ├── llist.hpp ├── llist_test.cpp ├── main.c ├── slice.hpp ├── slice_test.cpp ├── stringexamples.cpp ├── stringexamples.hpp ├── stringexamples_c.c ├── stringexamples_c.h ├── stringexamples_c_test.cpp ├── stringexamples_test.cpp ├── testfile.txt ├── tuple_map_test.cpp ├── workqueue.hpp └── workqueue_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Binary 5 | hello 6 | 7 | # Emacs save files 8 | *~ 9 | 10 | # Object files 11 | *.o 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Linker output 17 | *.ilk 18 | *.map 19 | *.exp 20 | 21 | # Precompiled Headers 22 | *.gch 23 | *.pch 24 | 25 | # Libraries 26 | *.lib 27 | *.a 28 | *.la 29 | *.lo 30 | 31 | # Shared objects (inc. Windows DLLs) 32 | *.dll 33 | *.so 34 | *.so.* 35 | *.dylib 36 | 37 | # Executables 38 | *.exe 39 | *.out 40 | *.app 41 | *.i*86 42 | *.x86_64 43 | *.hex 44 | 45 | # Debug files 46 | *.dSYM/ 47 | *.su 48 | *.idb 49 | *.pdb 50 | 51 | # Kernel Module Compile Results 52 | *.mod* 53 | *.cmd 54 | .tmp_versions/ 55 | modules.order 56 | Module.symvers 57 | Mkfile.old 58 | dkms.conf 59 | 60 | # CMake 61 | CMakeFiles 62 | CmakeCache.txt 63 | cmake_install.cmake 64 | C__ 65 | build 66 | 67 | # Python 68 | __pycache__ 69 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/clang", 10 | "cStandard": "c17", 11 | "cppStandard": "c++20", 12 | "intelliSenseMode": "linux-clang-arm64", 13 | "configurationProvider": "ms-vscode.cmake-tools" 14 | } 15 | ], 16 | "version": 4 17 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch Main Executable", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/hello", 12 | "args": ["fubar", "baz"], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "preLaunchTask": "CMake: build", 19 | "setupCommands": [ 20 | { 21 | "description": "Enable pretty-printing for gdb", 22 | "text": "-enable-pretty-printing", 23 | "ignoreFailures": true 24 | } 25 | ] 26 | }, 27 | 28 | { 29 | "name": "(gdb) Launch Test Harness", 30 | "type": "cppdbg", 31 | "request": "launch", 32 | "program": "${workspaceFolder}/build/testbinary", 33 | "args": ["--gtest_filter=SliceTest*"], 34 | "stopAtEntry": false, 35 | "cwd": "${workspaceFolder}", 36 | "environment": [], 37 | "externalConsole": false, 38 | "MIMode": "gdb", 39 | "preLaunchTask": "CMake: build", 40 | "setupCommands": [ 41 | { 42 | "description": "Enable pretty-printing for gdb", 43 | "text": "-enable-pretty-printing", 44 | "ignoreFailures": true 45 | } 46 | ] 47 | } 48 | 49 | 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.errorSquiggles": "enabled", 3 | "files.associations": { 4 | "any": "cpp", 5 | "array": "cpp", 6 | "atomic": "cpp", 7 | "bit": "cpp", 8 | "*.tcc": "cpp", 9 | "cctype": "cpp", 10 | "chrono": "cpp", 11 | "clocale": "cpp", 12 | "cmath": "cpp", 13 | "compare": "cpp", 14 | "concepts": "cpp", 15 | "condition_variable": "cpp", 16 | "cstdarg": "cpp", 17 | "cstddef": "cpp", 18 | "cstdint": "cpp", 19 | "cstdio": "cpp", 20 | "cstdlib": "cpp", 21 | "cstring": "cpp", 22 | "ctime": "cpp", 23 | "cwchar": "cpp", 24 | "cwctype": "cpp", 25 | "deque": "cpp", 26 | "forward_list": "cpp", 27 | "list": "cpp", 28 | "map": "cpp", 29 | "set": "cpp", 30 | "string": "cpp", 31 | "unordered_map": "cpp", 32 | "unordered_set": "cpp", 33 | "vector": "cpp", 34 | "exception": "cpp", 35 | "algorithm": "cpp", 36 | "functional": "cpp", 37 | "iterator": "cpp", 38 | "memory": "cpp", 39 | "memory_resource": "cpp", 40 | "numeric": "cpp", 41 | "optional": "cpp", 42 | "random": "cpp", 43 | "ratio": "cpp", 44 | "string_view": "cpp", 45 | "system_error": "cpp", 46 | "tuple": "cpp", 47 | "type_traits": "cpp", 48 | "utility": "cpp", 49 | "fstream": "cpp", 50 | "initializer_list": "cpp", 51 | "iomanip": "cpp", 52 | "iosfwd": "cpp", 53 | "iostream": "cpp", 54 | "istream": "cpp", 55 | "limits": "cpp", 56 | "mutex": "cpp", 57 | "new": "cpp", 58 | "numbers": "cpp", 59 | "ostream": "cpp", 60 | "semaphore": "cpp", 61 | "sstream": "cpp", 62 | "stdexcept": "cpp", 63 | "stop_token": "cpp", 64 | "streambuf": "cpp", 65 | "thread": "cpp", 66 | "cinttypes": "cpp", 67 | "typeinfo": "cpp", 68 | "variant": "cpp", 69 | "ranges": "cpp" 70 | } 71 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cmake", 6 | "label": "CMake: build", 7 | "command": "build", 8 | "targets": [ 9 | "hello", 10 | "testbinary" 11 | ], 12 | "group": "build", 13 | "problemMatcher": [], 14 | "detail": "CMake template build task" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(hello) 3 | 4 | set (CMAKE_CXX_STANDARD 20) 5 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g \ 6 | -Wall -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough \ 7 | -Werror=format-security \ 8 | -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \ 9 | -D_GLIBCXX_ASSERTIONS \ 10 | -fstack-clash-protection -fstack-protector-strong \ 11 | -Wl,-z,nodlopen -Wl,-z,noexecstack \ 12 | -Wl,-z,relro -Wl,-z,now \ 13 | -Wl,--as-needed -Wl,--no-copy-dt-needed-entries \ 14 | --coverage ") 15 | set (CMAKE_CXX_STANDARD_REQUIRED ON) 16 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra --coverage -D_GLIBCXX_ASSERTIONS") 17 | 18 | 19 | 20 | # This is geting gunit so you don't have to... 21 | include(FetchContent) 22 | FetchContent_Declare( 23 | googletest 24 | URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip 25 | ) 26 | # For Windows: Prevent overriding the parent project's compiler/linker settings 27 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 28 | FetchContent_MakeAvailable(googletest) 29 | 30 | add_executable(hello main.c 31 | confuzzle.c 32 | confuzzle.h) 33 | 34 | enable_testing() 35 | 36 | 37 | add_executable(testbinary confuzzle.c confuzzle_test.cpp stringexamples.cpp stringexamples_test.cpp 38 | stringexamples_c.c stringexamples_c_test.cpp llist.cpp llist_test.cpp graph_test.cpp 39 | c_list.c c_list_test.cpp fileio_test.cpp tuple_map_test.cpp workqueue_test.cpp badcompile_test.cpp slice_test.cpp) 40 | target_link_libraries( 41 | testbinary 42 | GTest::gtest_main 43 | ) 44 | 45 | include(GoogleTest) 46 | gtest_discover_tests(testbinary) -------------------------------------------------------------------------------- /Python/orderedtree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from tree import TreeNode 4 | import random 5 | 6 | 7 | class OrderedTree: 8 | 9 | @staticmethod 10 | def rotate_left(node): 11 | new_top = node.right 12 | node.right = new_top.left 13 | new_top.left = node 14 | new_top.left.update_height() 15 | new_top.update_height() 16 | return new_top 17 | 18 | @staticmethod 19 | def rotate_right(node): 20 | new_top = node.left 21 | node.left = new_top.right 22 | new_top.right = node 23 | new_top.right.update_height() 24 | new_top.update_height() 25 | return new_top 26 | 27 | def __init__(self, *args): 28 | self.tree = None 29 | for a in args: 30 | for data in a: 31 | self.avl_insert(data) 32 | 33 | def avl_insert(self, data): 34 | def avl_insert_internal(node): 35 | if node is None: 36 | return TreeNode(data, None, None) 37 | elif data < node.data: 38 | node.left = avl_insert_internal(node.left) 39 | else: 40 | node.right = avl_insert_internal(node.right) 41 | node.update_height() 42 | return OrderedTree.avl_rebalance(node) 43 | self.tree = avl_insert_internal(self.tree) 44 | 45 | @staticmethod 46 | def avl_rebalance(node): 47 | if abs(node.balance()) < 2: 48 | return node 49 | if node.balance() == 2: 50 | # Right heavy. Is our right subtree left heavy? 51 | if node.right.balance() == -1: 52 | node.right = OrderedTree.rotate_right(node.right) 53 | return OrderedTree.rotate_left(node) 54 | if node.balance() == -2: 55 | if node.left.balance() == 1: 56 | node.left = OrderedTree.rotate_left(node.left) 57 | return OrderedTree.rotate_right(node) 58 | 59 | def __repr__(self): 60 | return repr(self.tree) 61 | 62 | def is_ordered(self): 63 | if self.tree is None: 64 | return True 65 | last = None 66 | for x in self.tree: 67 | if last is None or last <= x: 68 | last = x 69 | else: 70 | return False 71 | return True 72 | 73 | def assert_correct_balance(self): 74 | def check_balance(node): 75 | if node is None: 76 | return 77 | balance = node.balance() 78 | if node.left: 79 | node.left.update_height() 80 | check_balance(node.left) 81 | if node.right: 82 | node.right.update_height() 83 | check_balance(node.right) 84 | node.update_height() 85 | assert balance == node.balance() 86 | 87 | check_balance(self.tree) 88 | 89 | def __contains__(self, data): 90 | def internal(tree): 91 | if tree is None: 92 | return False 93 | elif tree.data == data: 94 | return True 95 | elif data < tree.data: 96 | return internal(tree.left) 97 | return internal(tree.right) 98 | return internal(self.tree) 99 | 100 | 101 | def avl_testing(): 102 | test = OrderedTree([1, 2, 3, 4, 5, 6, 7, 8, 9]) 103 | print(test) 104 | test.assert_correct_balance() 105 | 106 | 107 | def full_test(): 108 | avl_testing() 109 | alphabet = "ABCDEFGHIJK" 110 | data = [] 111 | for x in alphabet: 112 | data.append(x) 113 | random.shuffle(data) 114 | test = OrderedTree(data) 115 | print(repr(test)) 116 | i = 0 117 | for x in test.tree: 118 | assert x == alphabet[i] 119 | i += 1 120 | test.assert_correct_balance() 121 | 122 | print(repr(OrderedTree(alphabet))) 123 | 124 | data = [] 125 | for x in range(0, 80, 2): 126 | data.append(x) 127 | random.shuffle(data) 128 | test = OrderedTree(data) 129 | print(repr(test)) 130 | for x in range(-1, 80, 1): 131 | if x % 2 == 0: 132 | assert x in test 133 | else: 134 | assert x not in test 135 | assert test.is_ordered() 136 | test.assert_correct_balance() 137 | 138 | 139 | if __name__ == "__main__": 140 | full_test() 141 | -------------------------------------------------------------------------------- /Python/tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import deque 4 | 5 | def get_height(node): 6 | if node is None: 7 | return 0 8 | return node.height 9 | 10 | class TreeNode: 11 | def __init__(self, data, left, right): 12 | self.data = data 13 | self.left = left 14 | self.right = right 15 | self.height = 1 16 | 17 | def update_height(self): 18 | self.height = 1 + max(get_height(self.left), get_height(self.right)) 19 | 20 | def balance(self): 21 | return(get_height(self.right) - get_height(self.left)) 22 | 23 | def __repr__(self): 24 | res = "(" 25 | if self.left: 26 | res += repr(self.left) + " " 27 | res += repr(self.data) + "/b:{}".format(self.balance()) 28 | if self.right: 29 | res += " " + repr(self.right) 30 | return res + ")" 31 | 32 | def _nodes(self): 33 | """ An internal iterator that returns nodes rather than the data""" 34 | yield self 35 | if self.left != None: 36 | if isinstance(self.left, TreeNode): 37 | yield from self.left._nodes() 38 | else: 39 | yield self.left 40 | if self.right is not None: 41 | if isinstance(self.right, TreeNode): 42 | yield from self.right._nodes() 43 | else: 44 | yield self.right 45 | 46 | 47 | def is_well_formed(self): 48 | """ Returns true if this is a proper tree, false otherwise. 49 | Useful for a lot of assertions""" 50 | s = set() 51 | for x in self._nodes(): 52 | if x in s: 53 | return False 54 | if not isinstance(x, TreeNode): 55 | return False 56 | s.add(x) 57 | return True 58 | 59 | def __bool__(self): 60 | return True 61 | 62 | # This is an in-order iteration 63 | def __iter__(self): 64 | assert self.is_well_formed() 65 | if self.left: 66 | yield from self.left 67 | yield self.data 68 | if self.right: 69 | yield from self.right 70 | 71 | # THis is the pre-order iteration 72 | def preorder(self): 73 | assert self.is_well_formed() 74 | yield self.data 75 | if self.left: 76 | yield from self.left.preorder() 77 | if self.right: 78 | yield from self.right.preorder() 79 | 80 | # THis is the post-order iteration 81 | def postorder(self): 82 | assert self.is_well_formed() 83 | if self.left: 84 | yield from self.left.postorder() 85 | if self.right: 86 | yield from self.right.postorder() 87 | yield self.data 88 | 89 | # This is the level order 90 | def levelorder(self): 91 | assert self.is_well_formed() 92 | queue = deque() 93 | # A double ended queue has a "Left" and "Right" 94 | queue.append(self) 95 | while len(queue) > 0: 96 | at = queue.popleft() 97 | yield at.data 98 | if at.left is not None: 99 | queue.append(at.left) 100 | if at.right is not None: 101 | queue.append(at.right) 102 | 103 | 104 | def test_tree(): 105 | return TreeNode("A", 106 | TreeNode("B", 107 | TreeNode("D", None, 108 | TreeNode("H", None, None)), 109 | TreeNode("E", 110 | TreeNode("I", None, None), 111 | TreeNode("J", None, 112 | TreeNode("K", None, None)))), 113 | TreeNode("C", 114 | TreeNode("F", None, None), 115 | TreeNode("G", None, None))) 116 | 117 | if __name__ == "__main__": 118 | print(repr(test_tree())) 119 | t = test_tree() 120 | print("In order " + ", ".join(t)) 121 | print("Preorder " + ", ".join(t.preorder())) 122 | print("Postorder " + ", ".join(t.postorder())) 123 | print("Level order " + ", ".join(t.levelorder())) 124 | assert t.is_well_formed() 125 | broken = test_tree() 126 | broken.left.right = broken.left.left 127 | assert not broken.is_well_formed() 128 | broken = test_tree() 129 | broken.left.right = broken 130 | assert not broken.is_well_formed() 131 | broken = test_tree() 132 | broken.left.right = 32 133 | assert not broken.is_well_formed() 134 | 135 | 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C-Template 2 | Template for C programming for ECS 36C at Davis 3 | 4 | This consists of a simple Hello World program and a trivial Makefile 5 | with debugging turned on. 6 | -------------------------------------------------------------------------------- /badcompile_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | 6 | -------------------------------------------------------------------------------- /c_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "c_list.h" 5 | 6 | linked_list *new_linked_list() 7 | { 8 | linked_list *ret = (linked_list *)malloc( 9 | sizeof(linked_list)); 10 | ret->length = 0; 11 | ret->head = NULL; 12 | return ret; 13 | }; 14 | 15 | void free_linked_list(linked_list *l) 16 | { 17 | linked_list_cell *at = l->head; 18 | linked_list_cell *tmp = at; 19 | while (at) 20 | { 21 | tmp = at->next; 22 | free(at); 23 | at = tmp; 24 | } 25 | free(l); 26 | } 27 | 28 | void prepend(linked_list *l, void *data) 29 | { 30 | linked_list_cell *node = malloc(sizeof(linked_list_cell)); 31 | node->next = l->head; 32 | l->head = node; 33 | node->data = data; 34 | l->length += 1; 35 | } 36 | 37 | void append(linked_list *l, void *data) 38 | { 39 | linked_list_cell *tmp = l->head; 40 | l->length += 1; 41 | if (l->head == NULL) 42 | { 43 | l->head = malloc(sizeof(linked_list_cell)); 44 | l->head->next = NULL; 45 | l->head->data = data; 46 | return; 47 | } 48 | while (tmp->next != NULL) 49 | { 50 | tmp = tmp->next; 51 | } 52 | tmp->next = malloc(sizeof(linked_list_cell)); 53 | tmp->next->data = data; 54 | tmp->next->next = NULL; 55 | return; 56 | } 57 | 58 | // Returns NULL if out of range 59 | void *get_at(linked_list *l, size_t index) 60 | { 61 | linked_list_cell *node = l->head; 62 | while (node) 63 | { 64 | if (index == 0) 65 | { 66 | return node->data; 67 | } 68 | node = node->next; 69 | index--; 70 | } 71 | return NULL; 72 | } 73 | 74 | void set_at(linked_list *l, size_t index, void *data) 75 | { 76 | linked_list_cell *at = l->head; 77 | size_t i; 78 | while (at != NULL) 79 | { 80 | if (index == i) 81 | { 82 | at->data = data; 83 | return; 84 | } 85 | i++; 86 | at = at->next; 87 | } 88 | } 89 | 90 | // Returns -1 if not found, otherwise returns index 91 | int find_at(linked_list *l, bool (*matcher)(void *)) 92 | { 93 | linked_list_cell *at = l->head; 94 | int i = 0; 95 | while (at != NULL) 96 | { 97 | if (matcher(at->data)) 98 | { 99 | return i; 100 | } 101 | i++; 102 | at = at->next; 103 | } 104 | return -1; 105 | } 106 | -------------------------------------------------------------------------------- /c_list.h: -------------------------------------------------------------------------------- 1 | #ifndef _C_LIST_H 2 | #define _C_LIST_H 3 | 4 | #include 5 | 6 | typedef struct linked_list_cell 7 | { 8 | void *data; 9 | struct linked_list_cell *next; 10 | } linked_list_cell; 11 | 12 | typedef struct linked_list 13 | { 14 | struct linked_list_cell *head; 15 | size_t length; 16 | } linked_list; 17 | 18 | linked_list *new_linked_list(); 19 | 20 | void prepend(linked_list *l, void *data); 21 | void append(linked_list *l, void *data); 22 | 23 | // Returns NULL if out of range 24 | void *get_at(linked_list *l, size_t index); 25 | 26 | void set_at(linked_list *l, size_t index, void *data); 27 | 28 | // Returns -1 if not found, otherwise returns index 29 | int find_at(linked_list *l, bool (*matcher)(void *)); 30 | 31 | void free_linked_list(linked_list *l); 32 | 33 | #endif -------------------------------------------------------------------------------- /c_list_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" 5 | { 6 | #include "c_list.h" 7 | #include 8 | 9 | bool is_foo(void *data) 10 | { 11 | return !strcmp((const char *)data, "foo"); 12 | } 13 | 14 | bool is_false(void *data) 15 | { 16 | (void)data; 17 | return false; 18 | } 19 | 20 | int sorter(const void *a, const void *b){ 21 | return strcmp(*((const char **) a), *((const char **) b)); 22 | } 23 | 24 | } 25 | 26 | TEST(C_LIST, BasicTestsPrepend) 27 | { 28 | linked_list *l = new_linked_list(); 29 | std::vector teststrings = {"foo", "bar", "baz", "garplay"}; 30 | char *tmp = (char *) malloc(100); 31 | sprintf(tmp, "foo"); 32 | 33 | size_t i; 34 | i = 0; 35 | for (const char *s : teststrings) 36 | { 37 | EXPECT_EQ(get_at(l, i), nullptr); 38 | prepend(l, (void *)s); 39 | i++; 40 | EXPECT_EQ(get_at(l, 0), s); 41 | EXPECT_EQ(l->length, i); 42 | } 43 | for (size_t i = 0; i < 4; ++i) 44 | { 45 | EXPECT_EQ((const char *)get_at(l, i), teststrings[3 - i]); 46 | } 47 | EXPECT_EQ(find_at(l, is_foo), 3); 48 | EXPECT_EQ(find_at(l, is_false), -1); 49 | // This does a pointer comparison 50 | EXPECT_NE(get_at(l, 3), tmp); 51 | EXPECT_TRUE(!strcmp((const char *) get_at(l, 3), tmp)); 52 | free_linked_list(l); 53 | free(tmp); 54 | }; 55 | 56 | TEST(C_LIST, BasicTestsAppend) 57 | { 58 | linked_list *l = new_linked_list(); 59 | std::vector teststrings = {"foo", "bar", "baz", "garplay"}; 60 | size_t i; 61 | i = 0; 62 | for (const char *s : teststrings) 63 | { 64 | EXPECT_EQ(get_at(l, i), nullptr); 65 | append(l, (void *)s); 66 | EXPECT_EQ(get_at(l, i), s); 67 | i++; 68 | EXPECT_EQ(l->length, i); 69 | } 70 | for (size_t i = 0; i < 4; ++i) 71 | { 72 | EXPECT_EQ((const char *)get_at(l, i), teststrings[i]); 73 | } 74 | free_linked_list(l); 75 | }; 76 | 77 | TEST(C_LIST, SandboxPrint) 78 | { 79 | float f = 1.0; 80 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 81 | #pragma GCC diagnostic ignored "-Wuninitialized" 82 | 83 | printf("%i\n", *((int *)&f)); 84 | printf("%i\n\n", (int)f); 85 | f = 0.0; 86 | printf("%i\n", *((int *)&f)); 87 | printf("%i\n\n", (int)f); 88 | f = -0.0; 89 | printf("%i\n", *((int *)&f)); 90 | printf("%i\n\n", (int)f); 91 | } 92 | 93 | 94 | TEST(C_LIST, qsort_test) 95 | { 96 | const char *test[] = {"foo", "bar", "baz", "garplay"}; 97 | const char *sorted[] = {"bar", "baz", "foo", "garplay"}; 98 | int i; 99 | qsort(test, 4, sizeof(char *), sorter); 100 | for (i = 0; i < 4; ++i){ 101 | EXPECT_STREQ(test[i], sorted[i]); 102 | } 103 | } -------------------------------------------------------------------------------- /confuzzle.c: -------------------------------------------------------------------------------- 1 | #include "confuzzle.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static const uint8_t sbox[256] = { 8 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 9 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 10 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 11 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 12 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 13 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 14 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 15 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 16 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 17 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 18 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 19 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 20 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 21 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 22 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 23 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 24 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 25 | 26 | 27 | /* 28 | * You could just have it PRINT the confuzzle value but don't do that! 29 | * This is to make sure you can run the debugger and development tools 30 | */ 31 | int confuzzle(char *sid){ 32 | char *confusion = malloc(sizeof(char) * 2 * (strlen(sid) + 1)); 33 | size_t i; 34 | int j = 0; 35 | uint8_t c; 36 | uint8_t d = 0; 37 | for (i = 0; i < strlen(sid); ++i){ 38 | c = (uint8_t) sid[i]; 39 | d = d + sbox[c] + 1; 40 | j += sprintf((confusion + 2 * i), "%2x", d); 41 | } 42 | /* set breakpoint before free to see value w/o memory leak*/ 43 | free(confusion); 44 | return j; 45 | } 46 | -------------------------------------------------------------------------------- /confuzzle.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFUZZLE_H 2 | #define _CONFUZZLE_H 3 | 4 | int confuzzle(char *input); 5 | 6 | 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /confuzzle_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | #include "confuzzle.h" 5 | } 6 | 7 | // Demonstrate some basic assertions. 8 | TEST(HelloTest, BasicAssertions) { 9 | // Expect two strings not to be equal. 10 | EXPECT_STRNE("hello", "world"); 11 | // Expect equality. 12 | EXPECT_EQ(7 * 6, 42); 13 | } 14 | 15 | TEST(HelloTest, KnownFailure) { 16 | // EXPECT_TRUE(false) << "False is not true!"; 17 | } 18 | 19 | TEST(TestC, ConfuzzleTest) { 20 | EXPECT_EQ(confuzzle((char *) "abc"), 6) << "Just a test"; 21 | } -------------------------------------------------------------------------------- /examplescript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "This is a simple example script, coding in VSCode" 4 | -------------------------------------------------------------------------------- /fileio_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stringexamples.hpp" 4 | #include 5 | 6 | // Demonstrate opening a file for reading 7 | TEST(FileTest, BasicLoadingGetline) 8 | { 9 | // This is being run in build but the actual file is outside, so 10 | // we do ../ on loading 11 | std::ifstream input{"../testfile.txt"}; 12 | if (!input.is_open()) 13 | { 14 | ASSERT_TRUE(false); 15 | } 16 | std::string s; 17 | while (!input.eof()) 18 | { 19 | // Getline gets the string but EXCLUDES the newline 20 | getline(input, s); 21 | std::cout << "getline Got string \"" << s << "\"\n"; 22 | } 23 | } 24 | 25 | TEST(FileTest, BasicLoadingGetlineSmartpointer) 26 | { 27 | auto input = std::make_shared("../testfile.txt"); 28 | if (!input->is_open()) 29 | { 30 | ASSERT_TRUE(false); 31 | } 32 | std::string s; 33 | while (!input->eof()) 34 | { 35 | // Getline gets the string but EXCLUDES the newline 36 | getline(*input, s); 37 | std::cout << "getline Got string \"" << s << "\"\n"; 38 | } 39 | } 40 | 41 | TEST(FileTest, OperatorInTest) 42 | { 43 | std::ifstream input{"../testfile.txt"}; 44 | if (!input.is_open()) 45 | { 46 | ASSERT_TRUE(false); 47 | } 48 | std::string s; 49 | while (!input.eof()) 50 | { 51 | input >> s; 52 | std::cout << "<< Got string \"" << s << "\"\n"; 53 | } 54 | } 55 | 56 | TEST(FileTest, OperatorInBetter) 57 | { 58 | // Here is an example of doing an fstream, where we specify it is binary 59 | // (which doesn't affect getline from working) and that we are reading the file 60 | // showing how we can OR two flags together 61 | std::fstream input{"../testfile.txt", std::istream::binary | std::istream::in}; 62 | if (!input.is_open()) 63 | { 64 | ASSERT_TRUE(false); 65 | } 66 | std::string s; 67 | while (!input.eof()) 68 | { 69 | getline(input, s); 70 | std::istringstream ibuf(s); 71 | int i; 72 | ibuf >> i; 73 | if (ibuf) 74 | { 75 | std::cout << "<< Got integer \"" << i << "\"\n"; 76 | std::cout.setf(std::ios_base::hex, std::ios_base::basefield); 77 | std::cout.setf(std::ios_base::showbase); 78 | std::cout << "<< As hex it is \"" << i << "\"\n"; 79 | 80 | std::cout.unsetf(std::ios_base::hex); 81 | std::cout.unsetf(std::ios_base::showbase); 82 | std::cout << "<< and back at default \"" << i << "\"\n"; 83 | 84 | // Can also shortcut 85 | std::cout << std::hex << "<< As hex it is \"" << i << "\"\n"; 86 | std::cout << std::dec << "<< and back at default \"" << i << "\"\n"; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /graph.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef GRAPH_H 3 | #define GRAPH_H 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // C++ is somewhat obnoxious here: You can't do a circular 12 | // reference, so we declare all the classes we will use all up here 13 | // and then declare the bodies later. 14 | 15 | // Additionally, since we are doing things with parameterized typing 16 | // on how we name the Graph nodes, basically ALL the code needs to 17 | // be in the header, as C++'s parameterization is effectively textual 18 | // substitution: replacing the typename T with whatever type is actually 19 | // used in the code. 20 | 21 | template 22 | class GraphNode; 23 | template 24 | class GraphEdge; 25 | template 26 | class Graph; 27 | 28 | template 29 | struct DijkstraIterationStep; 30 | template 31 | class DijkstraTraversal; 32 | template 33 | struct DijkstraTraversalIterator; 34 | 35 | // The primary class for a Graph. 36 | 37 | // This implementation uses an adjacency list within each node (so each node has 38 | // a set of all edges to or from that node, with each edge knowing which 39 | // nodes it starts and ends with), and the Graph 40 | // itself has a name->node mapping. 41 | 42 | // We have to be careful here in distinguishing between std::shared_ptr and 43 | // std::weak_ptr, in order to prevent cycles in the data structure. The idea: 44 | // The Graph class itself has a std::shared_ptr to each node in the Graph. 45 | // Thats fine, now when the Graph is removed all the nodes will be removed. 46 | 47 | // And each node has a shared_ptr to each edge which starts or ends 48 | // at this node, which again is fine as when a node is deleted it will 49 | // delete its edges. 50 | 51 | // The problem comes with the link from the edges back to the nodes. 52 | // If we used std::shared_ptr here, this would mean a node could never 53 | // be deleted because an edge would refer to it, and the edge wouldn't be deleted 54 | // because a node was referring to it, creating a cyclic structure. 55 | 56 | // Thus we have the pointers in the edges be typed as std::weak_ptr instead. A 57 | // weak pointer is a "safe" placeholder. As is, the weak pointer does not count 58 | // as a reference for cleanup purposes, so an edge won't keep a node around needlessly. 59 | // But a weak pointer can't also be accessed directly, instead one needs to 60 | // convert it to a shared_pointer by either calling lock() (which returns a shared 61 | // pointer or nullptr if the underlying object has been freed), or by calling 62 | // std::shared_ptr's constructor on a weak-pointer (which will throw a std::bad_weak_ptr 63 | // exception if the data was already freed) 64 | 65 | template 66 | class Graph : std::enable_shared_from_this> 67 | { 68 | private: 69 | // We store the name->node mapping as an unordered map. 70 | // The unordered map class doesn't guarentee any order 71 | // when iterating over the contents but it is fast: O(1) 72 | // expected to insert new data. 73 | 74 | std::unordered_map>> nodes{}; 75 | 76 | // A set of friend declarations. 77 | friend GraphEdge; 78 | friend GraphNode; 79 | friend DijkstraTraversalIterator; 80 | 81 | // Done so the constructor can't be called except 82 | // by the make_shared factory function create(). 83 | // As a result we will never have to worry about 84 | // Graph objects that aren't automatically managed 85 | // by std::shared_ptr reference counting. 86 | struct Private 87 | { 88 | explicit Private() = default; 89 | }; 90 | 91 | public: 92 | // The constructor doesn't do anything since the 93 | // default constructor for the map creates everything 94 | // just fine. 95 | Graph(Private) 96 | { 97 | } 98 | 99 | // This is an example of a static "Factory" function 100 | // that creates instances of Graph objects as shared 101 | // pointers. 102 | static std::shared_ptr> create() 103 | { 104 | return std::make_shared>(Private()); 105 | } 106 | 107 | // Create a new node in the graph named "name". 108 | // This does a fair bit of checking, making sure 109 | // that the name wasn't already used to create a node. 110 | void create_node(T name) 111 | { 112 | if (nodes.contains(name)) 113 | { 114 | throw std::domain_error("Node already exists"); 115 | } 116 | nodes[name] = std::make_shared>(name); 117 | } 118 | 119 | // Creates a link between to nodes. There can only exist 120 | // one link from a given start to a given end, and each link has a 121 | // weight that is used in the traversal. 122 | void create_link(T start, T end, double weight) 123 | { 124 | // Make sure that the nodes actually exist. 125 | if (!nodes.contains(start) || !nodes.contains(end)) 126 | { 127 | throw std::domain_error("Node does not exist"); 128 | } 129 | 130 | // The edge constructor uses std::weak_ptr, which the 131 | // shared_ptrs are automatically converted to. 132 | auto edge = std::make_shared>(nodes[start], nodes[end], 133 | weight); 134 | for (auto edge : nodes[start]->out_edges) 135 | { 136 | // Our edge->end node is a weak_ptr, so we need 137 | // to convert it back to a shared pointer 138 | // before we access its fields. Note that 139 | // we aren't bothering to check that the 140 | // conversion worked. It would only fail if 141 | // someone mucked with our data structures so 142 | // crashing immediately afterwards with a null pointer 143 | // exception is Just Fine. 144 | auto end_node = edge->end.lock(); 145 | if (end_node->name == end) 146 | { 147 | throw std::domain_error("Edge already exists"); 148 | } 149 | } 150 | nodes[start]->out_edges.insert(edge); 151 | nodes[end]->in_edges.insert(edge); 152 | } 153 | }; 154 | 155 | // The class for the edge. Its pretty simple, with 156 | // just a reference to the starting node, the ending node 157 | // and the weight on the edge. 158 | template 159 | class GraphEdge : std::enable_shared_from_this> 160 | { 161 | 162 | public: 163 | const double weight; 164 | // The big thing however is the start and edge references 165 | // are weak pointers rather than shared_ptrs so as to not 166 | // create a cycle in the reference counting system 167 | const std::weak_ptr> start; 168 | const std::weak_ptr> end; 169 | 170 | GraphEdge(std::weak_ptr> startIn, 171 | std::weak_ptr> endIn, 172 | double weightIn) : weight(weightIn), start(startIn), end(endIn) 173 | { 174 | // This algorithm needs positive weights to work... 175 | if (weight <= 0) 176 | { 177 | throw std::domain_error("Weights must be positive"); 178 | } 179 | } 180 | }; 181 | 182 | // And the class for the node. This is an adjacency list approach, where 183 | // each node has an unordered set of outward edges and a corresponding unordered 184 | // set of inward edges. For our traversal we are only using the outEdges, but 185 | // we include both to enable this class to support other Graph operations. 186 | template 187 | class GraphNode : std::enable_shared_from_this> 188 | { 189 | private: 190 | std::unordered_set>> out_edges{}; 191 | std::unordered_set>> in_edges{}; 192 | friend Graph; 193 | friend GraphEdge; 194 | friend DijkstraTraversalIterator; 195 | 196 | public: 197 | const T name; 198 | 199 | explicit GraphNode(T nameIn) : name(nameIn) 200 | { 201 | } 202 | }; 203 | 204 | /* 205 | * This class is used to return step in the iteration: 206 | * it contains a pointer to the node, the distance to this node from 207 | * the start, and the prior node on the path (if this isn't the starting 208 | * node). 209 | */ 210 | template 211 | struct DijkstraIterationStep 212 | { 213 | public: 214 | std::shared_ptr> current; 215 | double distance = HUGE_VAL; 216 | std::shared_ptr> previous = nullptr; 217 | 218 | explicit DijkstraIterationStep(std::shared_ptr> node) : current(node) 219 | { 220 | } 221 | }; 222 | 223 | // This is the heart of the calculation. In C++ iterators are somewhat 224 | // complex: the root object needs to support begin() and end() which return 225 | // the iteration objects themselves. The iteration object for the beginning needs 226 | // to support ++ (increment), * (get the current element) and != (is it at the end). 227 | // 228 | // The end object is effectively a dummy in this case, but it needs to be supported 229 | // anyway. It will create a needless empty map in the process, but ah well. 230 | // 231 | // Basically, in C++, a loop like 232 | 233 | // for(auto a : b) { ... } 234 | 235 | // gets converted into: 236 | // auto iterStart = b.begin() 237 | // auto iterEnd = b.end() 238 | // while(iterStart != iterEnd; ) { 239 | // auto a = *iterStart; 240 | // ... 241 | // iterStart++;} 242 | 243 | // This means that * will be called for each time through the loop 244 | // and ++ will be called just before the ending is checked. 245 | 246 | template 247 | struct DijkstraTraversalIterator : std::input_iterator_tag 248 | { 249 | friend DijkstraTraversal; 250 | 251 | private: 252 | // The working set maps GraphNodes (as shared ptrs) to the 253 | // associated iteration information (which contains the node, the distance, 254 | // and the prior node.) 255 | std::unordered_map>, 256 | std::shared_ptr>> 257 | working_set; 258 | std::shared_ptr> current_node = nullptr; 259 | const std::shared_ptr> working_graph; 260 | 261 | // The private constructor for the iterator. If its the end it does nothing. 262 | // If it is the beginning it creates the working set and initializes all the 263 | // distances to +infinity, except for the start which it initializes to zero. 264 | 265 | // Once done it calls the intnernal iteration function once so that current_node 266 | // will be pointing to the first node in the traversal (which is the start node). 267 | // and the first iteration of the calculation will be executed. 268 | DijkstraTraversalIterator(std::shared_ptr> graph_ptr, T start, bool is_beginning) : working_graph(graph_ptr) 269 | { 270 | // Only do the work for the beginning iterator. The end iterator 271 | // is effectively a dummy. 272 | if (is_beginning) 273 | { 274 | if (!working_graph->nodes.contains(start)) 275 | { 276 | throw std::logic_error("Unable to find the node"); 277 | } 278 | // Iterator for maps return an object where the .first field is the key and 279 | // the .second field is the value. So for this it is the name and the 280 | // GraphNode object itself. 281 | for (auto [name, node] : working_graph->nodes) 282 | { 283 | auto element = std::make_shared>(node); 284 | if (name == start) 285 | { 286 | element->distance = 0; 287 | } 288 | working_set[node] = element; 289 | } 290 | // Does a single step of the iterator so we are all queued up 291 | // at the first element. 292 | this->iter(); 293 | } 294 | } 295 | 296 | // And this is the heart of the iteration step. It clears the current node 297 | // and first finds the closest node remaining in the working set which is the 298 | // new current node (or returns if nothing to do). 299 | // 300 | // If the distance for the current node is still +infinity it sets the current to 301 | // nullptr and returns. 302 | // 303 | // It removes that node from the working set and then for each outbound edge it looks 304 | // up the destination. If that destination is in the working set, it checks the 305 | // distance. If the new distance would be less it reduces the distance and updates 306 | // the previous node on the record. 307 | void iter() 308 | { 309 | current_node = nullptr; 310 | if (working_set.size() == 0) 311 | { 312 | return; 313 | } 314 | // The iterator for a map automatically has first as the key 315 | // which is the node itself and 316 | // second as the value, which in this case is a DijkstraTraversal 317 | for (auto [name, node] : working_set) 318 | { 319 | (void) name; 320 | if (current_node == nullptr) 321 | { 322 | current_node = node; 323 | } 324 | if (node->distance < current_node->distance) 325 | { 326 | current_node = node; 327 | } 328 | } 329 | working_set.erase(current_node->current); 330 | if (current_node->distance == HUGE_VAL) 331 | { 332 | current_node = nullptr; 333 | return; 334 | } 335 | for (auto itr : current_node->current->out_edges) 336 | { 337 | // start and end are weak pointers so lets make the 338 | // shared version for the actual work here. 339 | auto start = itr->start.lock(); 340 | auto end = itr->end.lock(); 341 | if (working_set.contains(end)) 342 | { 343 | auto distance = current_node->distance + itr->weight; 344 | if (distance < working_set[end]->distance) 345 | { 346 | working_set[end]->distance = distance; 347 | working_set[end]->previous = current_node->current; 348 | } 349 | } 350 | } 351 | } 352 | 353 | public: 354 | // The ++ operator is the part that calls the iterator to 355 | // make sure the current node is available. 356 | void operator++() 357 | { 358 | iter(); 359 | } 360 | 361 | // And the * operator returns the current node. 362 | std::shared_ptr> operator*() 363 | { 364 | return current_node; 365 | } 366 | 367 | // And this is "is there still data left". The contract for the 368 | // iterator says that ++ is called AFTER the data is accessed and before 369 | // this is called, so we know it will be executed in the loop in order: If there is 370 | // no data left the != operation will be checked before the next call 371 | // to *. 372 | bool operator!=(DijkstraTraversalIterator &) 373 | { 374 | return current_node != nullptr; 375 | } 376 | }; 377 | 378 | // 379 | // And this is the basic shell for the above iterator. 380 | // The constructor accepts the graph and the starting node, 381 | // while the object itself returns the iterator using begin() 382 | // and end(). 383 | // 384 | // The reason why C++ requires returing TWO iterators, while 385 | // just about every other language with iteration primatives uses 386 | // one is because C++ bears a lot of old legacy. Iterators were initially 387 | // designed to do things like iterate over an array's internal storage, 388 | // and the start and end were just pointers to the first element and one plus 389 | // the last element, and the ++ was just doing pointer arithmatic. 390 | template 391 | class DijkstraTraversal 392 | { 393 | 394 | public: 395 | const std::shared_ptr> working_graph; 396 | const T start; 397 | DijkstraTraversal(std::shared_ptr> g, T s) : working_graph(g), start(s) 398 | { 399 | } 400 | 401 | DijkstraTraversalIterator begin() 402 | { 403 | return DijkstraTraversalIterator(working_graph, start, true); 404 | } 405 | 406 | DijkstraTraversalIterator begin() const 407 | { 408 | return DijkstraTraversalIterator(working_graph, start, true); 409 | } 410 | 411 | DijkstraTraversalIterator end() 412 | { 413 | return DijkstraTraversalIterator(working_graph, start, false); 414 | } 415 | 416 | DijkstraTraversalIterator end() const 417 | { 418 | return DijkstraTraversalIterator(working_graph, start, false); 419 | } 420 | }; 421 | 422 | #endif -------------------------------------------------------------------------------- /graph_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "graph.hpp" 4 | #include 5 | 6 | // Demonstrate some basic assertions. 7 | TEST(GraphTest, Comprehensive) 8 | { 9 | auto array = std::vector(10); 10 | auto array2 = std::vector(10); 11 | 12 | int i; 13 | for (i = 0; i < 10; ++i) 14 | { 15 | array[i] = i; 16 | array2[i] = i; 17 | } 18 | auto rng = std::default_random_engine{}; 19 | for (auto k = 0; k < 10; ++k) 20 | { 21 | auto g = Graph::create(); 22 | std::shuffle(std::begin(array), std::end(array), rng); 23 | for (i = 0; i < 10; ++i) 24 | { 25 | g->create_node(array[i]); 26 | } 27 | std::shuffle(std::begin(array), std::end(array), rng); 28 | for (i = 0; i < 10; ++i) 29 | { 30 | g->create_link(array[i], (array[i] + 1) % 10, 1.0); 31 | EXPECT_THROW(g->create_link(array[i], (array[i] + 1) % 10, 32.3), std::domain_error); 32 | } 33 | i = 0; 34 | 35 | for(auto step : DijkstraTraversal(g, 0)) { 36 | EXPECT_TRUE(step->current->name == i); 37 | EXPECT_TRUE(step->distance == float(i)); 38 | if(i != 0) { 39 | EXPECT_TRUE(step->previous->name == (i-1)); 40 | } else { 41 | EXPECT_TRUE(step->previous == nullptr); 42 | } 43 | i++; 44 | } 45 | for(i = 0; i < 10; ++i) { 46 | for(auto j = 0; j < 10; ++j) { 47 | if((i + 1) % 10 != j) { 48 | g->create_link(i,j,11); 49 | } 50 | } 51 | } 52 | i = 0; 53 | for(auto step : DijkstraTraversal(g, 0)) { 54 | EXPECT_TRUE(step->current->name == i); 55 | EXPECT_TRUE(step->distance == float(i)); 56 | i++; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /llist.cpp: -------------------------------------------------------------------------------- 1 | #include "llist.hpp" 2 | 3 | // This file is effectively a noop, as templates pretty much require all 4 | // the code to be in the header 5 | -------------------------------------------------------------------------------- /llist.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _LLIST_H 2 | #define _LLIST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Yes, C++ standard containers already have a similar (and indeed more advanced) 11 | // version called std::list, but we are doing this as an example of how 12 | // to program references in C++ and similar work. 13 | 14 | template 15 | class LinkedListCell; 16 | template 17 | class LinkedList; 18 | 19 | class ListException : public std::exception 20 | { 21 | public: 22 | ListException(const std::string &in) { _msg = in; } 23 | virtual const char *what() { return _msg.c_str(); } 24 | 25 | protected: 26 | std::string _msg; 27 | }; 28 | 29 | template 30 | class LinkedListIterator : std::input_iterator_tag 31 | { 32 | public: 33 | void operator++() 34 | { 35 | _at = _at->_next; 36 | } 37 | 38 | bool operator!=(LinkedListIterator &comp) 39 | { 40 | (void)comp; // Suppresses unused variable compiler warning 41 | return _at != nullptr; 42 | } 43 | 44 | T &operator*() 45 | { 46 | return _at->_data; 47 | } 48 | 49 | LinkedListIterator(std::shared_ptr> at) { _at = at; } 50 | 51 | protected: 52 | std::shared_ptr> _at; 53 | }; 54 | 55 | template 56 | class ConstLinkedListIterator : std::input_iterator_tag 57 | { 58 | public: 59 | void operator++() 60 | { 61 | _at = _at->_next; 62 | } 63 | 64 | bool operator!=(const ConstLinkedListIterator &comp) 65 | { 66 | (void)comp; // Suppresses unused variable compiler warning 67 | return _at != nullptr; 68 | } 69 | 70 | const T &operator*() 71 | { 72 | return _at->_data; 73 | } 74 | 75 | ConstLinkedListIterator(std::shared_ptr> at) { _at = at; } 76 | 77 | protected: 78 | std::shared_ptr> _at; 79 | }; 80 | 81 | template 82 | class LinkedListCell 83 | { 84 | public: 85 | friend class LinkedList; 86 | friend class LinkedListIterator; 87 | friend class ConstLinkedListIterator; 88 | 89 | LinkedListCell(const T &data, std::shared_ptr> next) 90 | { 91 | _next = next; 92 | _data = data; 93 | } 94 | 95 | protected: 96 | T _data; 97 | std::shared_ptr> _next; 98 | }; 99 | 100 | template 101 | class LinkedList 102 | { 103 | public: 104 | LinkedList() 105 | { 106 | _head = nullptr; 107 | _tail = nullptr; 108 | _len = 0; 109 | } 110 | 111 | // In an attempt to bring things into line with 112 | // standard C++ libraries, assignment is a deep copy 113 | // (like string or vectors are). 114 | LinkedList &operator=(const LinkedList &other) 115 | { 116 | // For self assignment we do nothing... 117 | if (&other == this) 118 | { 119 | return *this; 120 | } 121 | 122 | // Otherwise, we erase our old data... 123 | _head = nullptr; 124 | _tail = nullptr; 125 | _len = 0; 126 | for (const auto &data : other) 127 | { 128 | append(data); 129 | } 130 | return *this; 131 | } 132 | 133 | LinkedList &operator=(LinkedList &&other){ 134 | _head = other._head; 135 | _tail = other._tail; 136 | _len = other._len; 137 | other._tail = nullptr; 138 | other._head = nullptr; 139 | other._len = 0; 140 | return *this; 141 | } 142 | 143 | LinkedList(const LinkedList &other) 144 | { 145 | _head = nullptr; 146 | _tail = nullptr; 147 | _len = 0; 148 | for (const auto &data : other) 149 | { 150 | append(data); 151 | } 152 | } 153 | 154 | LinkedList(LinkedList &&other){ 155 | _head = other._head; 156 | _tail = other._tail; 157 | _len = other._len; 158 | other._tail = nullptr; 159 | other._head = nullptr; 160 | other._len = 0; 161 | } 162 | 163 | 164 | 165 | virtual size_t len() { return _len; } 166 | 167 | virtual void prepend( T &data) 168 | { 169 | _head = std::make_shared>(data, _head); 170 | if(!_tail){ 171 | _tail = _head; 172 | } 173 | _len++; 174 | } 175 | 176 | virtual void append(const T &data) 177 | { 178 | if (!_head) 179 | { 180 | _head = std::make_shared>(data, nullptr); 181 | _tail = _head; 182 | _len++; 183 | return; 184 | } 185 | 186 | _tail->_next = std::make_shared>(data, nullptr); 187 | _tail = _tail->_next; 188 | _len++; 189 | } 190 | 191 | virtual T &operator[](size_t location) 192 | { 193 | auto at = _head; 194 | while (true) 195 | { 196 | if (at == nullptr) 197 | throw ListException("Index out of range"); 198 | if (location == 0) 199 | return at->_data; 200 | location--; 201 | at = at->_next; 202 | } 203 | } 204 | 205 | virtual LinkedListIterator begin() const 206 | { 207 | return LinkedListIterator(_head); 208 | }; 209 | virtual LinkedListIterator end() const 210 | { 211 | return LinkedListIterator(nullptr); 212 | } 213 | 214 | virtual ConstLinkedListIterator cbegin() const 215 | { 216 | return ConstLinkedListIterator(_head); 217 | }; 218 | virtual ConstLinkedListIterator cend() const 219 | { 220 | return ConstLinkedListIterator(nullptr); 221 | } 222 | 223 | protected: 224 | std::shared_ptr> _head; 225 | std::shared_ptr> _tail; 226 | size_t _len; 227 | }; 228 | 229 | template 230 | std::string to_string(LinkedList &in) 231 | { 232 | std::stringstream s; 233 | s << "["; 234 | auto length = in.len(); 235 | size_t i = 0; 236 | for (auto item : in) 237 | { 238 | s << item; 239 | i++; 240 | if (i < length) 241 | s << ", "; 242 | } 243 | s << "]"; 244 | return s.str(); 245 | } 246 | 247 | // Note that for all of these the need 248 | // to return a new list does an additional copying. 249 | // 250 | // One is probably better served in C++20 to use the 251 | // std::views | operations and just iterate rather than 252 | // create a new list, but sometimes you do want a new list 253 | template 254 | LinkedList list_map(LinkedList &in, 255 | std::function f) 256 | { 257 | LinkedList ret; 258 | for (const auto &c : in) 259 | { 260 | ret.append(f(c)); 261 | } 262 | return ret; 263 | } 264 | 265 | template 266 | U list_reduce(LinkedList &in, 267 | std::function f, U initval) 268 | { 269 | for (const auto &c : in) 270 | { 271 | initval = f(initval, c); 272 | } 273 | return initval; 274 | } 275 | 276 | template 277 | LinkedList list_filter(LinkedList &in, 278 | std::function f) 279 | { 280 | LinkedList ret; 281 | for (auto c : in) 282 | { 283 | if (f(c)) 284 | ret.append(c); 285 | } 286 | return ret; 287 | } 288 | 289 | #endif -------------------------------------------------------------------------------- /llist_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "llist.hpp" 4 | 5 | // Demonstrate some basic assertions. 6 | TEST(LinkedListTest, AppendingOnEnd) 7 | { 8 | LinkedList foo, bar; 9 | EXPECT_EQ(foo.len(), 0); 10 | for (auto i = 0; i < 10; ++i) 11 | { 12 | EXPECT_EQ(foo.len(), i); 13 | EXPECT_EQ(bar.len(), i); 14 | EXPECT_THROW(foo[i], ListException); 15 | foo.append(i); 16 | bar.prepend(i); 17 | EXPECT_EQ(foo[i], i); 18 | foo[i] = 2 * i; 19 | EXPECT_EQ(foo[i], 2 * i); 20 | } 21 | for (auto i = 0; i < 10; ++i) 22 | { 23 | EXPECT_EQ(bar[i], 9 - i); 24 | EXPECT_EQ(foo[i], 2 * i); 25 | } 26 | auto j = 0; 27 | for (auto &i : foo) 28 | { 29 | EXPECT_EQ(i, j * 2); 30 | i = j; 31 | j++; 32 | } 33 | j = 0; 34 | for (auto i : foo) 35 | { 36 | EXPECT_EQ(i, j); 37 | j++; 38 | } 39 | EXPECT_EQ(to_string(foo), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"); 40 | 41 | auto moved = std::move(foo); 42 | 43 | EXPECT_EQ(to_string(moved), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"); 44 | EXPECT_EQ(to_string(foo), "[]"); 45 | 46 | foo = std::move(moved); 47 | 48 | EXPECT_EQ(to_string(foo), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"); 49 | EXPECT_EQ(to_string(moved), "[]"); 50 | 51 | LinkedList baz; 52 | baz.append("Hola"); 53 | baz.append("Hello"); 54 | 55 | std::cout << to_string(baz) << "\n"; 56 | std::cout << to_string(foo) << "\n"; 57 | std::cout << to_string(bar) << "\n"; 58 | } 59 | 60 | std::string stringify(int i) 61 | { 62 | return "\"" + std::to_string(i) + "\""; 63 | } 64 | 65 | bool iseven(int i) 66 | { 67 | return (i % 2) == 0; 68 | } 69 | 70 | TEST(LinkedListTest, FunctionalTesting) 71 | { 72 | LinkedList foo; 73 | 74 | for (auto i = 0; i < 10; ++i) 75 | { 76 | foo.append(i); 77 | } 78 | 79 | auto filtered = list_filter(foo, iseven); 80 | EXPECT_EQ(to_string(filtered), "[0, 2, 4, 6, 8]"); 81 | 82 | filtered = filtered; 83 | EXPECT_EQ(to_string(filtered), "[0, 2, 4, 6, 8]"); 84 | 85 | auto filtered2 = filtered; 86 | EXPECT_EQ(to_string(filtered2), "[0, 2, 4, 6, 8]"); 87 | filtered2[0] = 20; 88 | EXPECT_EQ(to_string(filtered2), "[20, 2, 4, 6, 8]"); 89 | EXPECT_EQ(to_string(filtered), "[0, 2, 4, 6, 8]"); 90 | 91 | // Trailing type on the lambda isn't strictly necessary, 92 | filtered = list_filter(foo, [](auto i) -> bool 93 | { return i % 2; }); 94 | EXPECT_EQ(to_string(filtered), "[1, 3, 5, 7, 9]"); 95 | 96 | filtered = list_filter(foo, [](auto i) 97 | { return i % 2; }); 98 | EXPECT_EQ(to_string(filtered), "[1, 3, 5, 7, 9]"); 99 | 100 | auto baz = list_map(foo, 101 | stringify); 102 | EXPECT_EQ(to_string(baz), "[\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"]"); 103 | 104 | auto bar = list_map(foo, 105 | [](auto x) 106 | { return x + 1; }); 107 | EXPECT_EQ(to_string(bar), "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"); 108 | 109 | const auto incr = 10; 110 | bar = list_map(foo, 111 | [](auto x) 112 | { return x + incr; }); 113 | EXPECT_EQ(to_string(bar), "[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]"); 114 | 115 | auto garplay = list_reduce(foo, [](auto x, auto y) 116 | { return x + y; }, 0); 117 | EXPECT_EQ(garplay, 45); 118 | } 119 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "confuzzle.h" 6 | 7 | /* This routine checks to make sure we are running on a Linux system 8 | such as in a container. As a requriemnet for the class we are runnig on 9 | Linux evironments so if you are using Windows or Mac you should be running in 10 | a container */ 11 | void check_unix(){ 12 | FILE *f = popen("/usr/bin/uname", "r"); 13 | char data[512]; 14 | fgets(data,512,f); 15 | pclose(f); 16 | if(strcmp(data,"Linux\n")){ 17 | printf("This program must be run in a Linux environment\n"); 18 | exit(0); 19 | 20 | } 21 | } 22 | 23 | int main(int argc, char **argv) { 24 | printf("Hello World!!!\n"); 25 | check_unix(); 26 | if (argc < 2) { 27 | printf("Specify student ID as a command line argument\n"); 28 | exit(0); 29 | } 30 | printf("Confuzzle executing, return value %i\n", confuzzle(argv[1])); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /slice.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SLICE_HPP 2 | #define SLICE_HPP 3 | 4 | #include 5 | #include 6 | 7 | class SliceException : public std::exception 8 | { 9 | public: 10 | SliceException(const std::string &in) { _msg = in; } 11 | virtual const char *what() { return _msg.c_str(); } 12 | 13 | protected: 14 | std::string _msg; 15 | }; 16 | 17 | class SliceOutOfBoundsException : public SliceException 18 | { 19 | public: 20 | SliceOutOfBoundsException(const std::string &in) : SliceException(in) {}; 21 | }; 22 | 23 | template 24 | class Slice 25 | { 26 | public: 27 | Slice() 28 | { 29 | _data = std::make_shared>(); 30 | _start = 0; 31 | _len = 0; 32 | } 33 | 34 | // Subrange 35 | Slice(Slice &s, int start, int end) 36 | { 37 | _data = s._data; 38 | auto tmp = ((int)(s._start) + start); 39 | if (tmp < 0) 40 | throw SliceException("Negative Start"); 41 | if (end < start) 42 | throw SliceException("End before start"); 43 | _start = (size_t)tmp; 44 | _len = (size_t)end - start + 1; 45 | if (_len > _data->size()) 46 | throw SliceException("Can't have slice beyond end"); 47 | } 48 | 49 | Slice(std::vector &from, int start, int end) { 50 | _data = std::make_shared>(from); 51 | auto tmp = (start); 52 | if (tmp < 0) 53 | throw SliceException("Negative Start"); 54 | if (end < start) 55 | throw SliceException("End before start"); 56 | _start = (size_t)tmp; 57 | _len = (size_t)end - start + 1; 58 | if (_len > _data->size()) 59 | throw SliceException("Can't have slice beyond end"); 60 | } 61 | 62 | void push_back(const T &value) 63 | { 64 | if (_start + _len >= _data->size()) 65 | { 66 | _data->push_back(value); 67 | _len += 1; 68 | } 69 | else { 70 | _len += 1; 71 | (*this)[_len-1] = value; 72 | } 73 | } 74 | 75 | T &operator[](size_t pos) 76 | { 77 | if (pos >= _len) 78 | throw SliceOutOfBoundsException("Exceeded Bounds"); 79 | if (pos + _start >= _data->size()) 80 | throw SliceOutOfBoundsException("Exceeds Size"); 81 | return (*_data)[pos + _start]; 82 | } 83 | 84 | private: 85 | std::shared_ptr> _data; 86 | size_t _start; 87 | size_t _len; 88 | 89 | 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /slice_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "slice.hpp" 4 | #include 5 | 6 | TEST(SliceTest, AppendingOnEnd) 7 | { 8 | Slice foo; 9 | std::cout << "Running \n"; 10 | for(auto x = 0; x < 10; ++x){ 11 | foo.push_back(x); 12 | ASSERT_EQ(foo[x], x); 13 | } 14 | for(auto x = 0; x < 10; ++x){ 15 | ASSERT_EQ(foo[x], x); 16 | } 17 | Slice bar(foo, 1, 4); 18 | for(auto x = 0; x < 4; ++x){ 19 | ASSERT_EQ(bar[x], x+1); 20 | } 21 | for(auto x = 4; x < 10; ++x){ 22 | EXPECT_THROW(bar[x], SliceOutOfBoundsException); 23 | } 24 | EXPECT_THROW(Slice baz(bar, -10, 10), SliceException); 25 | bar.push_back(32); 26 | EXPECT_EQ(foo[5], 32); 27 | std::vector baz({1, 2, 3, 4}); 28 | Slice garplay(baz, 1, 2); 29 | EXPECT_EQ(garplay[0], 2); 30 | EXPECT_EQ(garplay[1], 3); 31 | EXPECT_THROW(garplay[3], SliceOutOfBoundsException); 32 | } 33 | -------------------------------------------------------------------------------- /stringexamples.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "stringexamples.hpp" 4 | #include "confuzzle.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | std::string toupper(std::string s) 11 | { 12 | for (auto &c : s) 13 | { 14 | #pragma GCC diagnostic push 15 | #pragma GCC diagnostic ignored "-Wconversion" 16 | c = toupper(c); 17 | #pragma GCC diagnostic pop 18 | } 19 | return s; 20 | } 21 | 22 | std::string reverse(std::string s) 23 | { 24 | reverse(s.begin(), s.end()); 25 | return s; 26 | } 27 | 28 | std::string stripl(const std::string &s) 29 | { 30 | auto index = 0; 31 | for (auto c : s) 32 | { 33 | if (!isspace(c)) 34 | { 35 | break; 36 | } 37 | index++; 38 | } 39 | return s.substr(index); 40 | } 41 | 42 | /* 43 | 44 | std::string stripr(std::string s){ 45 | auto len = s.length(); 46 | for (auto citerator = s.rbegin(); citerator != s.rend(); ++citerator){ 47 | if(!isspace(*citerator)) { 48 | break; 49 | } 50 | len--; 51 | } 52 | return s.substr(0, len); 53 | } 54 | 55 | 56 | */ 57 | 58 | std::string stripr(const std::string &s) 59 | { 60 | auto len = s.length(); 61 | for (auto c : s | std::views::reverse) 62 | { 63 | if (!isspace(c)) 64 | { 65 | break; 66 | } 67 | len--; 68 | } 69 | return s.substr(0, len); 70 | } 71 | -------------------------------------------------------------------------------- /stringexamples.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _STRINGEXAMPLES_H 2 | #define _STRINGEXAMPLES_H 3 | 4 | std::string toupper(std::string s); 5 | std::string reverse(std::string s); 6 | std::string stripl(const std::string &s); 7 | std::string stripr(const std::string &s); 8 | 9 | #endif -------------------------------------------------------------------------------- /stringexamples_c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "stringexamples_c.h" 6 | 7 | char *toupper_str(const char *s){ 8 | char *ret = (char *) calloc(sizeof(char), strlen(s)+1); 9 | size_t i; 10 | for(i = 0; i 2 | #include 3 | extern "C" { 4 | #include "stringexamples_c.h" 5 | } 6 | 7 | // Demonstrate some basic assertions. 8 | TEST(CStringTest, BasicAssertions) { 9 | // This should be equal by the std::string equality, which is 10 | // what we REALLY want. 11 | char *ret; 12 | ret = toupper_str("ThiS iS a TeST"); 13 | EXPECT_STREQ("THIS IS A TEST", ret); 14 | EXPECT_STRNE("This IS A TEST", ret); 15 | free(ret); 16 | } -------------------------------------------------------------------------------- /stringexamples_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stringexamples.hpp" 4 | 5 | // Demonstrate some basic assertions. 6 | TEST(StringTest, BasicAssertions) { 7 | // This should be equal by the std::string equality, which is 8 | // what we REALLY want. 9 | EXPECT_EQ("THIS IS A TEST", toupper("ThiS iS a TeST")); 10 | EXPECT_NE("This IS A TEST", toupper("ThiS iS a TeST")); 11 | 12 | 13 | EXPECT_EQ("gnip", reverse("ping")); 14 | EXPECT_NE("gnip ", reverse("ping")); 15 | EXPECT_EQ("test", stripl(" \t\n\rtest")); 16 | EXPECT_EQ("", stripl(" \t\n ")); 17 | EXPECT_EQ("test", stripl("test")); 18 | 19 | EXPECT_EQ("test ", stripl(" \t\ntest ")); 20 | EXPECT_EQ(" \t\ntest", stripr(" \t\ntest \t")); 21 | 22 | // But this also shows the limitation of C++'s string handling, 23 | // by passing by value we end up doing extra copying. But such 24 | // is life if we actually want sane strings 25 | } -------------------------------------------------------------------------------- /testfile.txt: -------------------------------------------------------------------------------- 1 | This is a test file 2 | 1234 5678 3 | for various I/O routines -------------------------------------------------------------------------------- /tuple_map_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | std::pair foo() 9 | { 10 | return {2, 3.14159}; 11 | } 12 | std::tuple bar() 13 | { 14 | return {13, 7.8, "this is a string"}; 15 | } 16 | 17 | TEST(TupleTests, BasicDestructuring) 18 | { 19 | auto [a, b] = foo(); 20 | auto [c, d, e] = bar(); 21 | std::cout << a << " " << b << "\n"; 22 | std::cout << c << " " << d << " " << e << "\n"; 23 | } 24 | 25 | TEST(TupleTests, ManualDestructuring) 26 | { 27 | auto baz = foo(); 28 | std::cout << baz.first << " " << baz.second << "\n"; 29 | // Pairs have the field names .first, and .second that can be 30 | // accessed 31 | auto garplay = bar(); 32 | std::cout << std::get<0>(garplay) << " " << std::get<1>(garplay) << " " << std::get<2>(garplay) << "\n"; 33 | } 34 | 35 | TEST(TupleTests, PipeSorting) 36 | { 37 | std::vector test = {"foo", "bar", "baz", "garplay"}; 38 | for (auto s : test) 39 | { 40 | std::cout << s << ", "; 41 | } 42 | std::cout << "\n"; 43 | std::sort(test.begin(), test.end()); 44 | for (auto s : test) 45 | { 46 | std::cout << s << ", "; 47 | } 48 | std::cout << "\n"; 49 | for (auto s : test | std::views::reverse) 50 | { 51 | std::cout << s << ", "; 52 | } 53 | std::cout << "\n"; 54 | for (auto s : test | std::views::filter([](std::string s) 55 | { return s == "foo"; })) 56 | { 57 | std::cout << s << ", "; 58 | } 59 | std::cout << "\n"; 60 | 61 | for (auto s : test | std::views::transform([](std::string s) 62 | { return s.length(); })) 63 | { 64 | std::cout << s << ", "; 65 | } 66 | std::cout << "\n"; 67 | 68 | for (auto i : std::views::iota(0, 10)) 69 | { 70 | std::cout << i << ", "; 71 | } 72 | std::cout << "\n"; 73 | 74 | for (auto i : std::views::iota(0) | std::views::take(10)) 75 | { 76 | std::cout << i << ", "; 77 | } 78 | std::cout << "\n"; 79 | 80 | for (auto i : std::views::iota(0) | std::views::take(10) | std::views::reverse) 81 | { 82 | std::cout << i << ", "; 83 | } 84 | std::cout << "\n"; 85 | 86 | for (auto i : std::views::iota(0) | 87 | std::views::filter([](auto i) 88 | { return i % 2 == 0; }) | 89 | std::views::take(10) | std::views::reverse) 90 | { 91 | std::cout << i << ", "; 92 | } 93 | std::cout << "\n"; 94 | 95 | auto allEven = std::views::iota(0) | std::views::filter([](auto i) 96 | { return i % 2 == 0; }); 97 | for (auto i : allEven | std::views::take(10)) 98 | { 99 | std::cout << i << ", "; 100 | } 101 | std::cout << "\n"; 102 | 103 | for (auto i : allEven | std::views::drop(10) | std::views::take(10)) 104 | { 105 | std::cout << i << ", "; 106 | } 107 | std::cout << "\n"; 108 | 109 | std::map testmap = { {"fubar", 42 } }; 110 | for(auto i : std::views::iota(0, 10) | std::views::reverse ) { 111 | std::stringstream s; 112 | s << "The value is " << i; 113 | testmap[s.str()] = i; 114 | } 115 | for(auto [key, value] : testmap){ 116 | std::cerr << "Key: " << key << " Value: " << value << "\n"; 117 | } 118 | 119 | std::multimap example = {{1, 'a'}, {2, 'b'}, {2, 'c'}, {3, 'x'}}; 120 | for(auto search = example.find(2); search != example.end(); ++search){ 121 | auto [key, value] = *search; 122 | std::cout << "Key : " << key << " Value: " << value << "\n"; 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /workqueue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WORKQUEUE_HPP 2 | #define WORKQUEUE_HPP 3 | 4 | #include 5 | #include 6 | 7 | template 8 | class WorkQueue 9 | { 10 | private: 11 | public: 12 | WorkQueue() {}; 13 | // It doesn't make sense to hold 0 elements, and 14 | // we use 0 capacity to be "unlimited capacity" 15 | WorkQueue(size_t size) 16 | { 17 | capacity = size; 18 | assert(size > 0); 19 | }; 20 | 21 | // We define our WorkQueue as NOT being copyable or movable, as it has non-copyable 22 | // components in it, and it would be undefined behavior even to 23 | // move it with things waiting on the old condition variables. To 24 | // actually make it moveable there would need to be an extra layer of 25 | // indirection to allow not having to actually move the queue and 26 | // condition variable. 27 | WorkQueue(const WorkQueue &) = delete; 28 | void operator=(const WorkQueue &) = delete; 29 | 30 | void put(T &element) 31 | { 32 | // This convention is so that we don't cause a 33 | // "wake up and lock again" immediately on the other 34 | // thread. Instead we see if we will need to 35 | // notify and if so, notify AFTER we release the lock. 36 | bool wasempty = false; 37 | { 38 | std::unique_lock l(lock); 39 | // If there are already more elements than capacity 40 | // we wait. Note that capacity 0 is special so... 41 | while (capacity != 0 && 42 | data.size() >= capacity) 43 | { 44 | notify_put.wait(l); 45 | } 46 | wasempty = data.empty(); 47 | data.push(element); 48 | } 49 | if (wasempty) 50 | notify_get.notify_one(); 51 | } 52 | 53 | T get() 54 | { 55 | bool wasfull = false; 56 | std::unique_lock l(lock); 57 | while (data.empty()) 58 | { 59 | notify_get.wait(l); 60 | } 61 | if (data.size() >= capacity) 62 | { 63 | wasfull = true; 64 | } 65 | auto ret = data.front(); 66 | data.pop(); 67 | // Doing an explicit unlock rather than RAII unlock because 68 | // of scoping issues with ret. 69 | l.unlock(); 70 | if (wasfull) 71 | { 72 | notify_put.notify_one(); 73 | } 74 | return ret; 75 | } 76 | 77 | private: 78 | std::queue data; 79 | std::mutex lock; 80 | std::condition_variable notify_get; 81 | std::condition_variable notify_put; 82 | size_t capacity = 0; 83 | }; 84 | 85 | #endif -------------------------------------------------------------------------------- /workqueue_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "workqueue.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Demonstrate some basic assertions. 10 | TEST(WorkQueue, BasicTest) 11 | { 12 | // auto rng = std::default_random_engine(); 13 | 14 | for (auto y : std::views::iota(0, 20)) 15 | { 16 | WorkQueue w; 17 | 18 | 19 | std::jthread j([&]() 20 | { 21 | std::cout << "."; 22 | std::cout.flush(); 23 | for (auto i : std::views::iota(0, 100)) 24 | { 25 | std::chrono::milliseconds ms{std::rand() % 3}; 26 | std::this_thread::sleep_for(ms); 27 | EXPECT_EQ(w.get(), i); 28 | } 29 | }); 30 | (void)y; 31 | for (auto i : std::views::iota(0, 100)) 32 | { 33 | std::chrono::milliseconds ms{std::rand() % 3}; 34 | std::this_thread::sleep_for(ms); 35 | w.put(i); 36 | } 37 | } 38 | std::cout << "\n"; 39 | std::cout << RAND_MAX << "\n"; 40 | } 41 | 42 | 43 | TEST(WorkQueue, LittleCapacity) 44 | { 45 | // auto rng = std::default_random_engine(); 46 | 47 | for (auto y : std::views::iota(0, 20)) 48 | { 49 | WorkQueue w(1); 50 | 51 | 52 | std::jthread j([&]() 53 | { 54 | std::cout << "."; 55 | std::cout.flush(); 56 | for (auto i : std::views::iota(0, 100)) 57 | { 58 | std::chrono::milliseconds ms{std::rand() % 3}; 59 | std::this_thread::sleep_for(ms); 60 | EXPECT_EQ(w.get(), i); 61 | } 62 | }); 63 | (void)y; 64 | for (auto i : std::views::iota(0, 100)) 65 | { 66 | std::chrono::milliseconds ms{std::rand() % 3}; 67 | std::this_thread::sleep_for(ms); 68 | w.put(i); 69 | } 70 | } 71 | std::cout << "\n"; 72 | std::cout << RAND_MAX << "\n"; 73 | } 74 | 75 | 76 | --------------------------------------------------------------------------------