├── .circleci └── config.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── _config.yml ├── codecov.yml ├── example └── example.cpp ├── include └── linear_linked_list.hpp ├── premake5.lua ├── release └── linear_linked_list.hpp ├── src └── linear_linked_list.cpp ├── tests ├── linked_list_test.cpp └── test_config_main.cpp └── third_party └── catch.hpp /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | machine: true 6 | steps: 7 | - checkout 8 | - run: 9 | name: Download Premake5 10 | command: 'wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha13/premake-5.0.0-alpha13-linux.tar.gz' 11 | - run: 12 | name: Install Premake5 13 | command: 'tar -xvf premake-5.0.0-alpha13-linux.tar.gz' 14 | - run: 15 | name: Creating Build Files 16 | command: './premake5 gmake' 17 | - run: 18 | name: debug build 19 | command: 'make -C gmake config=debug verbose=true' 20 | - run: 21 | name: release build 22 | command: 'make -C gmake config=release verbose=true' 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | tests/debug/* 34 | 35 | #Visual Studio folders 36 | .vs/ 37 | vs*/ 38 | 39 | 40 | # Swp files 41 | *.sw? 42 | 43 | # Generated Directories 44 | bin/ 45 | gmake/ 46 | gmake2/ 47 | 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Style Guide 20 | 21 | TODO Add formatting and style guide rules 22 | 23 | ## Code of Conduct 24 | 25 | ### Our Pledge 26 | 27 | In the interest of fostering an open and welcoming environment, we as 28 | contributors and maintainers pledge to making participation in our project and 29 | our community a harassment-free experience for everyone, regardless of age, body 30 | size, disability, ethnicity, gender identity and expression, level of experience, 31 | nationality, personal appearance, race, religion, or sexual identity and 32 | orientation. 33 | 34 | ### Our Standards 35 | 36 | Examples of behavior that contributes to creating a positive environment 37 | include: 38 | 39 | * Using welcoming and inclusive language 40 | * Being respectful of differing viewpoints and experiences 41 | * Gracefully accepting constructive criticism 42 | * Focusing on what is best for the community 43 | * Showing empathy towards other community members 44 | 45 | Examples of unacceptable behavior by participants include: 46 | 47 | * The use of sexualized language or imagery and unwelcome sexual attention or 48 | advances 49 | * Trolling, insulting/derogatory comments, and personal or political attacks 50 | * Public or private harassment 51 | * Publishing others' private information, such as a physical or electronic 52 | address, without explicit permission 53 | * Other conduct which could reasonably be considered inappropriate in a 54 | professional setting 55 | 56 | ### Our Responsibilities 57 | 58 | Project maintainers are responsible for clarifying the standards of acceptable 59 | behavior and are expected to take appropriate and fair corrective action in 60 | response to any instances of unacceptable behavior. 61 | 62 | Project maintainers have the right and responsibility to remove, edit, or 63 | reject comments, commits, code, wiki edits, issues, and other contributions 64 | that are not aligned to this Code of Conduct, or to ban temporarily or 65 | permanently any contributor for other behaviors that they deem inappropriate, 66 | threatening, offensive, or harmful. 67 | 68 | ### Scope 69 | 70 | This Code of Conduct applies both within project spaces and in public spaces 71 | when an individual is representing the project or its community. Examples of 72 | representing a project or community include using an official project e-mail 73 | address, posting via an official social media account, or acting as an appointed 74 | representative at an online or offline event. Representation of a project may be 75 | further defined and clarified by project maintainers. 76 | 77 | ### Enforcement 78 | 79 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 80 | reported by contacting the project team at alexander.j.dupree@gmail.com All 81 | complaints will be reviewed and investigated and will result in a response that 82 | is deemed necessary and appropriate to the circumstances. The project team is 83 | obligated to maintain confidentiality with regard to the reporter of an incident. 84 | Further details of specific enforcement policies may be posted separately. 85 | 86 | Project maintainers who do not follow or enforce the Code of Conduct in good 87 | faith may face temporary or permanent repercussions as determined by other 88 | members of the project's leadership. 89 | 90 | ### Attribution 91 | 92 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 93 | available at [http://contributor-covenant.org/version/1/4][version] 94 | 95 | [homepage]: http://contributor-covenant.org 96 | [version]: http://contributor-covenant.org/version/1/4/ 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alexander DuPree 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Proposed changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | What types of changes does your code introduce? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | 14 | ## Checklist 15 | 16 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 17 | 18 | - [ ] I have read the [CONTRIBUTING](https://github.com/AlexanderJDupree/LinkedListsCPP/blob/master/CONTRIBUTING.md) doc 19 | - [ ] Unit tests pass locally with my changes 20 | - [ ] I have added tests that prove my fix is effective or that my feature works 21 | - [ ] I have added necessary documentation (if appropriate) 22 | 23 | ## Further comments 24 | 25 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # LinkedListsCPP 4 | 5 | [![CircleCI](https://circleci.com/gh/AlexanderJDupree/LinkedListsCPP.svg?style=svg)](https://circleci.com/gh/AlexanderJDupree/LinkedListsCPP) 6 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/c24f1655cc534243b8ab5bcd60c8302c)](https://www.codacy.com/app/AlexanderJDupree/LinkedListsCPP?utm_source=github.com&utm_medium=referral&utm_content=AlexanderJDupree/LinkedListsCPP&utm_campaign=Badge_Grade) 7 | [![codecov](https://codecov.io/gh/AlexanderJDupree/LinkedListsCPP/branch/master/graph/badge.svg)](https://codecov.io/gh/AlexanderJDupree/LinkedListsCPP) 8 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/AlexanderJDupree/LinkedListsCPP/master/LICENSE) 9 | 10 | 11 | - [Getting Started](#getting-started) 12 | - [Usage](#usage) 13 | - [Execute unit tests](#execute-unit-tests) 14 | - [Built With](#built-with) 15 | - [Contributing](#contributing) 16 | - [Versioning](#versioning) 17 | - [Authors](#authors) 18 | - [License](#license) 19 | 20 | ## Introduction 21 | 22 | **LinkedListsCPP** project features a collection of linked list data structures. These linked lists are fully templated and mirror the syntax and functionality of the C++ standard library containers. Currently only the linear linked list is implemented, but stay tuned for doubly and circular linked list releases! 23 | 24 | ## Getting Started 25 | 26 | - Download the latest [release here](https://github.com/AlexanderJDupree/LinkedListsCPP/releases). 27 | 28 | ### Prerequisites 29 | - linked_list.hpp utilizes C++ 11 language features and will **NOT** compile in older C++ language standards. In the future, compiler and language standard detection will be added for compatibility. If you want to work on this feature, feel free to contribute! 30 | 31 | ### Usage 32 | 33 | All releases are header only, so just drop the .hpp file into your includes and start using the linked list like this: 34 | 35 | ```c++ 36 | #include 37 | #include "linear_linked_list.hpp" 38 | 39 | int main() 40 | { 41 | linear_linked_list list { 'H', 'E', 'L', 'L', 'O', '!', '\n' }; 42 | 43 | for(const char& letter : list) 44 | { 45 | std::cout << letter; 46 | } 47 | 48 | // Prints "HELLO!" to the console 49 | 50 | return 0; 51 | } 52 | ``` 53 | Extensive documentation can be expected for the future. For right now, however, please the read header comments or examine how the linked lists are used in the unit tests for usage instructions. 54 | 55 | ## Execute Unit Tests 56 | 57 | For those who wish to contribute, you'll need to build and run the unit tests. This repo utilizes [Premake5](https://github.com/premake/premake-core) to build the necessary project files. After you Premake5 is installed you can start building the project. 58 | 59 | - First, clone the repository 60 | ```bash 61 | git clone https://github.com/AlexanderJDupree/LinkedListsCPP.git 62 | ``` 63 | 64 | - Navigate to the project directory 65 | ```bash 66 | cd LinkedListsCPP 67 | ``` 68 | 69 | - And run premake5 to generate the project files for your platform. For example to build GNU Makefiles run: 70 | ```bash 71 | premake5 gmake 72 | ``` 73 | - To run the unit tests, just build the application. 74 | 75 | ``` 76 | cd gmake && make 77 | ``` 78 | 79 | - If you need to rerun the tests, they are located in bin/tests/ 80 | 81 | ## Built With 82 | 83 | * [Catch2](https://github.com/catchorg/Catch2) - Unit Testing framework used 84 | * [Premake5](https://github.com/premake/premake-core) - Build System 85 | 86 | ## Contributing 87 | 88 | Please read [CONTRIBUTING.md](https://github.com/AlexanderJDupree/LinkedListsCPP/blob/master/CONTRIBUTING.md) for details on our code of conduct. 89 | 90 | All contributions are welcome: bug fixes, recommendations, issues, features. 91 | 92 | Plase see the [issues](https://github.com/AlexanderJDupree/LinkedListsCPP/issues) before you submit a pull request or raise an issue to avoid duplication. 93 | 94 | To contribute to this repository: 95 | 96 | - [Fork the project to your own GitHub profile](https://help.github.com/articles/fork-a-repo/) 97 | 98 | - Download the project using git clone: 99 | ``` 100 | git clone git@github.com:/LinkedListsCPP.git 101 | ``` 102 | - Create a new branch with a descriptive name: 103 | ``` 104 | git checkout -b descriptive_branch_name 105 | ``` 106 | - Write some code, fix something, and add a test to prove that it works. **No pull request will be accepted without tests passing, or without new tests if new features are added.** 107 | 108 | - Commit your code and push it to GitHub 109 | 110 | - [Open a new pull request](https://help.github.com/articles/creating-a-pull-request/) and describe the changes you have made. 111 | 112 | - We'll accept your changes after review. 113 | 114 | Simple! 115 | 116 | ## Versioning 117 | 118 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/AlexanderJDupree/LinkedListsCPP/tags). 119 | 120 | ## Authors 121 | * **Alexander DuPree** - *Initial work* - [GitHub](https://github.com/alexanderjdupree) 122 | * **Jacob Bickle** - *Co-Author* - [GitHub](https://github.com/jake-bickle) 123 | 124 | See also the list of [contributors](https://github.com/AlexanderJDupree/LinkedListsCPP/graphs/contributors) who participated in this project. 125 | 126 | ## License 127 | 128 | This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/AlexanderJDupree/LinkedListsCPP/blob/master/LICENSE) file for details 129 | 130 | ## Special Thanks 131 | 132 | This readme and the contributing guidelines are based off this great [template](https://gist.github.com/PurpleBooth/109311bb0361f32d87a2) 133 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | plugins: 3 | - jekyll-seo-tag 4 | title: LinkedListsCPP 5 | author: Alexander Dupree 6 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "tests/third_party/catch.hpp" 3 | -------------------------------------------------------------------------------- /example/example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: linear_linked_list.h 4 | 5 | Brief: Example program demonstrating basic use of a linked list 6 | 7 | Copyright (c) 2018 Alexander DuPree 8 | 9 | This software is released as open source through the MIT License 10 | 11 | Authors: Alexander DuPree 12 | 13 | https://github.com/AlexanderJDupree/LinkedListsCPP 14 | 15 | */ 16 | 17 | #include 18 | #include "linear_linked_list.hpp" 19 | 20 | int main() 21 | { 22 | linear_linked_list list { 'H', 'E', 'L', 'L', 'O', '!', '\n' }; 23 | 24 | for(const char& letter : list) 25 | { 26 | std::cout << letter; 27 | } 28 | 29 | // Prints "HELLO!" to the console 30 | 31 | return 0; 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /include/linear_linked_list.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: linear_linked_list.h 4 | 5 | Brief: Linear Linked List is a heap allocated, singularly linked, sequence 6 | container. This data structure provides constant time insertion and 7 | removal operations. However, random access is not supported. This 8 | iplementation defines the standard push/pop/remove methods as well as 9 | forward iterators traversal as well as higher order functions like 10 | for_each, and sort_by. 11 | 12 | Copyright (c) 2018 Alexander DuPree 13 | 14 | This software is released as open source through the MIT License 15 | 16 | Authors: Alexander DuPree 17 | 18 | https://github.com/AlexanderJDupree/LinkedListsCPP 19 | 20 | */ 21 | 22 | #ifndef LINKED_LIST_H 23 | #define LINKED_LIST_H 24 | 25 | #include // std::move, std::exchange 26 | #include // std::swap 27 | #include // std::logic_error 28 | #include // std::initializer_list 29 | 30 | template 31 | class linear_linked_list 32 | { 33 | public: 34 | 35 | // forward declaration 36 | class const_forward_iterator; 37 | class forward_iterator; 38 | 39 | /* Type definitions */ 40 | typedef T value_type; 41 | typedef T* pointer; 42 | typedef T& reference; 43 | typedef const T& const_reference; 44 | typedef const T* const_pointer; 45 | typedef size_t size_type; 46 | typedef forward_iterator iterator; 47 | typedef const_forward_iterator const_iterator; 48 | typedef linear_linked_list self_type; 49 | 50 | /****** CONSTRUCTORS ******/ 51 | 52 | // Default 53 | linear_linked_list(); 54 | 55 | // Ranged based 56 | template 57 | linear_linked_list(InputIterator begin, InputIterator end); 58 | 59 | // Initializer List 60 | explicit linear_linked_list(std::initializer_list init); 61 | 62 | // Copy Constructor 63 | linear_linked_list(const self_type& origin); 64 | 65 | // Move Constructor 66 | linear_linked_list(self_type&& origin); 67 | 68 | // Destructor 69 | ~linear_linked_list(); 70 | 71 | /****** MODIFIERS ******/ 72 | 73 | // TODO add push_front/back methods for lists and iterators 74 | 75 | // Adds an element to the front of the list 76 | self_type& push_front(T&& data); 77 | self_type& push_front(const_reference data); 78 | 79 | // Adds an element to the back of the list 80 | self_type& push_back(T&& data); 81 | self_type& push_back(const_reference data); 82 | 83 | // Removes the element at the front of the list 84 | self_type& pop_front(); 85 | 86 | // Copies the front element onto the out_param and removes it 87 | reference pop_front(reference out_param); 88 | // NOTE: There is no pop_back method for the singly linked list, as the 89 | // time complexity for pop_back is O(n). Therefore pop_front is encouraged 90 | 91 | // Removes each element from the container 92 | self_type& clear(); 93 | 94 | // Reverses the order of elements 95 | self_type& reverse(); 96 | 97 | // Sorts the list, defaults to ascending order 98 | self_type& sort(); 99 | 100 | template 101 | self_type& sort(Compare&& comp); 102 | 103 | // Splits the list on the parameter and returns the split 104 | self_type split(const_iterator pos); 105 | 106 | // Merges list into this list 107 | self_type& merge(self_type& list); 108 | 109 | template 110 | self_type& merge(self_type& list, Compare&& comp); 111 | 112 | iterator erase_after(iterator pos); 113 | 114 | // Removes all items matching target, returns number of items removed 115 | int remove(const_reference target); 116 | 117 | // Removes the all items fullfilling the predicate function 118 | template 119 | int remove_if(Predicate&& pred); 120 | 121 | /****** CAPACITY ******/ 122 | 123 | // returns true if the list is empty 124 | bool empty() const; 125 | 126 | // returns length of list by recurring through the list. O(n) operation. 127 | size_type size() const; 128 | 129 | /****** ELEMENT ACCESS ******/ 130 | 131 | // Returns a direct reference to the front element, throws if list is empty 132 | reference front(); 133 | const_reference front() const; 134 | 135 | // Returns a direct reference to the rear element, throws if list is empty 136 | reference back(); 137 | const_reference back() const; 138 | 139 | /****** ITERATORS ******/ 140 | 141 | iterator begin(); 142 | const_iterator begin() const; 143 | 144 | iterator end(); 145 | const_iterator end() const; 146 | 147 | // Travels the list two nodes at a time to find the middle. O(n) complexity. 148 | iterator middle(); 149 | const_iterator middle() const; 150 | 151 | /****** COMPARISON OPERATORS ******/ 152 | 153 | // Compares sizes, then comapres each element of the list for equality 154 | bool operator==(const self_type& rhs) const; 155 | 156 | // returns the logical NOT of the equality comparison 157 | bool operator!=(const self_type& rhs) const; 158 | 159 | /****** COPY-ASSIGNMENT AND SWAP ******/ 160 | 161 | // Swaps pointers to each other's resources. effectively reassigning 162 | // ownership. 163 | void swap(self_type& origin); 164 | 165 | // creates a copy of the origin, then swaps ownership with the copy 166 | self_type& operator=(self_type copy); 167 | 168 | private: 169 | 170 | /* 171 | @struct: Node 172 | 173 | @brief: Node is the atomic member for the linked list class. It stores 174 | the desired data and contains a pointer to the next node in the list. 175 | */ 176 | struct Node 177 | { 178 | // Default values are default constructor and nullptr 179 | Node(const_reference value = value_type(), Node* next = nullptr) 180 | : data(value), next(next) {} 181 | 182 | // rvalue constructor 183 | Node(T&& value, Node* next = nullptr) 184 | : data(std::forward(value)), next(next) {} 185 | 186 | value_type data; 187 | Node* next; 188 | 189 | }; 190 | 191 | Node* head; 192 | Node* tail; 193 | 194 | /* Recursive Functions */ 195 | 196 | size_type size(Node* head) const; 197 | 198 | Node* middle(Node* head) const; 199 | Node* middle(Node* slow, Node* fast) const; 200 | 201 | template 202 | const_iterator find_split(Node* head, Predicate&& pred); 203 | 204 | void clear_list(Node*& current); 205 | 206 | void reverse(Node* current, Node* prev=nullptr); 207 | 208 | template 209 | Node* merge(Node* self, Node* other, Compare&& comp); 210 | 211 | template 212 | int remove_if(Predicate&& pred, Node*& current, Node* prev=nullptr); 213 | 214 | /* Subroutines */ 215 | 216 | self_type& push_front(Node* node); 217 | self_type& push_back(Node* node); 218 | 219 | // Throws a logic error exception if the node* is nullptr 220 | void throw_if_null(Node* node) const; 221 | // TODO make custom null exception that can print out useful information 222 | 223 | public: 224 | 225 | /* 226 | @class: const_forward_iterator 227 | 228 | @brief: The const_forward_iterator is a read-only abstraction of the node 229 | pointer. The const_forward_iterator provides methods for inspecting 230 | data, and incrementation. This iterator type does not support 231 | decrementation or random access 232 | 233 | Because the iterator does not manage any resources and it's only 234 | member is a pointer, we allow the use of the DEFAULT destructor, 235 | copy constructor, and copy-assignment operator. 236 | */ 237 | class const_forward_iterator 238 | { 239 | public: 240 | 241 | typedef const_forward_iterator self_type; 242 | 243 | /* Constructors */ 244 | 245 | // default constructor points the iterator to nullptr 246 | const_forward_iterator(Node* ptr = nullptr) : node(ptr) {} 247 | 248 | /* Operator Overloads */ 249 | 250 | self_type& operator++(); // Prefix ++ 251 | self_type operator++(int); // Postfix ++ 252 | 253 | const_reference operator*() const; 254 | const_pointer operator->() const; 255 | 256 | // Iterators are equal if they point to the same memory address 257 | bool operator==(const self_type& rhs) const; 258 | bool operator!=(const self_type& rhs) const; 259 | 260 | friend linear_linked_list; 261 | 262 | protected: 263 | 264 | Node* node; 265 | }; 266 | 267 | /* 268 | @class: forward_iterator 269 | 270 | @brief: The forward_iterator is a read/write abstraction of the node 271 | pointer. The forward_iterator inherits all methods from the 272 | const_forward_iterator but overrides the reference operators 273 | to allow the client to mutate data 274 | */ 275 | class forward_iterator : public const_forward_iterator 276 | { 277 | public: 278 | 279 | /* Type definitions */ 280 | typedef forward_iterator self_type; 281 | 282 | forward_iterator(Node* ptr = nullptr) : const_forward_iterator(ptr) {} 283 | 284 | reference operator*(); 285 | 286 | pointer operator->(); 287 | 288 | }; 289 | }; 290 | 291 | #include "linear_linked_list.cpp" 292 | 293 | #endif //LINKED_LIST_H 294 | 295 | -------------------------------------------------------------------------------- /premake5.lua: -------------------------------------------------------------------------------- 1 | -- File: premake5.lua 2 | 3 | -- Brief: Build script for the premake build system. Run 'premake5 gmake' to 4 | -- build GNU specific makefiles. 5 | 6 | -- Author: Alexander DuPree 7 | 8 | -- WORKSPACE CONFIGURATION -- 9 | workspace "LinkedList" 10 | configurations { "debug", "release" } 11 | 12 | if _ACTION == "clean" then 13 | os.rmdir("bin/") 14 | os.rmdir("gmake/") 15 | os.rmdir("gmake2/") 16 | end 17 | 18 | local project_action = "UNDEFINED" 19 | if _ACTION ~= nill then project_action = _ACTION end 20 | 21 | location (project_action) 22 | 23 | -- PLATFORM CONFIGURATIONS -- 24 | 25 | -- COMPILER/LINKER CONFIG -- 26 | flags "FatalWarnings" 27 | warnings "Extra" 28 | 29 | filter "configurations:debug*" 30 | defines { "DEBUG", "MOCKING_ENABLED" } 31 | symbols "On" 32 | 33 | filter "configurations:release*" 34 | defines { "NDEBUG" } 35 | optimize "On" 36 | 37 | filter "toolset:gcc" 38 | buildoptions { 39 | "-Wall", "-Wextra", "-Werror", "-std=c++11" 40 | } 41 | 42 | filter {} -- close filter 43 | 44 | project "LinkedList" 45 | kind "StaticLib" 46 | language "C++" 47 | targetdir "bin/%{cfg.buildcfg}/lib" 48 | targetname "LinkedList_%{cfg.shortname}" 49 | 50 | local source = "src/" 51 | local include = "include/" 52 | 53 | files (source .. "linear_linked_list.hpp") 54 | includedirs { source, include } 55 | 56 | project "example" 57 | kind "ConsoleApp" 58 | language "C++" 59 | links "LinkedList" 60 | targetdir "bin/example/" 61 | targetname "example_%{cfg.shortname}" 62 | 63 | local source = "example/" 64 | local include = "release/" 65 | 66 | files (source .. "example.cpp") 67 | includedirs{ include } 68 | 69 | project "Tests" 70 | kind "ConsoleApp" 71 | language "C++" 72 | links "LinkedList" 73 | targetdir "bin/tests/" 74 | targetname "run_tests" 75 | 76 | local include = "include/" 77 | local test_src = "tests/" 78 | local test_inc = "third_party/" 79 | 80 | files (test_src .. "**.cpp") 81 | 82 | includedirs { test_inc, include, "src/" } 83 | 84 | postbuildcommands ".././bin/tests/run_tests" 85 | 86 | filter {} -- close filter 87 | 88 | -------------------------------------------------------------------------------- /release/linear_linked_list.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: linear_linked_list.h 4 | 5 | Brief: Linear Linked List is a heap allocated, singularly linked, sequence 6 | container. This data structure provides constant time insertion and 7 | removal operations. However, random access is not supported. This 8 | iplementation defines the standard push/pop/remove methods as well as 9 | forward iterators traversal as well as higher order functions like 10 | for_each, and sort_by. 11 | 12 | Copyright (c) 2018 Alexander DuPree 13 | 14 | This software is released as open source through the MIT License 15 | 16 | Authors: Alexander DuPree 17 | 18 | v.2.1.0 19 | 20 | https://github.com/AlexanderJDupree/LinkedListsCPP 21 | 22 | */ 23 | 24 | #ifndef LINKED_LIST_H 25 | #define LINKED_LIST_H 26 | 27 | #include // std::move, std::exchange 28 | #include // std::swap 29 | #include // std::logic_error 30 | #include // std::initializer_list 31 | 32 | template 33 | class linear_linked_list 34 | { 35 | public: 36 | 37 | // forward declaration 38 | class const_forward_iterator; 39 | class forward_iterator; 40 | 41 | /* Type definitions */ 42 | typedef T value_type; 43 | typedef T* pointer; 44 | typedef T& reference; 45 | typedef const T& const_reference; 46 | typedef const T* const_pointer; 47 | typedef size_t size_type; 48 | typedef forward_iterator iterator; 49 | typedef const_forward_iterator const_iterator; 50 | typedef linear_linked_list self_type; 51 | 52 | /****** CONSTRUCTORS ******/ 53 | 54 | // Default 55 | linear_linked_list(); 56 | 57 | // Ranged based 58 | template 59 | linear_linked_list(InputIterator begin, InputIterator end); 60 | 61 | // Initializer List 62 | explicit linear_linked_list(std::initializer_list init); 63 | 64 | // Copy Constructor 65 | linear_linked_list(const self_type& origin); 66 | 67 | // Move Constructor 68 | linear_linked_list(self_type&& origin); 69 | 70 | // Destructor 71 | ~linear_linked_list(); 72 | 73 | /****** MODIFIERS ******/ 74 | 75 | // TODO add push_front/back methods for lists and iterators 76 | 77 | // Adds an element to the front of the list 78 | self_type& push_front(T&& data); 79 | self_type& push_front(const_reference data); 80 | 81 | // Adds an element to the back of the list 82 | self_type& push_back(T&& data); 83 | self_type& push_back(const_reference data); 84 | 85 | // Removes the element at the front of the list 86 | self_type& pop_front(); 87 | 88 | // Copies the front element onto the out_param and removes it 89 | reference pop_front(reference out_param); 90 | // NOTE: There is no pop_back method for the singly linked list, as the 91 | // time complexity for pop_back is O(n). Therefore pop_front is encouraged 92 | 93 | // Removes each element from the container 94 | self_type& clear(); 95 | 96 | // Reverses the order of elements 97 | self_type& reverse(); 98 | 99 | // Sorts the list, defaults to ascending order 100 | self_type& sort(); 101 | 102 | template 103 | self_type& sort(Compare&& comp); 104 | 105 | // Splits the list on the parameter and returns the split 106 | self_type split(const_iterator pos); 107 | 108 | // Merges list into this list 109 | self_type& merge(self_type& list); 110 | 111 | template 112 | self_type& merge(self_type& list, Compare&& comp); 113 | 114 | iterator erase_after(iterator pos); 115 | 116 | // Removes all items matching target, returns number of items removed 117 | int remove(const_reference target); 118 | 119 | // Removes the all items fullfilling the predicate function 120 | template 121 | int remove_if(Predicate&& pred); 122 | 123 | /****** CAPACITY ******/ 124 | 125 | // returns true if the list is empty 126 | bool empty() const; 127 | 128 | // returns length of list by recurring through the list. O(n) operation. 129 | size_type size() const; 130 | 131 | /****** ELEMENT ACCESS ******/ 132 | 133 | // Returns a direct reference to the front element, throws if list is empty 134 | reference front(); 135 | const_reference front() const; 136 | 137 | // Returns a direct reference to the rear element, throws if list is empty 138 | reference back(); 139 | const_reference back() const; 140 | 141 | /****** ITERATORS ******/ 142 | 143 | iterator begin(); 144 | const_iterator begin() const; 145 | 146 | iterator end(); 147 | const_iterator end() const; 148 | 149 | // Travels the list two nodes at a time to find the middle. O(n) complexity. 150 | iterator middle(); 151 | const_iterator middle() const; 152 | 153 | /****** COMPARISON OPERATORS ******/ 154 | 155 | // Compares sizes, then comapres each element of the list for equality 156 | bool operator==(const self_type& rhs) const; 157 | 158 | // returns the logical NOT of the equality comparison 159 | bool operator!=(const self_type& rhs) const; 160 | 161 | /****** COPY-ASSIGNMENT AND SWAP ******/ 162 | 163 | // Swaps pointers to each other's resources. effectively reassigning 164 | // ownership. 165 | void swap(self_type& origin); 166 | 167 | // creates a copy of the origin, then swaps ownership with the copy 168 | self_type& operator=(self_type copy); 169 | 170 | private: 171 | 172 | /* 173 | @struct: Node 174 | 175 | @brief: Node is the atomic member for the linked list class. It stores 176 | the desired data and contains a pointer to the next node in the list. 177 | */ 178 | struct Node 179 | { 180 | // Default values are default constructor and nullptr 181 | Node(const_reference value = value_type(), Node* next = nullptr) 182 | : data(value), next(next) {} 183 | 184 | // rvalue constructor 185 | Node(T&& value, Node* next = nullptr) 186 | : data(std::forward(value)), next(next) {} 187 | 188 | value_type data; 189 | Node* next; 190 | 191 | }; 192 | 193 | Node* head; 194 | Node* tail; 195 | 196 | /* Recursive Functions */ 197 | 198 | size_type size(Node* head) const; 199 | 200 | Node* middle(Node* head) const; 201 | Node* middle(Node* slow, Node* fast) const; 202 | 203 | template 204 | const_iterator find_split(Node* head, Predicate&& pred); 205 | 206 | void clear_list(Node*& current); 207 | 208 | void reverse(Node* current, Node* prev=nullptr); 209 | 210 | template 211 | Node* merge(Node* self, Node* other, Compare&& comp); 212 | 213 | template 214 | int remove_if(Predicate&& pred, Node*& current, Node* prev=nullptr); 215 | 216 | /* Subroutines */ 217 | 218 | self_type& push_front(Node* node); 219 | self_type& push_back(Node* node); 220 | 221 | // Throws a logic error exception if the node* is nullptr 222 | void throw_if_null(Node* node) const; 223 | // TODO make custom null exception that can print out useful information 224 | 225 | public: 226 | 227 | /* 228 | @class: const_forward_iterator 229 | 230 | @brief: The const_forward_iterator is a read-only abstraction of the node 231 | pointer. The const_forward_iterator provides methods for inspecting 232 | data, and incrementation. This iterator type does not support 233 | decrementation or random access 234 | 235 | Because the iterator does not manage any resources and it's only 236 | member is a pointer, we allow the use of the DEFAULT destructor, 237 | copy constructor, and copy-assignment operator. 238 | */ 239 | class const_forward_iterator 240 | { 241 | public: 242 | 243 | typedef const_forward_iterator self_type; 244 | 245 | /* Constructors */ 246 | 247 | // default constructor points the iterator to nullptr 248 | const_forward_iterator(Node* ptr = nullptr) : node(ptr) {} 249 | 250 | /* Operator Overloads */ 251 | 252 | self_type& operator++(); // Prefix ++ 253 | self_type operator++(int); // Postfix ++ 254 | 255 | const_reference operator*() const; 256 | const_pointer operator->() const; 257 | 258 | // Iterators are equal if they point to the same memory address 259 | bool operator==(const self_type& rhs) const; 260 | bool operator!=(const self_type& rhs) const; 261 | 262 | friend linear_linked_list; 263 | 264 | protected: 265 | 266 | Node* node; 267 | }; 268 | 269 | /* 270 | @class: forward_iterator 271 | 272 | @brief: The forward_iterator is a read/write abstraction of the node 273 | pointer. The forward_iterator inherits all methods from the 274 | const_forward_iterator but overrides the reference operators 275 | to allow the client to mutate data 276 | */ 277 | class forward_iterator : public const_forward_iterator 278 | { 279 | public: 280 | 281 | /* Type definitions */ 282 | typedef forward_iterator self_type; 283 | 284 | forward_iterator(Node* ptr = nullptr) : const_forward_iterator(ptr) {} 285 | 286 | reference operator*(); 287 | 288 | pointer operator->(); 289 | 290 | }; 291 | }; 292 | 293 | /* 294 | 295 | File: linear_linked_list.cpp 296 | 297 | Brief: Implementation file for the linear_linked_list data structure 298 | 299 | Copyright (c) 2018 Alexander DuPree 300 | 301 | This software is released as open source through the MIT License 302 | 303 | Authors: Alexander DuPree 304 | 305 | https://github.com/AlexanderJDupree/LinkedListsCPP 306 | 307 | */ 308 | 309 | #ifndef LINKED_LIST_CPP 310 | #define LINKED_LIST_CPP 311 | 312 | /****** CONSTRUCTORS ******/ 313 | 314 | // default constructor 315 | template 316 | linear_linked_list::linear_linked_list() 317 | : head(nullptr), tail(nullptr) {} 318 | 319 | // ranged based constructor 320 | template 321 | template 322 | linear_linked_list::linear_linked_list(InputIterator begin, InputIterator end) 323 | : linear_linked_list() 324 | { 325 | for(; begin != end; ++begin) 326 | { 327 | push_back(*begin); 328 | } 329 | } 330 | 331 | // Initializer List 332 | template 333 | linear_linked_list::linear_linked_list(std::initializer_list init) 334 | : linear_linked_list() 335 | { 336 | for (const_reference element : init) 337 | { 338 | push_back(element); 339 | } 340 | } 341 | 342 | // Copy constructor 343 | template 344 | linear_linked_list::linear_linked_list(const self_type& origin) 345 | : linear_linked_list() 346 | { 347 | const_iterator it; 348 | for (it = origin.begin(); it != origin.end(); ++it) 349 | { 350 | push_back(*it); 351 | } 352 | } 353 | 354 | // Move constructor 355 | template 356 | linear_linked_list::linear_linked_list(self_type&& origin) 357 | : linear_linked_list() 358 | { 359 | origin.swap(*this); 360 | } 361 | 362 | // Destructor 363 | template 364 | linear_linked_list::~linear_linked_list() 365 | { 366 | clear(); 367 | } 368 | 369 | /****** MODIFIERS ******/ 370 | 371 | template 372 | linear_linked_list& linear_linked_list::push_front(const_reference data) 373 | { 374 | return push_front(new Node(data, head)); 375 | } 376 | 377 | template 378 | linear_linked_list& linear_linked_list::push_front(T&& data) 379 | { 380 | return push_front(new Node(std::forward(data), head)); 381 | } 382 | 383 | template 384 | linear_linked_list& linear_linked_list::push_front(Node* node) 385 | { 386 | head = node; 387 | 388 | if (tail == nullptr) 389 | { 390 | tail = head; 391 | } 392 | 393 | return *this; 394 | } 395 | 396 | template 397 | linear_linked_list& linear_linked_list::push_back(const_reference& data) 398 | { 399 | return push_back(new Node(data)); 400 | } 401 | 402 | template 403 | linear_linked_list& linear_linked_list::push_back(T&& data) 404 | { 405 | return push_back(new Node(std::forward(data))); 406 | } 407 | 408 | template 409 | linear_linked_list& linear_linked_list::push_back(Node* node) 410 | { 411 | if(empty()) 412 | { 413 | return push_front(node); 414 | } 415 | 416 | tail->next = node; 417 | tail = node; 418 | 419 | return *this; 420 | } 421 | 422 | 423 | 424 | template 425 | linear_linked_list& linear_linked_list::pop_front() 426 | { 427 | if (empty()) 428 | { 429 | return *this; 430 | } 431 | 432 | Node* temp = head->next; 433 | 434 | // Edge case, there is only one element in the list 435 | if (tail == head) 436 | { 437 | tail = temp; 438 | } 439 | 440 | delete head; 441 | 442 | head = temp; 443 | 444 | return *this; 445 | } 446 | 447 | template 448 | T& linear_linked_list::pop_front(reference out_param) 449 | { 450 | if(!empty()) 451 | { 452 | out_param = std::move(head->data); 453 | 454 | pop_front(); 455 | } 456 | 457 | return out_param; 458 | } 459 | 460 | template 461 | linear_linked_list& linear_linked_list::clear() 462 | { 463 | if(empty()) 464 | { 465 | return *this; 466 | } 467 | 468 | // clear_list is a recursive function that deletes each node of the list 469 | clear_list(head); 470 | 471 | tail = nullptr; 472 | 473 | return *this; 474 | } 475 | 476 | template 477 | void linear_linked_list::clear_list(Node*& current) 478 | { 479 | // While the current node pointer is not at the end of the list 480 | if (current != tail) 481 | { 482 | // Recursive call to travel to the end of the list 483 | clear_list(current->next); 484 | } 485 | 486 | // Deletes the current node as the stack unwinds 487 | delete current; 488 | current = nullptr; 489 | 490 | return; 491 | } 492 | 493 | template 494 | linear_linked_list& linear_linked_list::reverse() 495 | { 496 | if(!empty()) 497 | { 498 | reverse(head); 499 | 500 | std::swap(head, tail); 501 | } 502 | return *this; 503 | } 504 | 505 | template 506 | void linear_linked_list::reverse(Node* current, Node* prev) 507 | { 508 | if(current->next != nullptr) 509 | { 510 | reverse(current->next, current); 511 | } 512 | 513 | current->next = prev; 514 | 515 | return; 516 | } 517 | 518 | template 519 | linear_linked_list& linear_linked_list::sort() 520 | { 521 | return sort([](const T& lhs, const T& rhs){ return lhs < rhs; }); 522 | } 523 | 524 | template 525 | template 526 | linear_linked_list& linear_linked_list::sort(Compare&& comp) 527 | { 528 | if(head == nullptr || head->next == nullptr) 529 | { 530 | return *this; 531 | } 532 | 533 | linear_linked_list right = split(middle()); 534 | 535 | sort(comp); 536 | right.sort(comp); 537 | 538 | return merge(right, comp); 539 | } 540 | 541 | template 542 | linear_linked_list linear_linked_list::split(const_iterator pos) 543 | { 544 | linear_linked_list temp; 545 | 546 | if(pos.node != nullptr) 547 | { 548 | temp.head = pos.node->next; 549 | temp.tail = (temp.head == nullptr) ? nullptr : tail; 550 | 551 | tail = pos.node; 552 | tail->next = nullptr; 553 | } 554 | return temp; 555 | } 556 | 557 | template 558 | linear_linked_list& linear_linked_list::merge(self_type& list) 559 | { 560 | return merge(list, [](const T& lhs, const T& rhs){ return lhs < rhs; }); 561 | } 562 | 563 | template 564 | template 565 | linear_linked_list& linear_linked_list::merge(self_type& list, Compare&& comp) 566 | { 567 | if(&list != this) 568 | { 569 | head = merge(head, list.head, comp); 570 | 571 | // set tail to the "greater" of the two tails OR whichever tail isn't null 572 | tail = !tail || (list.tail && comp(tail->data, list.tail->data)) 573 | ? list.tail : tail; 574 | 575 | // Merge does not copy, source must relinquish resources 576 | list.head = list.tail = nullptr; 577 | } 578 | return *this; 579 | } 580 | 581 | template 582 | template 583 | typename linear_linked_list::Node* 584 | linear_linked_list::merge(Node* self, Node* other, Compare&& comp) 585 | { 586 | // Base case : self OR other list is empty return the non-empty list 587 | if (self == nullptr) { return other; } 588 | if (other == nullptr) { return self; } 589 | 590 | bool comparison = comp(self->data, other->data); 591 | 592 | Node* head = comparison ? self : other; 593 | head->next = comparison ? merge(self->next, other, comp) 594 | : merge(self, other->next, comp); 595 | return head; 596 | } 597 | 598 | template 599 | typename linear_linked_list::iterator 600 | linear_linked_list::erase_after(iterator pos) 601 | { 602 | if(!empty() && pos.node != tail) 603 | { 604 | Node* temp = pos.node->next; 605 | pos.node->next = temp->next; 606 | delete temp; 607 | } 608 | return pos; 609 | } 610 | 611 | template 612 | int linear_linked_list::remove(const_reference target) 613 | { 614 | // lambda catches target and compares it to each element in the list 615 | return remove_if([&target](T& sample){ return target == sample; }); 616 | } 617 | 618 | template 619 | template 620 | int linear_linked_list::remove_if(Predicate&& pred) 621 | { 622 | if (empty()) 623 | { 624 | return 0; 625 | } 626 | 627 | return remove_if(pred, head); 628 | } 629 | 630 | template 631 | template 632 | int linear_linked_list::remove_if(Predicate&& pred, Node*& current, Node* prev) 633 | { 634 | // Base Case: Traversed the whole list 635 | if(current == nullptr) 636 | { 637 | return 0; 638 | } 639 | 640 | // Predicate fulfilled, remove this element 641 | if(pred(current->data)) 642 | { 643 | 644 | // Edge case : element to be removed is the tail. 645 | if (tail == current) 646 | { 647 | tail = prev; 648 | } 649 | 650 | prev = current; 651 | 652 | current = current->next; 653 | 654 | delete prev; 655 | 656 | return 1 + remove_if(pred, current, prev=current); 657 | } 658 | 659 | prev = current; 660 | 661 | return remove_if(pred, current->next, prev); 662 | } 663 | 664 | /****** CAPACITY ******/ 665 | 666 | template 667 | bool linear_linked_list::empty() const 668 | { 669 | return !(head); 670 | } 671 | 672 | template 673 | typename linear_linked_list::size_type linear_linked_list::size() const 674 | { 675 | return size(head); 676 | } 677 | 678 | template 679 | typename linear_linked_list::size_type linear_linked_list::size(Node* head) const 680 | { 681 | return (head != nullptr) ? 1 + size(head->next) : 0; 682 | } 683 | 684 | /****** ELEMENT ACCESS ******/ 685 | 686 | template 687 | T& linear_linked_list::front() 688 | { 689 | throw_if_null(head); 690 | 691 | return head->data; 692 | } 693 | 694 | template 695 | const T& linear_linked_list::front() const 696 | { 697 | throw_if_null(head); 698 | 699 | return head->data; 700 | } 701 | 702 | template 703 | T& linear_linked_list::back() 704 | { 705 | throw_if_null(tail); 706 | 707 | return tail->data; 708 | } 709 | 710 | template 711 | const T& linear_linked_list::back() const 712 | { 713 | throw_if_null(tail); 714 | 715 | return tail->data; 716 | } 717 | 718 | /****** ITERATORS ******/ 719 | 720 | template 721 | typename linear_linked_list::iterator 722 | linear_linked_list::begin() 723 | { 724 | return iterator(head); 725 | } 726 | 727 | template 728 | typename linear_linked_list::const_iterator 729 | linear_linked_list::begin() const 730 | { 731 | return const_iterator(head); 732 | } 733 | 734 | template 735 | typename linear_linked_list::iterator 736 | linear_linked_list::end() 737 | { 738 | return iterator(nullptr); 739 | } 740 | 741 | template 742 | typename linear_linked_list::const_iterator 743 | linear_linked_list::end() const 744 | { 745 | return const_iterator(nullptr); 746 | } 747 | 748 | template 749 | typename linear_linked_list::iterator 750 | linear_linked_list::middle() 751 | { 752 | return iterator(middle(head)); 753 | } 754 | 755 | template 756 | typename linear_linked_list::const_iterator 757 | linear_linked_list::middle() const 758 | { 759 | return const_iterator(middle(head)); 760 | } 761 | 762 | template 763 | typename linear_linked_list::Node* 764 | linear_linked_list::middle(Node* head) const 765 | { 766 | if(head == nullptr || head->next == nullptr) 767 | { 768 | return head; 769 | } 770 | 771 | return middle(head, head->next); 772 | } 773 | 774 | template 775 | typename linear_linked_list::Node* 776 | linear_linked_list::middle(Node* slow, Node* fast) const 777 | { 778 | return (fast == nullptr || (fast = fast->next) == nullptr) 779 | ? slow : middle(slow->next, fast->next); 780 | } 781 | 782 | /****** COMPARISON OPERATORS ******/ 783 | 784 | template 785 | bool linear_linked_list::operator==(const self_type& rhs) const 786 | { 787 | // Compare sizes first 788 | if (rhs.size() != size()) 789 | { 790 | return false; 791 | } 792 | 793 | const_iterator left = begin(); 794 | const_iterator right = rhs.begin(); 795 | 796 | while(left != end() && right != rhs.end()) 797 | { 798 | // If any element does not match then return false 799 | if (*(left++) != *(right++)) 800 | { 801 | return false; 802 | } 803 | } 804 | 805 | return true; // TODO test left and right are both end iterators 806 | } 807 | 808 | template 809 | bool linear_linked_list::operator!=(const self_type& rhs) const 810 | { 811 | return !(*this == rhs); 812 | } 813 | 814 | template 815 | typename linear_linked_list::self_type& 816 | linear_linked_list::operator=(self_type copy) 817 | { 818 | // Swap ownership of resources with the copy 819 | swap(copy); 820 | 821 | // As the copy goes out of scope it destructs with the old data 822 | return *this; 823 | } 824 | 825 | template 826 | void linear_linked_list::swap(self_type& origin) 827 | { 828 | using std::swap; 829 | 830 | // Swaps pointers, reassigns ownership 831 | swap(head, origin.head); 832 | swap(tail, origin.tail); 833 | return; 834 | } 835 | 836 | template 837 | void linear_linked_list::throw_if_null(Node* node) const 838 | { 839 | if(node) 840 | { 841 | return; 842 | } 843 | 844 | throw std::logic_error("Element access fail, null pointer"); 845 | } 846 | 847 | /******************************************************************************* 848 | ITERATOR CLASS 849 | *******************************************************************************/ 850 | 851 | /* Operator Overloads */ 852 | template 853 | typename linear_linked_list::const_iterator& 854 | linear_linked_list::const_iterator::operator++() 855 | { 856 | // reassign node member to point to the next element in the container 857 | node = node->next; 858 | return *this; 859 | } 860 | 861 | template 862 | typename linear_linked_list::const_iterator 863 | linear_linked_list::const_iterator::operator++(int) 864 | { 865 | // Create a copy to satisfy postfix incrementation requirements 866 | self_type copy = self_type(*this); 867 | ++(*this); 868 | return copy; 869 | } 870 | 871 | template 872 | bool linear_linked_list::const_iterator::operator==(const self_type& rhs) const 873 | { 874 | // Iterators are equal if they point to the same memory address 875 | return node == rhs.node; 876 | } 877 | 878 | template 879 | bool linear_linked_list::const_iterator::operator!=(const self_type& rhs) const 880 | { 881 | return !(*this == rhs); 882 | } 883 | 884 | template 885 | typename linear_linked_list::const_reference 886 | linear_linked_list::const_iterator::operator*() const 887 | { 888 | return node->data; 889 | } 890 | 891 | template 892 | typename linear_linked_list::const_pointer 893 | linear_linked_list::const_iterator::operator->() const 894 | { 895 | return &node->data; 896 | } 897 | 898 | template 899 | typename linear_linked_list::reference 900 | linear_linked_list::iterator::operator*() 901 | { 902 | return this->node->data; 903 | } 904 | 905 | template 906 | typename linear_linked_list::pointer 907 | linear_linked_list::iterator::operator->() 908 | { 909 | return &this->node->data; 910 | } 911 | 912 | #endif //LINKED_LIST_CPP 913 | #endif //LINKED_LIST_H 914 | 915 | -------------------------------------------------------------------------------- /src/linear_linked_list.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: linear_linked_list.cpp 4 | 5 | Brief: Implementation file for the linear_linked_list data structure 6 | 7 | Copyright (c) 2018 Alexander DuPree 8 | 9 | This software is released as open source through the MIT License 10 | 11 | Authors: Alexander DuPree 12 | 13 | https://github.com/AlexanderJDupree/LinkedListsCPP 14 | 15 | */ 16 | 17 | #ifndef LINKED_LIST_CPP 18 | #define LINKED_LIST_CPP 19 | 20 | #include "linear_linked_list.hpp" 21 | 22 | /****** CONSTRUCTORS ******/ 23 | 24 | // default constructor 25 | template 26 | linear_linked_list::linear_linked_list() 27 | : head(nullptr), tail(nullptr) {} 28 | 29 | // ranged based constructor 30 | template 31 | template 32 | linear_linked_list::linear_linked_list(InputIterator begin, InputIterator end) 33 | : linear_linked_list() 34 | { 35 | for(; begin != end; ++begin) 36 | { 37 | push_back(*begin); 38 | } 39 | } 40 | 41 | // Initializer List 42 | template 43 | linear_linked_list::linear_linked_list(std::initializer_list init) 44 | : linear_linked_list() 45 | { 46 | for (const_reference element : init) 47 | { 48 | push_back(element); 49 | } 50 | } 51 | 52 | // Copy constructor 53 | template 54 | linear_linked_list::linear_linked_list(const self_type& origin) 55 | : linear_linked_list() 56 | { 57 | const_iterator it; 58 | for (it = origin.begin(); it != origin.end(); ++it) 59 | { 60 | push_back(*it); 61 | } 62 | } 63 | 64 | // Move constructor 65 | template 66 | linear_linked_list::linear_linked_list(self_type&& origin) 67 | : linear_linked_list() 68 | { 69 | origin.swap(*this); 70 | } 71 | 72 | // Destructor 73 | template 74 | linear_linked_list::~linear_linked_list() 75 | { 76 | clear(); 77 | } 78 | 79 | /****** MODIFIERS ******/ 80 | 81 | template 82 | linear_linked_list& linear_linked_list::push_front(const_reference data) 83 | { 84 | return push_front(new Node(data, head)); 85 | } 86 | 87 | template 88 | linear_linked_list& linear_linked_list::push_front(T&& data) 89 | { 90 | return push_front(new Node(std::forward(data), head)); 91 | } 92 | 93 | template 94 | linear_linked_list& linear_linked_list::push_front(Node* node) 95 | { 96 | head = node; 97 | 98 | if (tail == nullptr) 99 | { 100 | tail = head; 101 | } 102 | 103 | return *this; 104 | } 105 | 106 | template 107 | linear_linked_list& linear_linked_list::push_back(const_reference& data) 108 | { 109 | return push_back(new Node(data)); 110 | } 111 | 112 | template 113 | linear_linked_list& linear_linked_list::push_back(T&& data) 114 | { 115 | return push_back(new Node(std::forward(data))); 116 | } 117 | 118 | template 119 | linear_linked_list& linear_linked_list::push_back(Node* node) 120 | { 121 | if(empty()) 122 | { 123 | return push_front(node); 124 | } 125 | 126 | tail->next = node; 127 | tail = node; 128 | 129 | return *this; 130 | } 131 | 132 | 133 | 134 | template 135 | linear_linked_list& linear_linked_list::pop_front() 136 | { 137 | if (empty()) 138 | { 139 | return *this; 140 | } 141 | 142 | Node* temp = head->next; 143 | 144 | // Edge case, there is only one element in the list 145 | if (tail == head) 146 | { 147 | tail = temp; 148 | } 149 | 150 | delete head; 151 | 152 | head = temp; 153 | 154 | return *this; 155 | } 156 | 157 | template 158 | T& linear_linked_list::pop_front(reference out_param) 159 | { 160 | if(!empty()) 161 | { 162 | out_param = std::move(head->data); 163 | 164 | pop_front(); 165 | } 166 | 167 | return out_param; 168 | } 169 | 170 | template 171 | linear_linked_list& linear_linked_list::clear() 172 | { 173 | if(empty()) 174 | { 175 | return *this; 176 | } 177 | 178 | // clear_list is a recursive function that deletes each node of the list 179 | clear_list(head); 180 | 181 | tail = nullptr; 182 | 183 | return *this; 184 | } 185 | 186 | template 187 | void linear_linked_list::clear_list(Node*& current) 188 | { 189 | // While the current node pointer is not at the end of the list 190 | if (current != tail) 191 | { 192 | // Recursive call to travel to the end of the list 193 | clear_list(current->next); 194 | } 195 | 196 | // Deletes the current node as the stack unwinds 197 | delete current; 198 | current = nullptr; 199 | 200 | return; 201 | } 202 | 203 | template 204 | linear_linked_list& linear_linked_list::reverse() 205 | { 206 | if(!empty()) 207 | { 208 | reverse(head); 209 | 210 | std::swap(head, tail); 211 | } 212 | return *this; 213 | } 214 | 215 | template 216 | void linear_linked_list::reverse(Node* current, Node* prev) 217 | { 218 | if(current->next != nullptr) 219 | { 220 | reverse(current->next, current); 221 | } 222 | 223 | current->next = prev; 224 | 225 | return; 226 | } 227 | 228 | template 229 | linear_linked_list& linear_linked_list::sort() 230 | { 231 | return sort([](const T& lhs, const T& rhs){ return lhs < rhs; }); 232 | } 233 | 234 | template 235 | template 236 | linear_linked_list& linear_linked_list::sort(Compare&& comp) 237 | { 238 | if(head == nullptr || head->next == nullptr) 239 | { 240 | return *this; 241 | } 242 | 243 | linear_linked_list right = split(middle()); 244 | 245 | sort(comp); 246 | right.sort(comp); 247 | 248 | return merge(right, comp); 249 | } 250 | 251 | template 252 | linear_linked_list linear_linked_list::split(const_iterator pos) 253 | { 254 | linear_linked_list temp; 255 | 256 | if(pos.node != nullptr) 257 | { 258 | temp.head = pos.node->next; 259 | temp.tail = (temp.head == nullptr) ? nullptr : tail; 260 | 261 | tail = pos.node; 262 | tail->next = nullptr; 263 | } 264 | return temp; 265 | } 266 | 267 | template 268 | linear_linked_list& linear_linked_list::merge(self_type& list) 269 | { 270 | return merge(list, [](const T& lhs, const T& rhs){ return lhs < rhs; }); 271 | } 272 | 273 | template 274 | template 275 | linear_linked_list& linear_linked_list::merge(self_type& list, Compare&& comp) 276 | { 277 | if(&list != this) 278 | { 279 | head = merge(head, list.head, comp); 280 | 281 | // set tail to the "greater" of the two tails OR whichever tail isn't null 282 | tail = !tail || (list.tail && comp(tail->data, list.tail->data)) 283 | ? list.tail : tail; 284 | 285 | // Merge does not copy, source must relinquish resources 286 | list.head = list.tail = nullptr; 287 | } 288 | return *this; 289 | } 290 | 291 | template 292 | template 293 | typename linear_linked_list::Node* 294 | linear_linked_list::merge(Node* self, Node* other, Compare&& comp) 295 | { 296 | // Base case : self OR other list is empty return the non-empty list 297 | if (self == nullptr) { return other; } 298 | if (other == nullptr) { return self; } 299 | 300 | bool comparison = comp(self->data, other->data); 301 | 302 | Node* head = comparison ? self : other; 303 | head->next = comparison ? merge(self->next, other, comp) 304 | : merge(self, other->next, comp); 305 | return head; 306 | } 307 | 308 | template 309 | typename linear_linked_list::iterator 310 | linear_linked_list::erase_after(iterator pos) 311 | { 312 | if(!empty() && pos.node != tail) 313 | { 314 | Node* temp = pos.node->next; 315 | pos.node->next = temp->next; 316 | delete temp; 317 | } 318 | return pos; 319 | } 320 | 321 | template 322 | int linear_linked_list::remove(const_reference target) 323 | { 324 | // lambda catches target and compares it to each element in the list 325 | return remove_if([&target](T& sample){ return target == sample; }); 326 | } 327 | 328 | template 329 | template 330 | int linear_linked_list::remove_if(Predicate&& pred) 331 | { 332 | if (empty()) 333 | { 334 | return 0; 335 | } 336 | 337 | return remove_if(pred, head); 338 | } 339 | 340 | template 341 | template 342 | int linear_linked_list::remove_if(Predicate&& pred, Node*& current, Node* prev) 343 | { 344 | // Base Case: Traversed the whole list 345 | if(current == nullptr) 346 | { 347 | return 0; 348 | } 349 | 350 | // Predicate fulfilled, remove this element 351 | if(pred(current->data)) 352 | { 353 | 354 | // Edge case : element to be removed is the tail. 355 | if (tail == current) 356 | { 357 | tail = prev; 358 | } 359 | 360 | prev = current; 361 | 362 | current = current->next; 363 | 364 | delete prev; 365 | 366 | return 1 + remove_if(pred, current, prev=current); 367 | } 368 | 369 | prev = current; 370 | 371 | return remove_if(pred, current->next, prev); 372 | } 373 | 374 | /****** CAPACITY ******/ 375 | 376 | template 377 | bool linear_linked_list::empty() const 378 | { 379 | return !(head); 380 | } 381 | 382 | template 383 | typename linear_linked_list::size_type linear_linked_list::size() const 384 | { 385 | return size(head); 386 | } 387 | 388 | template 389 | typename linear_linked_list::size_type linear_linked_list::size(Node* head) const 390 | { 391 | return (head != nullptr) ? 1 + size(head->next) : 0; 392 | } 393 | 394 | /****** ELEMENT ACCESS ******/ 395 | 396 | template 397 | T& linear_linked_list::front() 398 | { 399 | throw_if_null(head); 400 | 401 | return head->data; 402 | } 403 | 404 | template 405 | const T& linear_linked_list::front() const 406 | { 407 | throw_if_null(head); 408 | 409 | return head->data; 410 | } 411 | 412 | template 413 | T& linear_linked_list::back() 414 | { 415 | throw_if_null(tail); 416 | 417 | return tail->data; 418 | } 419 | 420 | template 421 | const T& linear_linked_list::back() const 422 | { 423 | throw_if_null(tail); 424 | 425 | return tail->data; 426 | } 427 | 428 | /****** ITERATORS ******/ 429 | 430 | template 431 | typename linear_linked_list::iterator 432 | linear_linked_list::begin() 433 | { 434 | return iterator(head); 435 | } 436 | 437 | template 438 | typename linear_linked_list::const_iterator 439 | linear_linked_list::begin() const 440 | { 441 | return const_iterator(head); 442 | } 443 | 444 | template 445 | typename linear_linked_list::iterator 446 | linear_linked_list::end() 447 | { 448 | return iterator(nullptr); 449 | } 450 | 451 | template 452 | typename linear_linked_list::const_iterator 453 | linear_linked_list::end() const 454 | { 455 | return const_iterator(nullptr); 456 | } 457 | 458 | template 459 | typename linear_linked_list::iterator 460 | linear_linked_list::middle() 461 | { 462 | return iterator(middle(head)); 463 | } 464 | 465 | template 466 | typename linear_linked_list::const_iterator 467 | linear_linked_list::middle() const 468 | { 469 | return const_iterator(middle(head)); 470 | } 471 | 472 | template 473 | typename linear_linked_list::Node* 474 | linear_linked_list::middle(Node* head) const 475 | { 476 | if(head == nullptr || head->next == nullptr) 477 | { 478 | return head; 479 | } 480 | 481 | return middle(head, head->next); 482 | } 483 | 484 | template 485 | typename linear_linked_list::Node* 486 | linear_linked_list::middle(Node* slow, Node* fast) const 487 | { 488 | return (fast == nullptr || (fast = fast->next) == nullptr) 489 | ? slow : middle(slow->next, fast->next); 490 | } 491 | 492 | /****** COMPARISON OPERATORS ******/ 493 | 494 | template 495 | bool linear_linked_list::operator==(const self_type& rhs) const 496 | { 497 | // Compare sizes first 498 | if (rhs.size() != size()) 499 | { 500 | return false; 501 | } 502 | 503 | const_iterator left = begin(); 504 | const_iterator right = rhs.begin(); 505 | 506 | while(left != end() && right != rhs.end()) 507 | { 508 | // If any element does not match then return false 509 | if (*(left++) != *(right++)) 510 | { 511 | return false; 512 | } 513 | } 514 | 515 | return true; // TODO test left and right are both end iterators 516 | } 517 | 518 | template 519 | bool linear_linked_list::operator!=(const self_type& rhs) const 520 | { 521 | return !(*this == rhs); 522 | } 523 | 524 | template 525 | typename linear_linked_list::self_type& 526 | linear_linked_list::operator=(self_type copy) 527 | { 528 | // Swap ownership of resources with the copy 529 | swap(copy); 530 | 531 | // As the copy goes out of scope it destructs with the old data 532 | return *this; 533 | } 534 | 535 | template 536 | void linear_linked_list::swap(self_type& origin) 537 | { 538 | using std::swap; 539 | 540 | // Swaps pointers, reassigns ownership 541 | swap(head, origin.head); 542 | swap(tail, origin.tail); 543 | return; 544 | } 545 | 546 | template 547 | void linear_linked_list::throw_if_null(Node* node) const 548 | { 549 | if(node) 550 | { 551 | return; 552 | } 553 | 554 | throw std::logic_error("Element access fail, null pointer"); 555 | } 556 | 557 | /******************************************************************************* 558 | ITERATOR CLASS 559 | *******************************************************************************/ 560 | 561 | /* Operator Overloads */ 562 | template 563 | typename linear_linked_list::const_iterator& 564 | linear_linked_list::const_iterator::operator++() 565 | { 566 | // reassign node member to point to the next element in the container 567 | node = node->next; 568 | return *this; 569 | } 570 | 571 | template 572 | typename linear_linked_list::const_iterator 573 | linear_linked_list::const_iterator::operator++(int) 574 | { 575 | // Create a copy to satisfy postfix incrementation requirements 576 | self_type copy = self_type(*this); 577 | ++(*this); 578 | return copy; 579 | } 580 | 581 | template 582 | bool linear_linked_list::const_iterator::operator==(const self_type& rhs) const 583 | { 584 | // Iterators are equal if they point to the same memory address 585 | return node == rhs.node; 586 | } 587 | 588 | template 589 | bool linear_linked_list::const_iterator::operator!=(const self_type& rhs) const 590 | { 591 | return !(*this == rhs); 592 | } 593 | 594 | template 595 | typename linear_linked_list::const_reference 596 | linear_linked_list::const_iterator::operator*() const 597 | { 598 | return node->data; 599 | } 600 | 601 | template 602 | typename linear_linked_list::const_pointer 603 | linear_linked_list::const_iterator::operator->() const 604 | { 605 | return &node->data; 606 | } 607 | 608 | template 609 | typename linear_linked_list::reference 610 | linear_linked_list::iterator::operator*() 611 | { 612 | return this->node->data; 613 | } 614 | 615 | template 616 | typename linear_linked_list::pointer 617 | linear_linked_list::iterator::operator->() 618 | { 619 | return &this->node->data; 620 | } 621 | 622 | #endif //LINKED_LIST_CPP 623 | -------------------------------------------------------------------------------- /tests/linked_list_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: linked_list_test.cpp 4 | 5 | Brief: Unit tests for linear linked list data structure 6 | 7 | Copyright (c) 2018 Alexander DuPree 8 | 9 | This software is released as open source through the MIT License 10 | 11 | Authors: Alexander DuPree, Jacob Bickle 12 | 13 | https://github.com/AlexanderJDupree/LinkedListsCPP 14 | 15 | */ 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include "linear_linked_list.hpp" 22 | 23 | class Data 24 | { 25 | public: 26 | 27 | Data(int num = 0, std::string str = "") 28 | : num(num), str(str) {} 29 | 30 | Data(const Data& origin) = default; 31 | 32 | Data(Data&& rval) 33 | : Data() 34 | { 35 | ++move_count; 36 | swap(rval); 37 | } 38 | 39 | void swap(Data& rval) 40 | { 41 | using std::swap; 42 | swap(num, rval.num); 43 | swap(str, rval.str); 44 | return; 45 | } 46 | 47 | Data& operator=(Data&& rval) 48 | { 49 | swap(rval); 50 | ++move_count; 51 | 52 | return *this; 53 | } 54 | 55 | bool operator==(const Data& rhs) const 56 | { 57 | return num == rhs.num && str == rhs.str; 58 | } 59 | 60 | int num; 61 | std::string str; 62 | static int move_count; // Ensures move semantics are actually being used 63 | }; 64 | int Data::move_count = 0; 65 | 66 | // Test functor for predicate functions 67 | struct is_seven 68 | { 69 | bool operator() (const int& value) 70 | { 71 | return value == 7; 72 | } 73 | }; 74 | 75 | TEST_CASE("Constructing linear_linked_list objects", "[constructors]") 76 | { 77 | SECTION("Default construction") 78 | { 79 | linear_linked_list list; 80 | 81 | REQUIRE(list.empty()); 82 | REQUIRE(list.size() == 0); 83 | } 84 | SECTION("Copy construction") 85 | { 86 | linear_linked_list origin { 1, 2, 3, 4, 5 }; 87 | linear_linked_list copy(origin); 88 | 89 | REQUIRE(origin == copy); 90 | REQUIRE(copy.size() == 5); 91 | } 92 | SECTION("Range based construction on a populated array") 93 | { 94 | int nums[] = {1, 2, 3, 4, 5}; 95 | 96 | linear_linked_list list(nums, nums + 5); 97 | 98 | linear_linked_list::const_iterator it; 99 | int i = 0; 100 | for(it = list.begin(); it != list.end(); ++it) 101 | { 102 | bool assert = *it == nums[i++]; 103 | REQUIRE(assert); 104 | } 105 | } 106 | SECTION("Ranged based construction with a standard container") 107 | { 108 | std::vector nums = { 1, 2, 3, 4, 5 }; 109 | 110 | linear_linked_list list(nums.begin(), nums.end()); 111 | 112 | std::vector::iterator iter = nums.begin(); 113 | for(auto num : list) 114 | { 115 | REQUIRE(num == *(iter++)); 116 | } 117 | } 118 | SECTION("Initializer list construction") 119 | { 120 | linear_linked_list list { 1, 2, 3, 4, 5 }; 121 | int i = 0; 122 | for(auto num : list) 123 | { 124 | REQUIRE(num == ++i); 125 | } 126 | } 127 | SECTION("Move Construction") 128 | { 129 | linear_linked_list origin { 1, 2, 3, 4, 5 }; 130 | linear_linked_list moved_list(std::move(origin)); 131 | 132 | int i = 0; 133 | for (auto num : moved_list) 134 | { 135 | REQUIRE(num == ++i); 136 | } 137 | REQUIRE(origin.empty()); 138 | } 139 | } 140 | 141 | TEST_CASE("Using clear to erase the list", "[clear], [destructor]") 142 | { 143 | SECTION("An empty list") 144 | { 145 | linear_linked_list list; 146 | 147 | list.clear(); 148 | 149 | REQUIRE(list.empty()); 150 | } 151 | SECTION("A populated list") 152 | { 153 | linear_linked_list list { 'a', 'b', 'c' }; 154 | 155 | list.clear(); 156 | 157 | REQUIRE(list.empty()); 158 | } 159 | } 160 | 161 | TEST_CASE("Front/Back element access", "[front], [back]") 162 | { 163 | SECTION("A populated list") 164 | { 165 | linear_linked_list list {1, 2, 3, 4, 5}; 166 | 167 | REQUIRE(list.front() == 1); 168 | REQUIRE(list.back() == 5); 169 | } 170 | SECTION("front and back are the same with a list with one element") 171 | { 172 | linear_linked_list list { 1 }; 173 | 174 | REQUIRE(list.front() == list.back()); 175 | } 176 | SECTION("Empty list front/back access will throw a logic_error") 177 | { 178 | linear_linked_list empty_list; 179 | 180 | REQUIRE_THROWS(empty_list.front()); 181 | REQUIRE_THROWS(empty_list.back()); 182 | } 183 | } 184 | 185 | TEST_CASE("Pushing to elements to the front of the list", "[push_front]") 186 | { 187 | SECTION("An empty list") 188 | { 189 | char letter = 'a'; 190 | linear_linked_list list; 191 | 192 | REQUIRE(list.push_front(letter).front() == letter); 193 | } 194 | SECTION("A populated list") 195 | { 196 | int num = 42; 197 | linear_linked_list list {1, 2, 3, 4, 5}; 198 | 199 | REQUIRE(list.push_front(num).front() == num); 200 | } 201 | SECTION("Pushing multiple elements") 202 | { 203 | int nums[] = { 3, 2, 1 }; 204 | linear_linked_list list; 205 | 206 | list.push_front(nums[0]).push_front(nums[1]).push_front(nums[2]); 207 | 208 | int i = 0; 209 | for (auto num : list) 210 | { 211 | REQUIRE(num == ++i); 212 | } 213 | } 214 | SECTION("Pushing simple data classes") 215 | { 216 | Data data[] = { Data(1, "one"), Data(2, "two"), Data(3, "three") }; 217 | linear_linked_list list; 218 | 219 | list.push_front(data[2]).push_front(data[1]).push_front(data[0]); 220 | int i = 0; 221 | for (auto item : list) 222 | { 223 | REQUIRE(item.num == data[i].num); 224 | REQUIRE(item.str == data[i++].str); 225 | } 226 | } 227 | SECTION("Using std::move with push") 228 | { 229 | Data data(1, "one"); 230 | linear_linked_list list; 231 | 232 | list.push_front(std::move(data)); 233 | 234 | REQUIRE(list.front() == Data(1, "one")); 235 | 236 | REQUIRE(data.num == 0); 237 | REQUIRE(data.str == ""); 238 | } 239 | } 240 | 241 | TEST_CASE("Pushing to elements to the back of the list", "[push_back]") 242 | { 243 | SECTION("An empty list") 244 | { 245 | char letter = 'b'; 246 | linear_linked_list list; 247 | 248 | REQUIRE(list.push_back(letter).front() == letter); 249 | } 250 | SECTION("A populated list") 251 | { 252 | int num = 42; 253 | linear_linked_list list {1, 2, 3, 4, 5}; 254 | 255 | REQUIRE(list.push_back(num).back() == num); 256 | } 257 | SECTION("Pushing multiple elements") 258 | { 259 | int nums[] = { 3, 2, 1 }; 260 | linear_linked_list list; 261 | 262 | list.push_back(nums[2]).push_back(nums[1]).push_back(nums[0]); 263 | 264 | int i = 0; 265 | for (auto num : list) 266 | { 267 | REQUIRE(num == ++i); 268 | } 269 | } 270 | SECTION("Pushing simple data classes") 271 | { 272 | Data data[] = { Data(1, "one"), Data(2, "two"), Data(3, "three") }; 273 | linear_linked_list list; 274 | 275 | list.push_back(data[0]).push_back(data[1]).push_back(data[2]); 276 | int i = 0; 277 | for (auto item : list) 278 | { 279 | REQUIRE(item.num == data[i].num); 280 | REQUIRE(item.str == data[i++].str); 281 | } 282 | } 283 | SECTION("Using std::move with push") 284 | { 285 | Data data(1, "one"); 286 | linear_linked_list list; 287 | 288 | list.push_back(std::move(data)); 289 | 290 | REQUIRE(list.front() == Data(1, "one")); 291 | 292 | REQUIRE(data.num == 0); 293 | REQUIRE(data.str == ""); 294 | } 295 | } 296 | 297 | TEST_CASE("Using swap to reassign data", "[swap]") 298 | { 299 | SECTION("An empty list and a populated list") 300 | { 301 | linear_linked_list old {1, 2, 3}; 302 | 303 | linear_linked_list list; 304 | 305 | list.swap(old); 306 | 307 | int i = 0; 308 | for (const auto& elem : list) 309 | { 310 | REQUIRE(elem == ++i); 311 | } 312 | REQUIRE(old.empty()); 313 | } 314 | SECTION("two empty lists") 315 | { 316 | linear_linked_list old; 317 | linear_linked_list list; 318 | 319 | list.swap(old); 320 | 321 | REQUIRE(old == list); 322 | } 323 | } 324 | 325 | TEST_CASE("Using the copy-assignment operator", "[operators], [copy-assignment]") 326 | { 327 | SECTION("An empty list and a populated list") 328 | { 329 | linear_linked_list old { 1, 2, 3 }; 330 | 331 | linear_linked_list list; 332 | 333 | REQUIRE((list = old) == old); 334 | } 335 | SECTION("two empty lists") 336 | { 337 | linear_linked_list old; 338 | linear_linked_list list; 339 | 340 | REQUIRE((old = list) == list); 341 | } 342 | SECTION("Self-assignment does nothing") 343 | { 344 | linear_linked_list list { 1, 2, 3 }; 345 | REQUIRE((list = list) == list); 346 | } 347 | SECTION("Move assignment empties old list") 348 | { 349 | linear_linked_list old { 1, 2, 3 }; 350 | linear_linked_list list = std::move(old); 351 | 352 | int i = 0; 353 | for (auto num : list) 354 | { 355 | REQUIRE(num == ++i); 356 | } 357 | REQUIRE(i == 3); 358 | REQUIRE(old.empty()); 359 | } 360 | } 361 | 362 | TEST_CASE("Testing equality between lists", "[operators], [equality]") 363 | { 364 | SECTION("Two empty lists") 365 | { 366 | linear_linked_list lhs; 367 | linear_linked_list rhs; 368 | 369 | REQUIRE(lhs == rhs); 370 | } 371 | SECTION("Two populated lists of differing sizes") 372 | { 373 | linear_linked_list lhs { 1, 2, 3 }; 374 | linear_linked_list rhs { 1, 2, 3, 4 }; 375 | 376 | REQUIRE(lhs != rhs); 377 | } 378 | SECTION("Two populated lists of matching sizes") 379 | { 380 | linear_linked_list lhs { 1, 2, 3 }; 381 | linear_linked_list rhs { 1, 2, 4 }; 382 | 383 | REQUIRE(lhs != rhs); 384 | } 385 | SECTION("Two populated lists with matching elements") 386 | { 387 | linear_linked_list lhs { 1, 2, 3 }; 388 | linear_linked_list rhs { 1, 2, 3 }; 389 | 390 | REQUIRE(lhs == rhs); 391 | } 392 | } 393 | 394 | TEST_CASE("Popping the front element off the list", "[operations], [pop_front]") 395 | { 396 | SECTION("pop the front of an empty list") 397 | { 398 | linear_linked_list empty_list; 399 | 400 | REQUIRE(empty_list.pop_front().empty()); 401 | } 402 | SECTION("Pop the front of a list with one element") 403 | { 404 | linear_linked_list list { 1 }; 405 | 406 | REQUIRE(list.pop_front().empty()); 407 | } 408 | SECTION("Pop the front of a populated list") 409 | { 410 | linear_linked_list list { 7, 1, 2, 3 }; 411 | 412 | list.pop_front(); 413 | 414 | int i = 0; 415 | for (const auto& n : list) 416 | { 417 | REQUIRE(n == ++i); 418 | } 419 | } 420 | SECTION("passing out param to pop on an empty list does not modify the param") 421 | { 422 | int i = 7; 423 | 424 | linear_linked_list empty_list; 425 | 426 | REQUIRE(empty_list.pop_front(i) == 7); 427 | } 428 | SECTION("Pop the front of a populated list with a out parameter copies the data") 429 | { 430 | int i = 7; 431 | 432 | linear_linked_list list { 1, 2, 3, 4 }; 433 | 434 | REQUIRE(list.pop_front(i) == 1); 435 | } 436 | SECTION("Popping the list with a class will utilize the move constructor") 437 | { 438 | Data::move_count = 0; 439 | Data out_param; 440 | 441 | linear_linked_list list { 442 | Data(1, "one"), 443 | Data(2, "two"), 444 | Data(3, "three") 445 | }; 446 | 447 | REQUIRE(list.pop_front(out_param) == Data(1, "one")); 448 | REQUIRE(Data::move_count == 1); 449 | } 450 | } 451 | 452 | TEST_CASE("Erasing elements from a list", "[operations], [erase_after]") 453 | { 454 | linear_linked_list empty_list; 455 | linear_linked_list list { 1, 4, 2, 3, 4, 5, 6 }; 456 | 457 | SECTION("Erasing elements from an empty list does nothing and returns the iterator") 458 | { 459 | REQUIRE(empty_list.erase_after(empty_list.begin()) == empty_list.begin()); 460 | } 461 | SECTION("Erasing with a list of a single element does nothing") 462 | { 463 | linear_linked_list singular_list { 1 }; 464 | 465 | REQUIRE(*singular_list.erase_after(singular_list.begin()) == 1); 466 | } 467 | SECTION("Erasing elements from a populated list") 468 | { 469 | list.erase_after(list.begin()); 470 | 471 | int i = 0; 472 | for(auto num : list) 473 | { 474 | REQUIRE(num == ++i); 475 | } 476 | } 477 | SECTION("Erasing with the last iterator in the list does nothing") 478 | { 479 | linear_linked_list::iterator it = list.begin(); 480 | while(*(++it) != 6); 481 | 482 | list.erase_after(it); 483 | 484 | REQUIRE((list.back() == 6) == (*it == 6)); 485 | } 486 | } 487 | 488 | TEST_CASE("Removing a specific element from a list", "[operations], [remove]") 489 | { 490 | linear_linked_list list { 1, 4, 2, 3, 4 }; 491 | 492 | SECTION("Remove from a populated list returns number of items removed") 493 | { 494 | REQUIRE(list.remove(4) == 2); 495 | REQUIRE(list.size() == 3); 496 | } 497 | 498 | SECTION("Removing an item not found in a list") 499 | { 500 | REQUIRE(list.remove(7) == 0); 501 | } 502 | } 503 | 504 | TEST_CASE("Using functors to remove a specific element", "[remove_if]") 505 | { 506 | SECTION("remove_if with a value constructed functor") 507 | { 508 | is_seven functor; 509 | 510 | int nums[] = { 7, 1, 2, 3, 4, 7, 5, 6, 7}; 511 | 512 | linear_linked_list list(nums, nums + 9); 513 | 514 | REQUIRE(list.remove_if(functor) == 3); 515 | 516 | int i = 0; 517 | linear_linked_list::const_iterator it; 518 | for (it = list.begin(); it != list.end(); ++it) 519 | { 520 | REQUIRE(*it == ++i); 521 | } 522 | } 523 | SECTION("remove_if with the element as the head") 524 | { 525 | int nums[] = { 7, 1, 2, 3, 4, 5, 6 }; 526 | 527 | linear_linked_list list(nums, nums + 7); 528 | 529 | REQUIRE(list.remove_if(is_seven())); 530 | 531 | int i = 0; 532 | linear_linked_list::const_iterator it; 533 | for (it = list.begin(); it != list.end(); ++it) 534 | { 535 | REQUIRE(*it == ++i); 536 | } 537 | } 538 | SECTION("remove_if with the element as the tail") 539 | { 540 | int nums[] = { 1, 2, 3, 4, 5, 6, 7 }; 541 | 542 | linear_linked_list list(nums, nums + 7); 543 | 544 | REQUIRE(list.remove_if(is_seven())); 545 | 546 | int i = 0; 547 | linear_linked_list::const_iterator it; 548 | for (it = list.begin(); it != list.end(); ++it) 549 | { 550 | REQUIRE(*it == ++i); 551 | } 552 | } 553 | SECTION("remove_if with no matching element") 554 | { 555 | int nums[] = { 1, 2, 3, 4, 5, 6 }; 556 | 557 | linear_linked_list list(nums, nums + 6); 558 | 559 | REQUIRE(!list.remove_if(is_seven())); 560 | } 561 | SECTION("remove_if with an empty list") 562 | { 563 | linear_linked_list list; 564 | 565 | REQUIRE_FALSE(list.remove_if(is_seven())); 566 | } 567 | } 568 | 569 | TEST_CASE("Using mutable iterators to modify data", "[iterators]") 570 | { 571 | int nums[] = { 1, 2, 3, 4, 5, 6, 7 }; 572 | 573 | linear_linked_list list(nums, nums + 7); 574 | 575 | int i = 1; 576 | for (linear_linked_list::iterator it = list.begin(); it != list.end(); ++it) 577 | { 578 | ++(*it); 579 | bool assert = *it = ++i; 580 | REQUIRE(assert); 581 | } 582 | } 583 | 584 | TEST_CASE("Finding the middle point of a list", "[iterators]") 585 | { 586 | SECTION("Populated list with odd number of elements") 587 | { 588 | linear_linked_list list { 1, 2, 3, 4, 5, 6, 7 }; 589 | 590 | REQUIRE(*list.middle() == 4); 591 | } 592 | SECTION("Populated list with even number of elements") 593 | { 594 | linear_linked_list list { 1, 2, 3, 4, 5, 6 }; 595 | 596 | REQUIRE(*list.middle() == 3); 597 | } 598 | SECTION("Middle point of a list with two elements is the beginning") 599 | { 600 | linear_linked_list list { 1, 2 }; 601 | 602 | REQUIRE(*list.middle() == 1); 603 | } 604 | SECTION("Middle point of a list with one element is the beginning") 605 | { 606 | linear_linked_list list { 1 }; 607 | 608 | REQUIRE(*list.middle() == 1); 609 | } 610 | SECTION("Calling middle on the empty list returns an end iterator") 611 | { 612 | linear_linked_list list; 613 | 614 | REQUIRE(list.middle() == list.end()); 615 | } 616 | } 617 | 618 | TEST_CASE("Reversing the order of a list", "[reverse]") 619 | { 620 | SECTION("Empty list") 621 | { 622 | linear_linked_list empty_list; 623 | 624 | REQUIRE(empty_list.reverse().empty()); 625 | } 626 | SECTION("A list with one element") 627 | { 628 | 629 | linear_linked_list list { 1 }; 630 | 631 | REQUIRE((list.reverse().front() == (list.back() == 1))); 632 | } 633 | SECTION("A populated list") 634 | { 635 | linear_linked_list list { 5, 4, 3, 2, 1 }; 636 | 637 | list.reverse(); 638 | 639 | int i = 0; 640 | for(auto num : list) 641 | { 642 | REQUIRE(num == ++i); 643 | } 644 | } 645 | } 646 | 647 | TEST_CASE("Merging two sorted lists", "[merge]") 648 | { 649 | SECTION("Two lists of equal size") 650 | { 651 | linear_linked_list first { 1, 3, 5 }; 652 | linear_linked_list second { 2, 4, 6 }; 653 | 654 | first.merge(second); 655 | 656 | int i = 0; 657 | for (auto num : first) 658 | { 659 | REQUIRE(num == ++i); 660 | } 661 | REQUIRE(second.empty()); 662 | REQUIRE(first.front() == 1); 663 | REQUIRE(first.back() == 6); 664 | } 665 | SECTION("second list replaces head") 666 | { 667 | linear_linked_list first { 2, 4, 6 }; 668 | linear_linked_list second { 1, 3, 5 }; 669 | 670 | first.merge(second); 671 | 672 | int i = 0; 673 | for (auto num : first) 674 | { 675 | REQUIRE(num == ++i); 676 | } 677 | REQUIRE(second.empty()); 678 | REQUIRE(first.front() == 1); 679 | REQUIRE(first.back() == 6); 680 | } 681 | SECTION("Two non-alternating lists") 682 | { 683 | linear_linked_list first { 1, 2, 3 }; 684 | linear_linked_list second { 4, 5, 6 }; 685 | 686 | first.merge(second); 687 | 688 | int i = 0; 689 | for (auto num : first) 690 | { 691 | REQUIRE(num == ++i); 692 | } 693 | REQUIRE(second.empty()); 694 | REQUIRE(first.front() == 1); 695 | REQUIRE(first.back() == 6); 696 | } 697 | SECTION("Lists of varying sizes") 698 | { 699 | linear_linked_list first { 6 }; 700 | linear_linked_list second { 1, 2, 3, 4, 5 }; 701 | 702 | first.merge(second); 703 | 704 | int i = 0; 705 | for (auto num : first) 706 | { 707 | REQUIRE(num == ++i); 708 | } 709 | REQUIRE(second.empty()); 710 | REQUIRE(first.front() == 1); 711 | REQUIRE(first.back() == 6); 712 | } 713 | SECTION("Merge a populated list into an empty list") 714 | { 715 | linear_linked_list first; 716 | linear_linked_list second { 1, 2, 3, 4, 5 }; 717 | 718 | first.merge(second); 719 | 720 | int i = 0; 721 | for (auto num : first) 722 | { 723 | REQUIRE(num == ++i); 724 | } 725 | REQUIRE(second.empty()); 726 | REQUIRE(first.front() == 1); 727 | REQUIRE(first.back() == 5); 728 | } 729 | SECTION("Merge an empty list into a populated list") 730 | { 731 | linear_linked_list first { 1, 2, 3, 4, 5 }; 732 | linear_linked_list second; 733 | 734 | first.merge(second); 735 | 736 | int i = 0; 737 | for (auto num : first) 738 | { 739 | REQUIRE(num == ++i); 740 | } 741 | REQUIRE(second.empty()); 742 | REQUIRE(first.front() == 1); 743 | REQUIRE(first.back() == 5); 744 | } 745 | SECTION("Merge two empty lists") 746 | { 747 | linear_linked_list first; 748 | linear_linked_list second; 749 | 750 | REQUIRE(first.merge(second).empty()); 751 | REQUIRE(second.empty()); 752 | } 753 | SECTION("Merge two lists with a custom compare function") 754 | { 755 | linear_linked_list first { 3, 2, 1 }; 756 | linear_linked_list second { 6, 5, 4 }; 757 | 758 | first.merge(second, [](int left, int right){ return left > right; }); 759 | 760 | int i = 7; 761 | for (auto num : first) 762 | { 763 | REQUIRE(num == --i); 764 | } 765 | REQUIRE(second.empty()); 766 | REQUIRE(first.front() == 6); 767 | REQUIRE(first.back() == 1); 768 | } 769 | SECTION("Merge two lists of one element each") 770 | { 771 | linear_linked_list first { 2 }; 772 | linear_linked_list second { 1 }; 773 | 774 | first.merge(second); 775 | 776 | REQUIRE(first.front() == 1); 777 | REQUIRE(first.back() == 2); 778 | REQUIRE(second.empty()); 779 | } 780 | } 781 | 782 | TEST_CASE("Splitting lists into smaller lists with iterators", "[split]") 783 | { 784 | SECTION("Break the head off a populated list") 785 | { 786 | linear_linked_list head { 1, 2, 3, 4, 5, 6 }; 787 | linear_linked_list tail = head.split(head.begin()); 788 | 789 | REQUIRE(head.front() == 1); 790 | 791 | int i = 1; 792 | for(auto num : tail) 793 | { 794 | REQUIRE(num == ++i); 795 | } 796 | } 797 | SECTION("Split the list in half") 798 | { 799 | linear_linked_list left { 1, 2, 3, 4, 5, 6, 7 }; 800 | linear_linked_list right = left.split(left.middle()); 801 | 802 | REQUIRE(left.back() == 4); 803 | 804 | int i = 4; 805 | for(auto num : right) 806 | { 807 | REQUIRE(num == ++i); 808 | } 809 | } 810 | SECTION("Split a empty list") 811 | { 812 | linear_linked_list left; 813 | linear_linked_list right = left.split(left.begin()); 814 | 815 | REQUIRE(left.empty()); 816 | REQUIRE(right.empty()); 817 | } 818 | SECTION("Splitting a list with one element returns the empty list") 819 | { 820 | linear_linked_list left { 1 }; 821 | linear_linked_list right = left.split(left.middle()); 822 | 823 | REQUIRE(left.front() == 1); 824 | REQUIRE(right.empty()); 825 | REQUIRE_THROWS(right.back()); 826 | } 827 | SECTION("Splitting at the end of the list returns the empty list") 828 | { 829 | linear_linked_list left { 1, 2, 3, 4, 5, 6, 7 }; 830 | 831 | linear_linked_list::const_iterator it = left.begin(); 832 | 833 | while(*(it++) != 7); 834 | 835 | linear_linked_list right = left.split(it); 836 | 837 | REQUIRE(right.empty()); 838 | REQUIRE_THROWS(right.back()); // Head and Tail should be NULL and throw an error 839 | REQUIRE_THROWS(right.front()); 840 | } 841 | SECTION("Splitting the list with an end iterator returns the empty list") 842 | { 843 | linear_linked_list left { 1, 2, 3, 4, 5, 6, 7 }; 844 | 845 | linear_linked_list right = left.split(left.end()); 846 | 847 | REQUIRE(right.empty()); 848 | } 849 | SECTION("Splitting the list with an iterator does not modify the iterator") 850 | { 851 | linear_linked_list list { 1, 2, 3, 4, 5 }; 852 | 853 | linear_linked_list::iterator it = list.begin(); 854 | linear_linked_list split = list.split(it); 855 | 856 | REQUIRE(*it == (*list.begin() == 1)); 857 | } 858 | } 859 | 860 | TEST_CASE("Sorting lists", "[sort]") 861 | { 862 | SECTION("Default sort is to sort into ascending order") 863 | { 864 | linear_linked_list list { 3, 5, 2, 1, 4, 6 }; 865 | 866 | list.sort(); 867 | 868 | int i = 0; 869 | for(auto num : list) 870 | { 871 | REQUIRE(num == ++i); 872 | } 873 | } 874 | SECTION("Sorting a list with a custom compare function") 875 | { 876 | linear_linked_list list { 3, 5, 2, 1, 4, 6 }; 877 | 878 | list.sort([](int lhs, int rhs){ return lhs > rhs; }); 879 | 880 | int i = 7; 881 | for(auto num : list) 882 | { 883 | REQUIRE(num == --i); 884 | } 885 | } 886 | SECTION("Sorting a sorted list") 887 | { 888 | linear_linked_list list { 1, 2, 3, 4, 5, 6 }; 889 | 890 | list.sort(); 891 | 892 | int i = 0; 893 | for(auto& num : list) 894 | { 895 | REQUIRE(num == ++i); 896 | } 897 | } 898 | } 899 | 900 | -------------------------------------------------------------------------------- /tests/test_config_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: test_config_main.cpp 3 | * 4 | * https://github.com/catchorg/Catch2/blob/master/docs/slow-compiles.md#top 5 | */ 6 | 7 | #define CATCH_CONFIG_MAIN 8 | #include "catch.hpp" 9 | 10 | --------------------------------------------------------------------------------