├── .github └── FUNDING.yml ├── .gitignore ├── Data Structures and Other Objects Using Cpp_4th edition_Michael Main Walter Savitch.pdf ├── README.md ├── chapter_10 ├── CMakeLists.txt ├── binary_search_tree.hpp ├── expression_tree.hpp ├── main.cpp ├── node.hpp └── tree_projects.cpp ├── chapter_11 ├── CMakeLists.txt ├── logarithm_implementation.hpp └── main.cpp ├── chapter_12 ├── CMakeLists.txt ├── dictionary.csv ├── dictionary.h ├── main.cpp └── rapid_csv.h ├── chapter_13 ├── CMakeLists.txt ├── main.cpp └── radix_sorter.hpp ├── chapter_14 ├── CMakeLists.txt ├── Female.hpp ├── Male.hpp ├── MembershipRoster.hpp ├── Person.hpp ├── date.h └── main.cpp ├── chapter_15 ├── CMakeLists.txt ├── Graph.h └── main.cpp ├── chapter_2 ├── lunar_lander.cpp ├── lunar_lander.h ├── main.cpp ├── statistician.cpp └── statistician.h ├── chapter_3 ├── CMakeLists.txt ├── main.cpp ├── peg.cpp ├── peg.h ├── towers.cpp └── towers.h ├── chapter_4 ├── ChoreManager.cpp ├── ChoreManager.h ├── chores.txt └── main.cpp ├── chapter_5 ├── linked_digit_list.cpp └── linked_digit_list.h ├── chapter_6 ├── CMakeLists.txt ├── gift_list.cpp ├── gift_list.h └── main.cpp ├── chapter_7 ├── CMakeLists.txt ├── expression_parser.cpp ├── expression_parser.h └── main.cpp ├── chapter_8 ├── CMakeLists.txt ├── main.cpp ├── store_checkout.cpp └── store_checkout.h └── chapter_9 ├── CMakeLists.txt ├── chapter_9_projects.hpp └── main.cpp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [raleighlittles] 4 | -------------------------------------------------------------------------------- /.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 | 34 | .idea/ 35 | .Rhistory 36 | cmake-build-debug/ 37 | *.cbp 38 | CMakeCache.txt 39 | *.cmake 40 | CMakeFiles/ 41 | Makefile -------------------------------------------------------------------------------- /Data Structures and Other Objects Using Cpp_4th edition_Michael Main Walter Savitch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raleighlittles/Data_Structures_and_Other_Objects_Using_Cpp/f7daf9693b3cd96d17c5fc0711bccb665bc6e29d/Data Structures and Other Objects Using Cpp_4th edition_Michael Main Walter Savitch.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | A collection of end-of-chapter projects from "Data Structures and Other Objects Using C++" (4th edition) by Michael Main and Walter Savitch. 3 | 4 | Besides STL, some examples rely on: 5 | 6 | 1. Boost 7 | 2. [Rapid CSV](https://github.com/d99kris/rapidcsv) 8 | 3. [date](https://github.com/HowardHinnant/date), which will be added to the C++ standard library in C++20 9 | 10 | # Table of contents 11 | 12 | Some chapters have more than one exercise grouped together. 13 | 14 | | Chapter | Exercise name | 15 | | ------- | ------------- | 16 | | 2 | Lunar lander, Statistician | 17 | | 3 | Towers of Hanoi | 18 | | 4 | Chore manager | 19 | | 5 | Linked List of digits | 20 | | 6 | Gift list | 21 | | 7 | Postfix and Infix expression parser | 22 | | 8 | Store checkout | 23 | | 9 | Ackermann function | 24 | | 10 | Binary Search Tree / Expression Tree | 25 | | 11 | Logarithm | 26 | | 12 | Word dictionary | 27 | | 13 | Radix sorter | 28 | | 14 | Gym membership roster | 29 | | 15 | Prim's Minimum Spanning Tree | -------------------------------------------------------------------------------- /chapter_10/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(chapter_10) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(chapter_10 main.cpp tree_projects.cpp binary_search_tree.hpp expression_tree.hpp node.hpp) -------------------------------------------------------------------------------- /chapter_10/binary_search_tree.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/23/18. 3 | // 4 | 5 | #ifndef CHAPTER_10_BST_H 6 | #define CHAPTER_10_BST_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "node.hpp" 13 | 14 | template 15 | class BinarySearchTree 16 | { 17 | public: 18 | std::shared_ptr> root_node; 19 | unsigned int nodes_count = 0; 20 | 21 | void add_node(Node node) 22 | { 23 | nodes_count++; 24 | 25 | if (this->root_node) 26 | { 27 | std::cout << "Attempting to add node with value : " << node.node_value << std::endl; 28 | auto res = add_node_recursive(std::make_shared>(node), node.node_value); 29 | } 30 | 31 | 32 | else 33 | { 34 | std::cout << "Empty tree, currently adding node with value " << node.node_value << std::endl; 35 | this->root_node = std::make_shared>(node.node_value); 36 | } 37 | 38 | } 39 | 40 | std::shared_ptr> insert(std::shared_ptr> node_to_insert_at, Type value_to_insert) 41 | { 42 | if (!node_to_insert_at) 43 | { 44 | return std::make_shared>(Node(value_to_insert)); 45 | } 46 | 47 | if (value_to_insert < node_to_insert_at->node_value) 48 | { 49 | node_to_insert_at->left_node = insert(node_to_insert_at->left_node, value_to_insert); 50 | 51 | } 52 | 53 | else if (value_to_insert > node_to_insert_at->node_value) 54 | { 55 | node_to_insert_at->right_node = insert(node_to_insert_at->right_node, value_to_insert); 56 | } 57 | 58 | return node_to_insert_at; 59 | } 60 | 61 | 62 | std::shared_ptr> add_node_recursive(std::shared_ptr> new_node, Type node_value) 63 | { 64 | 65 | if (node_value < new_node->node_value) 66 | { 67 | new_node->left_node = add_node_recursive(new_node->left_node, node_value); 68 | } 69 | 70 | else if (node_value > new_node->node_value) 71 | { 72 | new_node->right_node = add_node_recursive(new_node->right_node, node_value); 73 | } 74 | 75 | return new_node; 76 | } 77 | 78 | void print_nodes_recursive(std::shared_ptr> root_node) 79 | { 80 | if (root_node == nullptr) 81 | { 82 | return; 83 | } 84 | 85 | print_nodes_recursive(root_node->left_node); 86 | std::cout << "Value : " << root_node->node_value << " "; 87 | print_nodes_recursive(root_node->right_node); 88 | } 89 | 90 | void delete_value_recursive(std::shared_ptr> parent_node, std::shared_ptr> current_node, Type value_to_delete) 91 | { 92 | if (current_node == false) 93 | { 94 | if (current_node->node_value == value_to_delete) 95 | { 96 | if ((current_node->left_node == nullptr) or (current_node->right_node == nullptr)) 97 | { 98 | std::shared_ptr> temporary = current_node->left_node; 99 | if (current_node->right_node) 100 | { 101 | temporary = current_node->right_node; 102 | } 103 | 104 | if (parent_node) 105 | { 106 | if (parent_node->left == current_node) 107 | { 108 | parent_node->left = temporary; 109 | } 110 | 111 | else 112 | { 113 | parent_node->right_node = temporary; 114 | } 115 | } 116 | 117 | else 118 | { 119 | this->root_node = temporary; 120 | } 121 | } 122 | 123 | else 124 | { 125 | std::shared_ptr> substitute = current_node->right_node; 126 | while (substitute->left_node) 127 | { 128 | substitute = substitute->left_node; 129 | } 130 | 131 | Type temporary_value = current_node->value; 132 | current_node->value = substitute->node_value, 133 | substitute->node_value = temporary_value; 134 | return delete_value_recursive(current_node, current_node->right_node, temporary_value); 135 | } 136 | 137 | } 138 | } 139 | } 140 | 141 | void print_nodes() 142 | { 143 | print_nodes_recursive(root_node); 144 | } 145 | 146 | void delete_value(Type value) 147 | { 148 | return delete_value_recursive(nullptr, this->root_node, value); 149 | } 150 | 151 | explicit BinarySearchTree(const Type initial_value) 152 | { 153 | //std::cout << "BST constructor called with " << initial_value << std::endl; 154 | 155 | this->root_node = std::make_shared>(Node(initial_value)); 156 | } 157 | 158 | explicit BinarySearchTree(const BinarySearchTree& bst) 159 | { // copy constructor 160 | 161 | if (bst.root_node == nullptr) 162 | { 163 | root_node = nullptr; 164 | } 165 | 166 | else 167 | { 168 | copy_tree(this->root_node, bst.root_node); 169 | } 170 | 171 | this->nodes_count = bst.nodes_count; 172 | } 173 | 174 | void copy_tree(std::shared_ptr> new_root, std::shared_ptr> existing_root) 175 | { 176 | if (existing_root == nullptr) 177 | { 178 | new_root = nullptr; 179 | } 180 | 181 | else 182 | { 183 | new_root->node_value = existing_root->node_value; 184 | copy_tree(new_root->left_node, existing_root->left_node); 185 | copy_tree(new_root->right_node, existing_root->right_node); 186 | } 187 | } 188 | 189 | 190 | }; 191 | 192 | #endif // CHAPTER_10_BST_H -------------------------------------------------------------------------------- /chapter_10/expression_tree.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/24/18. 3 | // 4 | 5 | 6 | #ifndef CHAPTER_10_EXPRESSION_TREE_H 7 | #define CHAPTER_10_EXPRESSION_TREE_H 8 | 9 | #include "node.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /* 21 | * Problem #1 22 | * This project deals with a simple kind of 23 | expression tree, where there are two kinds of 24 | nodes: 25 | (a) Leaf nodes, which contain a real number as 26 | their entry; 27 | (b) Nonleaf nodes, which contain either the char- 28 | acter + or the character * as their entry, and have ex- 29 | actly two children. 30 | 1 31 | For this project, implement a class for expression 32 | trees, including operations for building expression 33 | trees. Also include a recursive function to “evalu- 34 | ate” a non-empty expression tree using these rules: 35 | (a) If the tree has only one node (which must be 36 | a leaf), then the evaluation of the tree returns the real 37 | number that is the node’s entry;Programming Projects 38 | (b) If the tree has more than one node, and the 39 | root contains +, then first evaluate the left subtree, 40 | then evaluate the right subtree, and add the results. 41 | If the root contains *, then evaluate the two subtrees 42 | and multiply the results. 43 | For example, consid- 44 | '*' 45 | er the small expression 46 | tree shown to the right. 47 | The left subtree evalu- 48 | '+' 49 | 14 50 | ates to 3+7, which is 10. 51 | The right subtree evalu- 52 | 3 53 | 7 54 | ates to 14. So the entire 55 | tree evaluates to 10*14, 56 | which is 140. 57 | */ 58 | template 59 | class ExpressionTree 60 | { 61 | private: 62 | std::array possible_operators = {"+", "-", "/", "*"}; 63 | 64 | bool is_operator(std::string string_to_check) 65 | { 66 | return (std::find(possible_operators.begin(), possible_operators.end(), string_to_check) != possible_operators.end()); 67 | } 68 | 69 | bool is_leaf_node(std::shared_ptr> node) 70 | { 71 | return ((node->left_node == nullptr) and (node->right_node == nullptr)); 72 | } 73 | 74 | public: 75 | 76 | size_t size = 0; 77 | 78 | std::shared_ptr> root_node; 79 | 80 | explicit ExpressionTree(const std::string &postfix_expression) 81 | { 82 | std::stack>> expression_stack; 83 | 84 | std::shared_ptr> node1, node2, node3; 85 | 86 | std::istringstream stringstream(postfix_expression); 87 | 88 | for (std::string character; stringstream >> character;) { 89 | 90 | this->size++; 91 | 92 | 93 | if (is_operator(character) == false) 94 | { 95 | node1 = std::make_shared>(character); 96 | 97 | expression_stack.push(node1); 98 | } 99 | else 100 | { 101 | 102 | node1 = std::make_shared>(character); 103 | 104 | node2 = expression_stack.top(); 105 | 106 | expression_stack.pop(); 107 | 108 | node3 = expression_stack.top(); 109 | 110 | expression_stack.pop(); 111 | 112 | node1->right_node = node2; 113 | 114 | node1->left_node = node3; 115 | 116 | expression_stack.push(node1); 117 | 118 | } 119 | 120 | } 121 | 122 | assert(expression_stack.empty() == false); 123 | 124 | node1 = expression_stack.top(); 125 | 126 | expression_stack.pop(); 127 | 128 | assert(expression_stack.empty() == true); 129 | 130 | this->root_node = node1; 131 | 132 | } 133 | 134 | double evaluate(std::shared_ptr> root_node) { 135 | 136 | if (root_node == nullptr) 137 | { 138 | assert(this->size != 0); 139 | throw(std::invalid_argument("Null pointer exception, empty tree")); 140 | } 141 | 142 | if (is_leaf_node(root_node)) 143 | { 144 | return std::stod(root_node->node_value); 145 | } 146 | 147 | double left_value = evaluate(root_node->left_node); 148 | double right_value = evaluate(root_node->right_node); 149 | 150 | 151 | if (root_node->node_value == "+") 152 | { 153 | return left_value + right_value; 154 | } 155 | 156 | if (root_node->node_value == "-") 157 | { 158 | return left_value - right_value; 159 | } 160 | 161 | if (root_node->node_value == "/") 162 | { 163 | return left_value / right_value; 164 | } 165 | if (root_node->node_value == "*" ) 166 | { 167 | return left_value * right_value; 168 | } 169 | else 170 | { 171 | throw(std::invalid_argument("Found a node that wasn't an operator type, or a leaf node.")); 172 | 173 | } 174 | 175 | } 176 | 177 | }; 178 | 179 | #endif //CHAPTER_10_EXPRESSION_TREE_H 180 | -------------------------------------------------------------------------------- /chapter_10/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tree_projects.cpp" 5 | #include "expression_tree.hpp" 6 | 7 | int main() { 8 | // test expression tree 9 | 10 | const std::string sample_postfix_expression = "10 2 8 * + 3 -"; 11 | 12 | ExpressionTree et = ExpressionTree(sample_postfix_expression); 13 | 14 | assert(et.root_node != nullptr); 15 | 16 | std::cout << "Expression tree result " << et.evaluate(et.root_node) << std::endl; 17 | 18 | // test BST/LL 19 | 20 | BinarySearchTree bst = BinarySearchTree(10); 21 | 22 | bst.insert(bst.root_node, 12); 23 | 24 | bst.insert(bst.root_node, 15); 25 | 26 | bst.insert(bst.root_node, 25); 27 | 28 | bst.insert(bst.root_node, 30); 29 | 30 | bst.insert(bst.root_node, 36); 31 | 32 | std::list ll; 33 | 34 | std::cout << "Beginning to convert BST to List." << std::endl; 35 | 36 | ll = Trees::convert_BST_to_LL(bst); 37 | 38 | std::cout << "This should now be a sorted list of " << bst.nodes_count << " eleemnts." << std::endl; 39 | 40 | for (auto el : ll) 41 | { 42 | std::cout << el << std::endl; 43 | } 44 | 45 | 46 | } -------------------------------------------------------------------------------- /chapter_10/node.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/24/18. 3 | // 4 | 5 | 6 | #ifndef CHAPTER_10_NODE_H 7 | #define CHAPTER_10_NODE_H 8 | 9 | #include 10 | 11 | template 12 | class Node 13 | { 14 | public: 15 | NodeType node_value; 16 | std::shared_ptr left_node; 17 | std::shared_ptr right_node; 18 | 19 | explicit Node(NodeType value) 20 | : 21 | node_value(value) 22 | { 23 | // empty constructor 24 | } 25 | 26 | Node(NodeType value, Node left, Node right) 27 | : 28 | node_value(value), 29 | left_node(std::make_shared>(left)), 30 | right_node(std::make_shared>(right)) 31 | { 32 | 33 | } 34 | 35 | // copy constructor 36 | Node(const Node ©) 37 | : 38 | node_value(copy.node_value), 39 | left_node(copy.left_node), 40 | right_node(copy.right_node) 41 | { 42 | // empty constructor 43 | } 44 | 45 | // assignment operator 46 | Node& operator=(const Node &node) 47 | { 48 | this->node_value = node.node_value; 49 | this->left_node = node.left_node; 50 | this->right_node = node.right_node; 51 | 52 | return *this; // so you can "chain" assignments 53 | } 54 | 55 | }; 56 | 57 | #endif //CHAPTER_10_NODE_H 58 | -------------------------------------------------------------------------------- /chapter_10/tree_projects.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/23/18. 3 | // 4 | #include "binary_search_tree.hpp" 5 | 6 | #include 7 | #include 8 | // Problem #8 9 | // Write a function that takes a binary search 10 | // tree as input and produces a linked list of 11 | // the entries, with the entries sorted (smallest 12 | // entries at the front of the list and largest entries at the 13 | // back). Hint: use in-order traversal. 14 | 15 | namespace Trees 16 | { 17 | template 18 | std::list convert_BST_to_LL(BinarySearchTree& input) 19 | { 20 | std::list result; 21 | 22 | std::stack>> stack; 23 | 24 | std::shared_ptr> current = input.root_node; 25 | 26 | while ((current != nullptr) or (stack.empty() == false)) 27 | { 28 | while (current != nullptr) 29 | { 30 | stack.push(current); 31 | current = current->left_node; 32 | } 33 | 34 | assert(current == nullptr); 35 | 36 | current = stack.top(); 37 | 38 | stack.pop(); 39 | 40 | result.push_back(current->node_value); 41 | 42 | current = current->right_node; 43 | 44 | } 45 | 46 | return result; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /chapter_11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(chapter_11) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(chapter_11 main.cpp logarithm_implementation.hpp) -------------------------------------------------------------------------------- /chapter_11/logarithm_implementation.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 12/28/18. 3 | // 4 | 5 | //The facility provides a standard 6 | // function log(x) , which returns the natural 7 | // logarithm of x (with a base that is ap- 8 | //proximately 2.71828). This base is written e, and the 9 | // logarithms with this base are called natural 10 | //logarithms, or Napierian logarithms, after the 11 | // Scottish mathematician John Napier (1550–1617) 12 | //who invented these logarithms. The number e may 13 | //seem like a strange choice for the base of a 14 | //logarithm, but the choice is motivated by the fact 15 | //that natural logarithms are easy to approximate as 16 | //the sum of a series, and they also occur as the limits 17 | //of series such as the computation of compound 18 | //interest. In your math class, you probably used the 19 | //notation ln x for the natural logarithm of x. 20 | //Anyway, in this project, you are to write a 21 | // function: 22 | // 23 | //double logb(double base, double x); 24 | //The function returns the logarithm of x to the given 25 | //base . Make use of the log function and the formulas 26 | //in Section 11.4. 27 | 28 | #include 29 | 30 | namespace TreeProjects { 31 | 32 | double logb(double base, double x) { 33 | // Using the well-known change of base formula for logarithms, and the formula on page 576 34 | return std::log(x) / std::log(base); 35 | } 36 | 37 | }; -------------------------------------------------------------------------------- /chapter_11/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | double base = 10; 5 | double number = 832; 6 | double result = TreeProjects::logb(base, number); 7 | std::cout << result << std::endl; 8 | // prints: 2.92012 9 | } -------------------------------------------------------------------------------- /chapter_12/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(chapter_12) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | find_package(Boost COMPONENTS locale REQUIRED) 6 | include_directories("usr/lib/") 7 | 8 | add_executable(chapter_12 main.cpp dictionary.h rapid_csv.h) 9 | target_link_libraries(chapter_12 ${Boost_LIBRARIES}) 10 | -------------------------------------------------------------------------------- /chapter_12/dictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 12/28/18. 3 | // 4 | 5 | #ifndef CHAPTER_12_DICTIONARY_H 6 | 7 | #define CHAPTER_12_DICTIONARY_H 8 | 9 | // Problem statement 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "rapid_csv.h" 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | /* Implement a dictionary program using a 22 | table. The user inputs a word, and the word’s 23 | definition is displayed. You will need to re- 24 | vise one of the table implementations to use a string 25 | as the search key. Use a hash function similar to the 26 | one you implemented for Self-Test Exercise 28. 27 | */ 28 | 29 | class WordDictionary { 30 | private: 31 | std::map dictionary_table; 32 | std::size_t dictionary_size = dictionary_table.size(); 33 | std::hash hasher; 34 | 35 | 36 | public: 37 | // Requires full path to executable 38 | explicit WordDictionary( 39 | std::string dictionary_filename) { 40 | 41 | rapidcsv::Document document(dictionary_filename, rapidcsv::LabelParams(0, -1)); 42 | 43 | std::vector column_names = document.GetColumnNames(); 44 | 45 | std::vector definitions = document.GetColumn("definition"); 46 | 47 | std::vector words = document.GetColumn("word"); 48 | 49 | assert(definitions.size() == words.size()); 50 | 51 | for (auto word = words.begin(); word != words.end(); ++word) { 52 | long index = std::distance(words.begin(), word); 53 | 54 | std::regex pattern("\""); 55 | 56 | std::string word_without_quotes = std::regex_replace(*word, pattern, ""); 57 | 58 | std::size_t hash_result = hasher(word_without_quotes); 59 | 60 | dictionary_table.insert(std::make_pair(hash_result, definitions[index])); 61 | } 62 | 63 | } 64 | 65 | std::string lookup(std::string word_to_search) { 66 | // https://stackoverflow.com/questions/13130359/why-is-stdbad-cast-thrown-by-boostlocale 67 | // The lines below are needed for boost 68 | boost::locale::generator gen; 69 | std::locale loc = gen(""); 70 | std::locale::global(loc); 71 | 72 | // Using boost separately for just this single line of code was kind of overkill, but C++ is stupid for not having a built in capitalize() method 73 | // like Rails does.. 74 | std::size_t desired_hash = hasher(boost::locale::to_title(word_to_search)); 75 | 76 | auto result = dictionary_table.find(desired_hash); 77 | 78 | if (result != dictionary_table.end()) { 79 | return result->second; 80 | } else { 81 | return ""; 82 | } 83 | } 84 | }; 85 | 86 | #endif //CHAPTER_12_DICTIONARY_H 87 | -------------------------------------------------------------------------------- /chapter_12/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dictionary.h" 3 | int main() 4 | { 5 | 6 | WordDictionary dictionary = WordDictionary("/home/raleigh/Programming/Github_Projects/Data_Structures_and_Other_Objects_Using_Cpp/chapter_12/dictionary.csv"); 7 | 8 | std::string definition = dictionary.lookup("vent"); 9 | 10 | // only outputs the first definition 11 | std::cout << "Definition: " << definition << std::endl; 12 | } 13 | -------------------------------------------------------------------------------- /chapter_12/rapid_csv.h: -------------------------------------------------------------------------------- 1 | // CSV parser library taken from: https://github.com/d99kris/rapidcsv/blob/master/src/rapidcsv.h 2 | 3 | #ifndef CHAPTER_12_RAPID_CSV_H 4 | #define CHAPTER_12_RAPID_CSV_H 5 | 6 | /* 7 | * rapidcsv.h 8 | * 9 | * URL: https://github.com/d99kris/rapidcsv 10 | * Version: 3.1 11 | * 12 | * Copyright (C) 2017-2018 Kristofer Berggren 13 | * All rights reserved. 14 | * 15 | * rapidcsv is distributed under the BSD 3-Clause license, see LICENSE for details. 16 | * 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #if defined(_MSC_VER) 33 | #include 34 | typedef SSIZE_T ssize_t; 35 | #endif 36 | 37 | namespace rapidcsv { 38 | #if defined(_MSC_VER) 39 | static const bool sPlatformHasCR = true; 40 | #else 41 | static const bool sPlatformHasCR = false; 42 | #endif 43 | 44 | /** 45 | * @brief Datastructure holding parameters controlling how invalid numbers (including 46 | * empty strings) should be handled. 47 | */ 48 | struct ConverterParams { 49 | /** 50 | * @brief Constructor 51 | * @param pHasDefaultConverter specifies if conversion of non-numerical strings shall be 52 | * converted to a default numerical value, instead of causing 53 | * an exception to be thrown (default). 54 | * @param pDefaultFloat floating-point default value to represent invalid numbers. 55 | * @param pDefaultInteger integer default value to represent invalid numbers. 56 | */ 57 | explicit ConverterParams(const bool pHasDefaultConverter = false, 58 | const long double pDefaultFloat = std::numeric_limits::signaling_NaN(), 59 | const long long pDefaultInteger = 0) 60 | : mHasDefaultConverter(pHasDefaultConverter), mDefaultFloat(pDefaultFloat), 61 | mDefaultInteger(pDefaultInteger) { 62 | } 63 | 64 | /** 65 | * @brief specifies if conversion of non-numerical strings shall be converted to a default 66 | * numerical value, instead of causing an exception to be thrown (default). 67 | */ 68 | bool mHasDefaultConverter; 69 | 70 | /** 71 | * @brief floating-point default value to represent invalid numbers. 72 | */ 73 | long double mDefaultFloat; 74 | 75 | /** 76 | * @brief integer default value to represent invalid numbers. 77 | */ 78 | long long mDefaultInteger; 79 | }; 80 | 81 | /** 82 | * @brief Exception thrown when attempting to access Document data in a datatype which 83 | * is not supported by the Converter class. 84 | */ 85 | class no_converter : public std::exception { 86 | /** 87 | * @brief Provides details about the exception 88 | * @returns an explanatory string 89 | */ 90 | virtual const char *what() const throw() { 91 | return "unsupported conversion datatype"; 92 | } 93 | }; 94 | 95 | /** 96 | * @brief Class providing conversion to/from numerical datatypes and strings. Only 97 | * intended for rapidcsv internal usage, but exposed externally to allow 98 | * specialization for custom datatype conversions. 99 | */ 100 | template 101 | class Converter { 102 | public: 103 | /** 104 | * @brief Constructor 105 | * @param pConverterParams specifies how conversion of non-numerical values to 106 | * numerical datatype shall be handled. 107 | */ 108 | Converter(const ConverterParams &pConverterParams) 109 | : mConverterParams(pConverterParams) { 110 | } 111 | 112 | /** 113 | * @brief Converts numerical value to string representation. 114 | * @param pVal numerical value 115 | * @param pStr output string 116 | */ 117 | void ToStr(const T &pVal, std::string &pStr) const { 118 | if (typeid(T) == typeid(int) || 119 | typeid(T) == typeid(long) || 120 | typeid(T) == typeid(long long) || 121 | typeid(T) == typeid(unsigned) || 122 | typeid(T) == typeid(unsigned long) || 123 | typeid(T) == typeid(unsigned long long) || 124 | typeid(T) == typeid(float) || 125 | typeid(T) == typeid(double) || 126 | typeid(T) == typeid(long double) || 127 | typeid(T) == typeid(char)) { 128 | std::ostringstream out; 129 | out << pVal; 130 | pStr = out.str(); 131 | } else { 132 | throw no_converter(); 133 | } 134 | } 135 | 136 | /** 137 | * @brief Converts string holding a numerical value to numerical datatype representation. 138 | * @param pVal numerical value 139 | * @param pStr output string 140 | */ 141 | void ToVal(const std::string &pStr, T &pVal) const { 142 | try { 143 | if (typeid(T) == typeid(int)) { 144 | pVal = static_cast(std::stoi(pStr)); 145 | return; 146 | } else if (typeid(T) == typeid(long)) { 147 | pVal = static_cast(std::stol(pStr)); 148 | return; 149 | } else if (typeid(T) == typeid(long long)) { 150 | pVal = static_cast(std::stoll(pStr)); 151 | return; 152 | } else if (typeid(T) == typeid(unsigned)) { 153 | pVal = static_cast(std::stoul(pStr)); 154 | return; 155 | } else if (typeid(T) == typeid(unsigned long)) { 156 | pVal = static_cast(std::stoul(pStr)); 157 | return; 158 | } else if (typeid(T) == typeid(unsigned long long)) { 159 | pVal = static_cast(std::stoull(pStr)); 160 | return; 161 | } 162 | } 163 | catch (...) { 164 | if (!mConverterParams.mHasDefaultConverter) { 165 | throw; 166 | } else { 167 | pVal = static_cast(mConverterParams.mDefaultInteger); 168 | return; 169 | } 170 | } 171 | 172 | try { 173 | if (typeid(T) == typeid(float)) { 174 | pVal = static_cast(std::stof(pStr)); 175 | return; 176 | } else if (typeid(T) == typeid(double)) { 177 | pVal = static_cast(std::stod(pStr)); 178 | return; 179 | } else if (typeid(T) == typeid(long double)) { 180 | pVal = static_cast(std::stold(pStr)); 181 | return; 182 | } 183 | } 184 | catch (...) { 185 | if (!mConverterParams.mHasDefaultConverter) { 186 | throw; 187 | } else { 188 | pVal = static_cast(mConverterParams.mDefaultFloat); 189 | return; 190 | } 191 | } 192 | 193 | if (typeid(T) == typeid(char)) { 194 | pVal = static_cast(pStr[0]); 195 | return; 196 | } else { 197 | throw no_converter(); 198 | } 199 | } 200 | 201 | private: 202 | const ConverterParams &mConverterParams; 203 | }; 204 | 205 | /** 206 | * @brief Specialized implementation handling string to string conversion. 207 | * @param pVal string 208 | * @param pStr string 209 | */ 210 | template<> 211 | inline void Converter::ToStr(const std::string &pVal, std::string &pStr) const { 212 | pStr = pVal; 213 | } 214 | 215 | /** 216 | * @brief Specialized implementation handling string to string conversion. 217 | * @param pVal string 218 | * @param pStr string 219 | */ 220 | template<> 221 | inline void Converter::ToVal(const std::string &pStr, std::string &pVal) const { 222 | pVal = pStr; 223 | } 224 | 225 | /** 226 | * @brief Datastructure holding parameters controlling which row and column should be 227 | * treated as labels. 228 | */ 229 | struct LabelParams { 230 | /** 231 | * @brief Constructor 232 | * @param pColumnNameIdx specifies the zero-based row index of the column labels, setting 233 | * it to -1 prevents column lookup by label name, and gives access 234 | * to all rows as document data. 235 | * @param pRowNameIdx specifies the zero-based column index of the row labels, setting 236 | * it to -1 prevents row lookup by label name, and gives access 237 | * to all columns as document data. 238 | */ 239 | explicit LabelParams(const int pColumnNameIdx = 0, const int pRowNameIdx = 0) 240 | : mColumnNameIdx(pColumnNameIdx), mRowNameIdx(pRowNameIdx) { 241 | } 242 | 243 | /** 244 | * @brief specifies the zero-based row index of the column labels. 245 | */ 246 | int mColumnNameIdx; 247 | 248 | /** 249 | * @brief specifies the zero-based column index of the row labels. 250 | */ 251 | int mRowNameIdx; 252 | }; 253 | 254 | /** 255 | * @brief Datastructure holding parameters controlling how the CSV data fields are separated. 256 | */ 257 | struct SeparatorParams { 258 | /** 259 | * @brief Constructor 260 | * @param pSeparator specifies the column separator (default ','). 261 | * @param pHasCR specifies whether a new document (i.e. not an existing document read) 262 | * should use CR/LF instead of only LF (default is to use standard 263 | * behavior of underlying platforms - CR/LF for Win, and LF for others). 264 | */ 265 | explicit SeparatorParams(const char pSeparator = ',', const bool pHasCR = sPlatformHasCR) 266 | : mSeparator(pSeparator), mHasCR(pHasCR) { 267 | } 268 | 269 | /** 270 | * @brief specifies the column separator. 271 | */ 272 | char mSeparator; 273 | 274 | /** 275 | * @brief specifies whether new documents should use CR/LF instead of LF. 276 | */ 277 | bool mHasCR; 278 | }; 279 | 280 | /** 281 | * @brief Class representing a CSV document. 282 | */ 283 | class Document { 284 | public: 285 | /** 286 | * @brief Constructor 287 | * @param pPath specifies the path of an existing CSV-file to populate the Document 288 | * data with. 289 | * @param pLabelParams specifies which row and column should be treated as labels. 290 | * @param pSeparatorParams specifies which field and row separators should be used. 291 | * @param pConverterParams specifies how invalid numbers (including empty strings) should be 292 | * handled. 293 | */ 294 | explicit Document(const std::string &pPath = std::string(), 295 | const LabelParams &pLabelParams = LabelParams(), 296 | const SeparatorParams &pSeparatorParams = SeparatorParams(), 297 | const ConverterParams &pConverterParams = ConverterParams()) 298 | : mPath(pPath), mLabelParams(pLabelParams), mSeparatorParams(pSeparatorParams), 299 | mConverterParams(pConverterParams) { 300 | if (!mPath.empty()) { 301 | ReadCsv(); 302 | } 303 | } 304 | 305 | /** 306 | * @brief Constructor 307 | * @param pStream specifies an input stream to read CSV data from. 308 | * @param pLabelParams specifies which row and column should be treated as labels. 309 | * @param pSeparatorParams specifies which field and row separators should be used. 310 | * @param pConverterParams specifies how invalid numbers (including empty strings) should be 311 | * handled. 312 | */ 313 | explicit Document(std::istream &pStream, 314 | const LabelParams &pLabelParams = LabelParams(), 315 | const SeparatorParams &pSeparatorParams = SeparatorParams(), 316 | const ConverterParams &pConverterParams = ConverterParams()) 317 | : mPath(), mLabelParams(pLabelParams), mSeparatorParams(pSeparatorParams), 318 | mConverterParams(pConverterParams) { 319 | ReadCsv(pStream); 320 | } 321 | 322 | 323 | /** 324 | * @brief Copy constructor 325 | * @param pDocument specifies the Document instance to copy. 326 | */ 327 | explicit Document(const Document &pDocument) 328 | : mPath(pDocument.mPath), mLabelParams(pDocument.mLabelParams), 329 | mSeparatorParams(pDocument.mSeparatorParams), mConverterParams(pDocument.mConverterParams), 330 | mData(pDocument.mData), mColumnNames(pDocument.mColumnNames), mRowNames(pDocument.mRowNames) { 331 | } 332 | 333 | /** 334 | * @brief Read Document data from file. 335 | * @param pPath specifies the path of an existing CSV-file to populate the Document 336 | * data with. 337 | */ 338 | void Load(const std::string &pPath) { 339 | mPath = pPath; 340 | ReadCsv(); 341 | } 342 | 343 | /** 344 | * @brief Write Document data to file. 345 | * @param pPath optionally specifies the path where the CSV-file will be created 346 | * (if not specified, the original path provided when creating or 347 | * loading the Document data will be used). 348 | */ 349 | void Save(const std::string &pPath = std::string()) { 350 | if (!pPath.empty()) { 351 | mPath = pPath; 352 | } 353 | WriteCsv(); 354 | } 355 | 356 | /** 357 | * @brief Write Document data to stream. 358 | * @param pStream specifies an output stream to write the data to. 359 | */ 360 | void Save(std::ostream &pStream) { 361 | WriteCsv(pStream); 362 | } 363 | 364 | /** 365 | * @brief Get column by index. 366 | * @param pColumnIdx zero-based column index. 367 | * @returns vector of column data. 368 | */ 369 | template 370 | std::vector GetColumn(const size_t pColumnIdx) const { 371 | const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 372 | std::vector column; 373 | Converter converter(mConverterParams); 374 | for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { 375 | if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) { 376 | T val; 377 | converter.ToVal(itRow->at(columnIdx), val); 378 | column.push_back(val); 379 | } 380 | } 381 | return column; 382 | } 383 | 384 | /** 385 | * @brief Get column by name. 386 | * @param pColumnName column label name. 387 | * @returns vector of column data. 388 | */ 389 | template 390 | std::vector GetColumn(const std::string &pColumnName) const { 391 | const ssize_t columnIdx = GetColumnIdx(pColumnName); 392 | if (columnIdx < 0) { 393 | throw std::out_of_range("column not found: " + pColumnName); 394 | } 395 | return GetColumn(columnIdx); 396 | } 397 | 398 | /** 399 | * @brief Set column by index. 400 | * @param pColumnIdx zero-based column index. 401 | * @param pColumn vector of column data. 402 | */ 403 | template 404 | void SetColumn(const size_t pColumnIdx, const std::vector &pColumn) { 405 | const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 406 | 407 | while (pColumn.size() + (mLabelParams.mColumnNameIdx + 1) > GetDataRowCount()) { 408 | std::vector row; 409 | row.resize(GetDataColumnCount()); 410 | mData.push_back(row); 411 | } 412 | 413 | if ((columnIdx + 1) > GetDataColumnCount()) { 414 | for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { 415 | itRow->resize(columnIdx + 1 + (mLabelParams.mRowNameIdx + 1)); 416 | } 417 | } 418 | 419 | Converter converter(mConverterParams); 420 | for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow) { 421 | std::string str; 422 | converter.ToStr(*itRow, str); 423 | mData.at(std::distance(pColumn.begin(), itRow) + (mLabelParams.mColumnNameIdx + 1)).at(columnIdx) = str; 424 | } 425 | } 426 | 427 | /** 428 | * @brief Set column by name. 429 | * @param pColumnName column label name. 430 | * @param pColumn vector of column data. 431 | */ 432 | template 433 | void SetColumn(const std::string &pColumnName, const std::vector &pColumn) { 434 | const ssize_t columnIdx = GetColumnIdx(pColumnName); 435 | if (columnIdx < 0) { 436 | throw std::out_of_range("column not found: " + pColumnName); 437 | } 438 | SetColumn(columnIdx, pColumn); 439 | } 440 | 441 | /** 442 | * @brief Remove column by index. 443 | * @param pColumnIdx zero-based column index. 444 | */ 445 | void RemoveColumn(const size_t pColumnIdx) { 446 | const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 447 | for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { 448 | itRow->erase(itRow->begin() + columnIdx); 449 | } 450 | } 451 | 452 | /** 453 | * @brief Remove column by name. 454 | * @param pColumnName column label name. 455 | */ 456 | void RemoveColumn(const std::string &pColumnName) { 457 | ssize_t columnIdx = GetColumnIdx(pColumnName); 458 | if (columnIdx < 0) { 459 | throw std::out_of_range("column not found: " + pColumnName); 460 | } 461 | 462 | RemoveColumn(columnIdx); 463 | } 464 | 465 | /** 466 | * @brief Get number of data columns. 467 | * @returns column count. 468 | */ 469 | size_t GetColumnCount() const { 470 | return (mData.size() > 0) ? (mData.at(0).size() - (mLabelParams.mRowNameIdx + 1)) : 0; 471 | } 472 | 473 | /** 474 | * @brief Get row by index. 475 | * @param pRowIdx zero-based row index. 476 | * @returns vector of row data. 477 | */ 478 | template 479 | std::vector GetRow(const size_t pRowIdx) const { 480 | const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 481 | std::vector row; 482 | Converter converter(mConverterParams); 483 | for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol) { 484 | if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) { 485 | T val; 486 | converter.ToVal(*itCol, val); 487 | row.push_back(val); 488 | } 489 | } 490 | return row; 491 | } 492 | 493 | /** 494 | * @brief Get row by name. 495 | * @param pRowName row label name. 496 | * @returns vector of row data. 497 | */ 498 | template 499 | std::vector GetRow(const std::string &pRowName) const { 500 | ssize_t rowIdx = GetRowIdx(pRowName); 501 | if (rowIdx < 0) { 502 | throw std::out_of_range("row not found: " + pRowName); 503 | } 504 | return GetRow(rowIdx); 505 | } 506 | 507 | /** 508 | * @brief Set row by index. 509 | * @param pRowIdx zero-based row index. 510 | * @param pRow vector of row data. 511 | */ 512 | template 513 | void SetRow(const size_t pRowIdx, const std::vector &pRow) { 514 | const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 515 | 516 | while ((rowIdx + 1) > GetDataRowCount()) { 517 | std::vector row; 518 | row.resize(GetDataColumnCount()); 519 | mData.push_back(row); 520 | } 521 | 522 | if (pRow.size() > GetDataColumnCount()) { 523 | for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { 524 | itRow->resize(pRow.size() + (mLabelParams.mRowNameIdx + 1)); 525 | } 526 | } 527 | 528 | Converter converter(mConverterParams); 529 | for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol) { 530 | std::string str; 531 | converter.ToStr(*itCol, str); 532 | mData.at(rowIdx).at(std::distance(pRow.begin(), itCol) + (mLabelParams.mRowNameIdx + 1)) = str; 533 | } 534 | } 535 | 536 | /** 537 | * @brief Set row by name. 538 | * @param pRowName row label name. 539 | * @param pRow vector of row data. 540 | */ 541 | template 542 | void SetRow(const std::string &pRowName, const std::vector &pRow) { 543 | ssize_t rowIdx = GetRowIdx(pRowName); 544 | if (rowIdx < 0) { 545 | throw std::out_of_range("row not found: " + pRowName); 546 | } 547 | return SetRow(rowIdx, pRow); 548 | } 549 | 550 | /** 551 | * @brief Remove row by index. 552 | * @param pRowIdx zero-based row index. 553 | */ 554 | void RemoveRow(const size_t pRowIdx) { 555 | const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 556 | mData.erase(mData.begin() + rowIdx); 557 | } 558 | 559 | /** 560 | * @brief Remove row by name. 561 | * @param pRowName row label name. 562 | */ 563 | void RemoveRow(const std::string &pRowName) { 564 | ssize_t rowIdx = GetRowIdx(pRowName); 565 | if (rowIdx < 0) { 566 | throw std::out_of_range("row not found: " + pRowName); 567 | } 568 | 569 | RemoveRow(rowIdx); 570 | } 571 | 572 | /** 573 | * @brief Get number of data rows. 574 | * @returns row count. 575 | */ 576 | size_t GetRowCount() const { 577 | return mData.size() - (mLabelParams.mColumnNameIdx + 1); 578 | } 579 | 580 | /** 581 | * @brief Get cell by index. 582 | * @param pRowIdx zero-based row index. 583 | * @param pColumnIdx zero-based column index. 584 | * @returns cell data. 585 | */ 586 | template 587 | T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const { 588 | const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 589 | const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 590 | 591 | T val; 592 | Converter converter(mConverterParams); 593 | converter.ToVal(mData.at(rowIdx).at(columnIdx), val); 594 | return val; 595 | } 596 | 597 | /** 598 | * @brief Get cell by name. 599 | * @param pColumnName column label name. 600 | * @param pRowName row label name. 601 | * @returns cell data. 602 | */ 603 | template 604 | T GetCell(const std::string &pColumnName, const std::string &pRowName) const { 605 | const ssize_t columnIdx = GetColumnIdx(pColumnName); 606 | if (columnIdx < 0) { 607 | throw std::out_of_range("column not found: " + pColumnName); 608 | } 609 | 610 | const ssize_t rowIdx = GetRowIdx(pRowName); 611 | if (rowIdx < 0) { 612 | throw std::out_of_range("row not found: " + pRowName); 613 | } 614 | 615 | return GetCell(columnIdx, rowIdx); 616 | } 617 | 618 | /** 619 | * @brief Set cell by index. 620 | * @param pRowIdx zero-based row index. 621 | * @param pColumnIdx zero-based column index. 622 | * @param pCell cell data. 623 | */ 624 | template 625 | void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T &pCell) { 626 | const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 627 | const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 628 | 629 | while ((rowIdx + 1) > GetDataRowCount()) { 630 | std::vector row; 631 | row.resize(GetDataColumnCount()); 632 | mData.push_back(row); 633 | } 634 | 635 | if ((columnIdx + 1) > GetDataColumnCount()) { 636 | for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { 637 | itRow->resize(columnIdx + 1); 638 | } 639 | } 640 | 641 | std::string str; 642 | Converter converter(mConverterParams); 643 | converter.ToStr(pCell, str); 644 | mData.at(rowIdx).at(columnIdx) = str; 645 | } 646 | 647 | /** 648 | * @brief Set cell by name. 649 | * @param pColumnName column label name. 650 | * @param pRowName row label name. 651 | * @param pCell cell data. 652 | */ 653 | template 654 | void SetCell(const std::string &pColumnName, const std::string &pRowName, const T &pCell) { 655 | const ssize_t columnIdx = GetColumnIdx(pColumnName); 656 | if (columnIdx < 0) { 657 | throw std::out_of_range("column not found: " + pColumnName); 658 | } 659 | 660 | const ssize_t rowIdx = GetRowIdx(pRowName); 661 | if (rowIdx < 0) { 662 | throw std::out_of_range("row not found: " + pRowName); 663 | } 664 | 665 | SetCell(columnIdx, rowIdx, pCell); 666 | } 667 | 668 | /** 669 | * @brief Get column name 670 | * @param pColumnIdx zero-based column index. 671 | * @returns column name. 672 | */ 673 | std::string GetColumnName(const ssize_t pColumnIdx) { 674 | const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 675 | if (mLabelParams.mColumnNameIdx < 0) { 676 | throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx)); 677 | } 678 | 679 | return mData.at(mLabelParams.mColumnNameIdx).at(columnIdx); 680 | } 681 | 682 | /** 683 | * @brief Set column name 684 | * @param pColumnIdx zero-based column index. 685 | * @param pColumnName column name. 686 | */ 687 | void SetColumnName(size_t pColumnIdx, const std::string &pColumnName) { 688 | const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); 689 | mColumnNames[pColumnName] = columnIdx; 690 | if (mLabelParams.mColumnNameIdx < 0) { 691 | throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx)); 692 | } 693 | 694 | mData.at(mLabelParams.mColumnNameIdx).at(columnIdx) = pColumnName; 695 | } 696 | 697 | /** 698 | * @brief Get column names 699 | * @returns vector of column names. 700 | */ 701 | std::vector GetColumnNames() { 702 | if (mLabelParams.mColumnNameIdx >= 0) { 703 | return std::vector(mData.at(mLabelParams.mColumnNameIdx).begin() + 704 | (mLabelParams.mRowNameIdx + 1), 705 | mData.at(mLabelParams.mColumnNameIdx).end()); 706 | } 707 | 708 | return std::vector(); 709 | } 710 | 711 | /** 712 | * @brief Get row name 713 | * @param pRowIdx zero-based column index. 714 | * @returns row name. 715 | */ 716 | std::string GetRowName(const ssize_t pRowIdx) { 717 | const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 718 | if (mLabelParams.mRowNameIdx < 0) { 719 | throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx)); 720 | } 721 | 722 | return mData.at(rowIdx).at(mLabelParams.mRowNameIdx); 723 | } 724 | 725 | /** 726 | * @brief Set row name 727 | * @param pRowIdx zero-based row index. 728 | * @param pRowName row name. 729 | */ 730 | void SetRowName(size_t pRowIdx, const std::string &pRowName) { 731 | const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); 732 | mRowNames[pRowName] = rowIdx; 733 | if (mLabelParams.mRowNameIdx < 0) { 734 | throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx)); 735 | } 736 | 737 | mData.at(rowIdx).at(mLabelParams.mRowNameIdx) = pRowName; 738 | } 739 | 740 | /** 741 | * @brief Get row names 742 | * @returns vector of row names. 743 | */ 744 | std::vector GetRowNames() { 745 | std::vector rownames; 746 | if (mLabelParams.mRowNameIdx >= 0) { 747 | for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { 748 | if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) { 749 | rownames.push_back(itRow->at(mLabelParams.mRowNameIdx)); 750 | } 751 | } 752 | } 753 | return rownames; 754 | } 755 | 756 | private: 757 | void ReadCsv() { 758 | std::ifstream stream; 759 | stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); 760 | stream.open(mPath, std::ios::binary | std::ios::ate); 761 | ReadCsv(stream); 762 | } 763 | 764 | void ReadCsv(std::istream &pStream) { 765 | pStream.seekg(0, std::ios::end); 766 | std::streamsize fileLength = pStream.tellg(); 767 | pStream.seekg(0, std::ios::beg); 768 | const std::streamsize bufLength = 64 * 1024; 769 | std::vector buffer(bufLength); 770 | std::vector row; 771 | std::string cell; 772 | bool quoted = false; 773 | int cr = 0; 774 | int lf = 0; 775 | 776 | while (fileLength > 0) { 777 | std::streamsize readLength = std::min(fileLength, bufLength); 778 | pStream.read(buffer.data(), readLength); 779 | for (int i = 0; i < readLength; ++i) { 780 | if (buffer[i] == '"') { 781 | if (cell.empty() || cell[0] == '"') { 782 | quoted = !quoted; 783 | } 784 | cell += buffer[i]; 785 | } else if (buffer[i] == mSeparatorParams.mSeparator) { 786 | if (!quoted) { 787 | row.push_back(cell); 788 | cell.clear(); 789 | } else { 790 | cell += buffer[i]; 791 | } 792 | } else if (buffer[i] == '\r') { 793 | ++cr; 794 | } else if (buffer[i] == '\n') { 795 | ++lf; 796 | row.push_back(cell); 797 | cell.clear(); 798 | mData.push_back(row); 799 | row.clear(); 800 | quoted = false; // disallow line breaks in quoted string, by auto-unquote at linebreak 801 | } else { 802 | cell += buffer[i]; 803 | } 804 | } 805 | fileLength -= readLength; 806 | } 807 | 808 | // Handle last line without linebreak 809 | if (!cell.empty() || !row.empty()) { 810 | row.push_back(cell); 811 | cell.clear(); 812 | mData.push_back(row); 813 | row.clear(); 814 | } 815 | 816 | // Assume CR/LF if at least half the linebreaks have CR 817 | mSeparatorParams.mHasCR = (cr > (lf / 2)); 818 | 819 | // Set up column labels 820 | if ((mLabelParams.mColumnNameIdx >= 0) && 821 | (mData.size() > 0)) { 822 | int i = 0; 823 | for (auto &columnName : mData[mLabelParams.mColumnNameIdx]) { 824 | mColumnNames[columnName] = i++; 825 | } 826 | } 827 | 828 | // Set up row labels 829 | if ((mLabelParams.mRowNameIdx >= 0) && 830 | (static_cast(mData.size()) > 831 | (mLabelParams.mColumnNameIdx + 1))) { 832 | int i = 0; 833 | for (auto &dataRow : mData) { 834 | mRowNames[dataRow[mLabelParams.mRowNameIdx]] = i++; 835 | } 836 | } 837 | } 838 | 839 | void WriteCsv() const { 840 | std::ofstream stream; 841 | stream.exceptions(std::ofstream::failbit | std::ofstream::badbit); 842 | stream.open(mPath, std::ios::binary | std::ios::trunc); 843 | WriteCsv(stream); 844 | } 845 | 846 | void WriteCsv(std::ostream &pStream) const { 847 | for (auto itr = mData.begin(); itr != mData.end(); ++itr) { 848 | for (auto itc = itr->begin(); itc != itr->end(); ++itc) { 849 | if ((std::string::npos == itc->find(mSeparatorParams.mSeparator)) || 850 | ((itc->length() >= 2) && ((*itc)[0] == '\"') && ((*itc)[itc->length() - 1] == '\"'))) { 851 | pStream << *itc; 852 | } else { 853 | pStream << '"' << *itc << '"'; 854 | } 855 | 856 | if (std::distance(itc, itr->end()) > 1) { 857 | pStream << mSeparatorParams.mSeparator; 858 | } 859 | } 860 | pStream << (mSeparatorParams.mHasCR ? "\r\n" : "\n"); 861 | } 862 | } 863 | 864 | ssize_t GetColumnIdx(const std::string &pColumnName) const { 865 | if (mLabelParams.mColumnNameIdx >= 0) { 866 | if (mColumnNames.find(pColumnName) != mColumnNames.end()) { 867 | return mColumnNames.at(pColumnName) - (mLabelParams.mRowNameIdx + 1); 868 | } 869 | } 870 | return -1; 871 | } 872 | 873 | ssize_t GetRowIdx(const std::string &pRowName) const { 874 | if (mLabelParams.mRowNameIdx >= 0) { 875 | if (mRowNames.find(pRowName) != mRowNames.end()) { 876 | return mRowNames.at(pRowName) - (mLabelParams.mColumnNameIdx + 1); 877 | } 878 | } 879 | return -1; 880 | } 881 | 882 | size_t GetDataRowCount() const { 883 | return mData.size(); 884 | } 885 | 886 | size_t GetDataColumnCount() const { 887 | return (mData.size() > 0) ? mData.at(0).size() : 0; 888 | } 889 | 890 | private: 891 | std::string mPath; 892 | LabelParams mLabelParams; 893 | SeparatorParams mSeparatorParams; 894 | ConverterParams mConverterParams; 895 | std::vector > mData; 896 | std::map mColumnNames; 897 | std::map mRowNames; 898 | }; 899 | } 900 | 901 | 902 | #endif //CHAPTER_12_RAPID_CSV_H 903 | -------------------------------------------------------------------------------- /chapter_13/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(chapter_13) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_executable(chapter_13 main.cpp radix_sorter.hpp) 7 | -------------------------------------------------------------------------------- /chapter_13/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "radix_sorter.hpp" 3 | 4 | 5 | int main() { // only works on equal-length strings 6 | std::vector v = {"ccc", "bbb", "aaa", "dfd"}; 7 | 8 | Sorter::radix_sort(v); 9 | 10 | std::cout << v.size() << std::endl; 11 | 12 | for (std::string word : v) 13 | { 14 | std::cout << " " << word; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /chapter_13/radix_sorter.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 12/31/18. 3 | // 4 | 5 | #ifndef CHAPTER_13_RADIX_SORTER_H 6 | #define CHAPTER_13_RADIX_SORTER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace Sorter 15 | { 16 | const std::size_t ASCII_LENGTH = 256; 17 | 18 | /* 19 | * Returns the length of the longest word in the array. 20 | */ 21 | size_t max_word_length(std::vector unsorted_list_of_words) 22 | { 23 | std::sort(unsorted_list_of_words.begin(), unsorted_list_of_words.end(), 24 | [](const std::string &s1, const std::string &s2) -> bool 25 | { 26 | return s1.size() > s2.size(); 27 | }); 28 | 29 | 30 | return unsorted_list_of_words.front().size(); 31 | } 32 | 33 | void counting_radix_sort(std::vector& unsorted_list, std::size_t string_position) 34 | { 35 | std::array counts = {0}; 36 | std::vector output; 37 | // Learn the difference between resize and reserve! 38 | output.resize(unsorted_list.size()); 39 | 40 | for (auto word : unsorted_list) 41 | { 42 | std::string letter_at_string_position = std::string(1, word.at(word.length() -1 - string_position)); 43 | // C++ has a bug that doesn't let you convert a one-length string into an integer, but since each element of a string 44 | // is a character, and character -> int static casts work, you have to manually select an index from the string 45 | int ascii_value_of_letter = static_cast(letter_at_string_position[0]); 46 | counts[ascii_value_of_letter]++; 47 | } 48 | 49 | for (int i = 1; i < ASCII_LENGTH; i++) 50 | { 51 | counts[i] += counts[i-1]; 52 | } 53 | 54 | for (int i = unsorted_list.size() - 1; i >= 0; i--) 55 | { 56 | std::string current_element = unsorted_list[i]; 57 | std::string letter_at_string_position = std::string(1, current_element.at(current_element.length() - 1 - string_position)); 58 | int ascii_value_of_letter = static_cast(letter_at_string_position[0]); 59 | 60 | size_t second_index = counts[ascii_value_of_letter] - 1; 61 | output.at(second_index) = current_element; 62 | counts[ascii_value_of_letter]--; 63 | } 64 | 65 | 66 | for (std::size_t i = 0; i < unsorted_list.size(); i++) 67 | { 68 | unsorted_list[i] = output[i]; 69 | } 70 | 71 | } 72 | 73 | 74 | void radix_sort(std::vector& initial_array_to_sort) 75 | { 76 | std::size_t longest_word_length = max_word_length(initial_array_to_sort); 77 | 78 | for (int position = 0; position < longest_word_length; position++) 79 | { 80 | counting_radix_sort(initial_array_to_sort, position); 81 | } 82 | 83 | } 84 | } 85 | 86 | #endif //CHAPTER_13_RADIX_SORTER_H 87 | -------------------------------------------------------------------------------- /chapter_14/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(chapter_14) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wpedantic -Werror -g -O0 -std=c++2a -ftime-report") 7 | 8 | add_executable(chapter_14 main.cpp Person.hpp date.h Male.hpp Female.hpp MembershipRoster.hpp) 9 | -------------------------------------------------------------------------------- /chapter_14/Female.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 6/7/19. 3 | // 4 | 5 | #ifndef CHAPTER_14_FEMALE_H 6 | 7 | #include "Person.hpp" 8 | 9 | class Female : public Person { 10 | 11 | public: 12 | 13 | Female(const std::string &first_name, const std::string &last_name, date::year_month_day birthday, 14 | unsigned int height, double weight) 15 | : Person(first_name, last_name, birthday) { 16 | set_gender('F'); 17 | set_height(height); 18 | set_weight(weight); 19 | } 20 | 21 | double compute_BMR() override { 22 | 23 | return 655 + (21.12 * weight_in_kgs) + (0.71 * height_in_inches) - (4.7 * get_age()); 24 | 25 | } 26 | 27 | void set_height(double height) override { 28 | height_in_inches = height; 29 | } 30 | 31 | double get_height() override { 32 | return height_in_inches; 33 | } 34 | 35 | private: 36 | 37 | double weight_in_kgs = weight_in_pounds / 2.2046; 38 | 39 | }; 40 | 41 | #define CHAPTER_14_FEMALE_H 42 | 43 | #endif //CHAPTER_14_FEMALE_H 44 | -------------------------------------------------------------------------------- /chapter_14/Male.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 6/7/19. 3 | // 4 | 5 | #ifndef CHAPTER_14_MALE_H 6 | 7 | #include "Person.hpp" 8 | 9 | class Male : public Person { 10 | 11 | public: 12 | 13 | Male(const std::string &first_name, const std::string &last_name, date::year_month_day birthday, 14 | unsigned int height, double weight) 15 | : Person(first_name, last_name, birthday) { 16 | set_gender('M'); 17 | set_height(height); 18 | set_weight(weight); 19 | } 20 | 21 | double compute_BMR() override { 22 | return 66 + (30.14 * weight_in_pounds) + (1.97 * height_in_inches) - (6.8 * get_age()); 23 | } 24 | 25 | void set_height(double height) override { 26 | height_in_inches = height; 27 | } 28 | 29 | double get_height() override { 30 | return height_in_inches; 31 | } 32 | 33 | }; 34 | 35 | #define CHAPTER_14_MALE_H 36 | 37 | #endif //CHAPTER_14_MALE_H 38 | -------------------------------------------------------------------------------- /chapter_14/MembershipRoster.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 6/8/19. 3 | // 4 | 5 | #ifndef CHAPTER_14_MEMBERSHIPROSTER_H 6 | 7 | #include 8 | #include 9 | #include "Person.hpp" 10 | 11 | class MembershipRoster { 12 | public: 13 | 14 | explicit MembershipRoster(const std::vector &members) 15 | : members(members) { 16 | 17 | } 18 | 19 | double compute_average_weight(Gender gender) { 20 | return std::accumulate(members.begin(), members.end(), 0.0, [&](double current_sum, Person &p) { 21 | return current_sum + ((p.get_gender() == gender) ? p.get_weight() : 0.0); 22 | }) 23 | / count_members_with_gender(gender); 24 | 25 | } 26 | 27 | long compute_average_age(Gender gender) { 28 | return std::accumulate(members.begin(), members.end(), 0, [&](double current_sum, Person &p) { 29 | return current_sum + ((p.get_gender() == gender) ? p.get_age() : 0); 30 | }) / count_members_with_gender(gender); 31 | 32 | } 33 | 34 | 35 | double compute_average_activity_level(Gender gender) { 36 | return std::accumulate(members.begin(), members.end(), 0.0, [&](double current_sum, Person &p) { 37 | return current_sum + ((p.get_gender() == gender) ? p.get_activity_level() : 0.0); 38 | }) / count_members_with_gender(gender); 39 | } 40 | 41 | protected: 42 | 43 | std::vector members; 44 | 45 | 46 | private: 47 | 48 | long count_members_with_gender(Gender gender) { 49 | return std::count_if(members.begin(), members.end(), [&](Person &p) { return p.get_gender() == gender; }); 50 | } 51 | 52 | 53 | }; 54 | 55 | #define CHAPTER_14_MEMBERSHIPROSTER_H 56 | 57 | #endif //CHAPTER_14_MEMBERSHIPROSTER_H 58 | -------------------------------------------------------------------------------- /chapter_14/Person.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 6/7/19. 3 | // 4 | 5 | #ifndef CHAPTER_14_PERSON_H 6 | 7 | #include "date.h" 8 | 9 | enum class Gender { 10 | MALE, 11 | FEMALE, 12 | OTHER 13 | }; 14 | 15 | enum class ActivityLevel { 16 | SEDENTARY, 17 | LIGHTLY_ACTIVE, 18 | MODERATELY_ACTIVE, 19 | HIGHLY_ACTIVE 20 | }; 21 | 22 | namespace ActivityLevels { 23 | double sedentary = 1.2; 24 | double lightly_active = 1.35; 25 | double moderately_active = 1.55; 26 | double highly_active = 1.725; 27 | } 28 | 29 | class Person { 30 | 31 | public: 32 | 33 | Person(const std::string &first_name, const std::string &last_name, date::year_month_day birthday) 34 | : first_name(first_name), last_name(last_name), birthday(birthday) { 35 | 36 | } 37 | 38 | /** 39 | * Computes the Basal Metabolic Rate for a person 40 | * 41 | */ 42 | virtual double compute_BMR() { 43 | return 0.0; 44 | } 45 | 46 | virtual void set_height(double height) { 47 | height_in_inches = height; 48 | } 49 | 50 | virtual double get_height() { 51 | return height_in_inches; 52 | } 53 | 54 | unsigned int get_age() { 55 | auto now = std::chrono::system_clock::now(); 56 | 57 | auto today = date::year_month_day{date::floor(now)}; 58 | 59 | unsigned int age = (today.year() - birthday.year()).count(); 60 | 61 | assert(age > 0); 62 | 63 | return age; 64 | } 65 | 66 | void set_weight(double weight) { 67 | this->weight_in_pounds = weight; 68 | } 69 | 70 | void set_activity_level(ActivityLevel activityLevel) { 71 | switch (activityLevel) { 72 | case (ActivityLevel::SEDENTARY): { 73 | activity_level = ActivityLevels::sedentary; 74 | } 75 | 76 | case (ActivityLevel::LIGHTLY_ACTIVE): { 77 | activity_level = ActivityLevels::lightly_active; 78 | } 79 | 80 | case (ActivityLevel::MODERATELY_ACTIVE): { 81 | activity_level = ActivityLevels::moderately_active; 82 | } 83 | 84 | case (ActivityLevel::HIGHLY_ACTIVE): { 85 | activity_level = ActivityLevels::highly_active; 86 | } 87 | } 88 | } 89 | 90 | double get_activity_level() { 91 | return activity_level; 92 | } 93 | 94 | double get_weight() { 95 | return weight_in_pounds; 96 | } 97 | 98 | Gender get_gender() { 99 | return gender; 100 | } 101 | 102 | void set_gender(char gender_letter) { 103 | if (toupper(gender_letter) == 'M') { 104 | this->gender = Gender::MALE; 105 | } else if (toupper(gender_letter) == 'L') { 106 | this->gender = Gender::FEMALE; 107 | } else { 108 | this->gender = Gender::OTHER; 109 | } 110 | 111 | } 112 | 113 | double compute_daily_caloric_intake() { 114 | return activity_level * compute_BMR(); 115 | } 116 | 117 | friend std::ostream &operator<<(std::ostream &out, const Person &person) { 118 | out << person.last_name << ", " << person.first_name << std::endl; 119 | out << "DOB: " << person.birthday.year() << "/" << person.birthday.month() << "/" << person.birthday.day() 120 | << std::endl; 121 | out << "Ht: " << person.height_in_inches << "in.; Weight " << person.weight_in_pounds << " lbs." << std::endl; 122 | return out; 123 | } 124 | 125 | protected: 126 | 127 | std::string first_name; 128 | std::string last_name; 129 | 130 | double height_in_inches = 0.0; 131 | 132 | date::year_month_day birthday; 133 | 134 | double weight_in_pounds = 0.0; 135 | 136 | double activity_level = 0.0; 137 | 138 | 139 | Gender gender = Gender::OTHER; 140 | 141 | 142 | }; 143 | 144 | #define CHAPTER_14_PERSON_H 145 | 146 | #endif //CHAPTER_14_PERSON_H 147 | -------------------------------------------------------------------------------- /chapter_14/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Person.hpp" 2 | #include "Female.hpp" 3 | #include "Male.hpp" 4 | #include "MembershipRoster.hpp" 5 | 6 | #include 7 | 8 | int main() { 9 | Female first("Jane", "Roe", date::year(2000) / date::month(1) / date::day(1), 72, 150); 10 | 11 | Male second("John", "Doe", date::year(1999) / date::month(1) / date::day(1), 80, 200); 12 | 13 | const std::vector v = {first, second}; 14 | 15 | MembershipRoster m(v); 16 | 17 | assert(m.compute_average_weight(Gender::MALE) == second.get_weight()); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /chapter_15/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(chapter_15) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | set(CMAKE_CXX_FLAGS "-g -O0 -Wall -Wextra --std=c++2a") 8 | 9 | add_executable(chapter_15 main.cpp Graph.h) 10 | -------------------------------------------------------------------------------- /chapter_15/Graph.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Graph.h 3 | * @author Raleigh Littles 4 | */ 5 | 6 | 7 | #ifndef CHAPTER_15_GRAPH_H 8 | 9 | #define CHAPTER_15_GRAPH_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | class Vertex 22 | { 23 | public: 24 | 25 | explicit Vertex(const std::size_t& id, const std::string& label = "") 26 | : id(id) 27 | , label(label) 28 | { 29 | 30 | } 31 | 32 | /* The std::vector's subscript operator takes in an std::size_t, and you will be using the subscript operator with 33 | * this id later on. To avoid a warning about the implicit cast from int to size_t, just use a size_t type here. */ 34 | std::size_t id; 35 | std::string label; 36 | 37 | bool operator==(const Vertex& v) 38 | { 39 | return (v.id == this->id); 40 | } 41 | 42 | friend bool operator<(const Vertex& v1, const Vertex& v2) 43 | { 44 | return (v1.id < v2.id); 45 | } 46 | }; 47 | 48 | struct Edge 49 | { 50 | int weight; 51 | Vertex source; 52 | Vertex destination; 53 | }; 54 | 55 | /** 56 | * @brief Implementation of a weighted, undirected graph 57 | */ 58 | class Graph 59 | { 60 | public: 61 | 62 | explicit Graph(const std::vector& edges) 63 | { 64 | 65 | for (const Edge& edge : edges ) 66 | { 67 | /* For an undirected graph, you can think of an edge between two vertices (say, v1 and v2) as really being two 68 | * separate edges: one that is going from v1 to v2, and another one going from v2 to v1. */ 69 | 70 | adjacency_list[edge.source.id].push_back(std::make_pair(edge.destination, edge.weight)); 71 | 72 | adjacency_list[edge.destination.id].push_back(std::make_pair(edge.source, edge.weight)); 73 | 74 | vertex_set.insert(edge.source); 75 | vertex_set.insert(edge.destination); 76 | } 77 | 78 | } 79 | 80 | // TODO : add an operator overload for the ostream operator instead of this function 81 | void printGraph() 82 | { 83 | for (auto const& element: adjacency_list) 84 | { 85 | // Check if a vertex here exists 86 | if (!element.second.empty()) 87 | { 88 | for (const std::pair& edge_pair : element.second) 89 | { 90 | const auto& [vertex, weight] = edge_pair; 91 | 92 | std::cout << element.first << " <- [ " << weight << " ]" << " -> " << vertex.id << std::endl; 93 | 94 | } 95 | 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * @brief Implements Prim's algorithm to compute the MST. 102 | * @details Psuedo-ode: 103 | * 104 | * Pick any vertex in the graph, at random, this will be a starting vertex s. 105 | * Create a set of visited vertices, called V, and create a set of vertices remaining, called Q. 106 | * Initially, V is empty, and Q contains all vertices in the graph. 107 | * 108 | * From s, travel along the edge with the lowest cost. Once you reach the first vertex on that edge (say s1), add it, and the starting vertex to V, 109 | * and remove them from Q. 110 | * 111 | * Now V contains two vertices, s and s1. Repeat the process until Q is empty. 112 | */ 113 | std::vector get_minimum_spanning_tree() 114 | { 115 | 116 | std::set V; 117 | std::vector MST_edge_list; 118 | std::set Q = this->vertex_set; 119 | 120 | 121 | 122 | while (!Q.empty()) 123 | { 124 | // Pick the first vertex in Q 125 | Vertex current_vertex = *Q.begin(); 126 | 127 | // Prepare a list of vertices connected to your current vertex 128 | std::vector> adjacent_vertices; 129 | 130 | // To find the list of vertices connected to your current vertex, look them up in the map. 131 | auto it = adjacency_list.find(current_vertex.id); 132 | 133 | // the second entry in the map is the vector representing all of the adjacent vertices to the one you just 134 | // looked up. 135 | adjacent_vertices = it->second; 136 | 137 | // now, find the lowest weight edge connecting your current vertex to another vertex that is NOT in V 138 | int weight_of_lowest_edge = std::numeric_limits::max(); 139 | 140 | size_t idOfBestVertex = 0; 141 | 142 | for (const auto& [vertex, weight_of_edge_to_vertex] : adjacent_vertices) 143 | { 144 | if (weight_of_edge_to_vertex < weight_of_lowest_edge) 145 | { 146 | weight_of_lowest_edge = weight_of_edge_to_vertex; 147 | idOfBestVertex = vertex.id; 148 | 149 | } 150 | } 151 | 152 | // Now, you've found the best vertex (one on the other end of the lowest weight edge) 153 | Vertex bestVertex = Vertex(idOfBestVertex); 154 | 155 | 156 | // Add this best vertex to your set V 157 | V.insert(bestVertex); 158 | 159 | // Remove the current vertex from Q, so that the next time you start this loop you will be on a different vertex 160 | Q.erase(current_vertex); 161 | 162 | // Add this edge to your edge list to be used for the MST 163 | Edge new_edge = {.weight = weight_of_lowest_edge, .source = current_vertex, .destination = bestVertex}; 164 | 165 | MST_edge_list.push_back(new_edge); 166 | 167 | } 168 | 169 | // By now, you should have added every vertex in your graph to your MST 170 | // You have a list of edges that comprises the MST, now construct a graph from these edges 171 | 172 | return MST_edge_list; 173 | } 174 | 175 | 176 | protected: 177 | // std::vector>> adjacency_list; 178 | std::map>> adjacency_list; 179 | std::set vertex_set; 180 | }; 181 | 182 | 183 | #endif //CHAPTER_15_GRAPH_H 184 | -------------------------------------------------------------------------------- /chapter_15/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Graph.h" 4 | 5 | int main() { 6 | 7 | 8 | 9 | std::vector edgesBeforeMst = 10 | { 11 | {4, Vertex(0), Vertex(1)}, 12 | {8, Vertex(0), Vertex(7)}, 13 | {8, Vertex(1), Vertex(2)}, 14 | {11, Vertex(1), Vertex(7)}, 15 | {7, Vertex(7), Vertex(8)}, 16 | {1, Vertex(7), Vertex(6)}, 17 | {2, Vertex(2), Vertex(8)}, 18 | {6, Vertex(8), Vertex(6)}, 19 | {2, Vertex(6), Vertex(5)}, 20 | {7, Vertex(2), Vertex(3)}, 21 | {4, Vertex(2), Vertex(5)}, 22 | {14, Vertex(3), Vertex(5)}, 23 | {10, Vertex(5), Vertex(4)}, 24 | {9, Vertex(3), Vertex(4)} 25 | }; 26 | 27 | Graph beforeMst(edgesBeforeMst); 28 | 29 | Graph mst(beforeMst.get_minimum_spanning_tree()); 30 | 31 | mst.printGraph(); 32 | 33 | return 0; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /chapter_2/lunar_lander.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 8/17/18 at 10:50 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "lunar_lander.h" 7 | #include 8 | 9 | void LunarLander::recalculate_values(float time_seconds) 10 | { 11 | if ((this->fuel_flow_rate > 0) && (this->current_fuel_mass == 0)) 12 | { 13 | // gas tank empty, no fuel left to even flow 14 | this->fuel_flow_rate = 0; 15 | } 16 | 17 | // recompute vertical velocity 18 | float vertical_speed_delta = compute_velocity_change(time_seconds); 19 | 20 | float new_vertical_speed = this->vertical_speed + vertical_speed_delta; 21 | this->vertical_speed = new_vertical_speed; 22 | 23 | // recompute altitude 24 | this->altitude = (time_seconds * this->vertical_speed); 25 | 26 | // recompute fuel available 27 | this->current_fuel_mass = time_seconds * this->fuel_flow_rate * this->max_fuel_consumption_rate; 28 | 29 | // check if lander has landed 30 | if (this->altitude <= 0) 31 | { 32 | std::cout << "Eagle has landed!" << std::endl; 33 | this->altitude = 0; 34 | this->vertical_speed = 0; 35 | } 36 | 37 | // check if lander ran out of fuel 38 | if (this->current_fuel_mass <= 0) 39 | { 40 | std::cout << "Oh no, ran out of fuel!" << std::endl; 41 | this->current_fuel_mass = 0; 42 | } 43 | 44 | } 45 | 46 | float LunarLander::compute_velocity_change(float time_seconds) 47 | { 48 | float velocity_change = (time_seconds) * ((this->max_engine_thrust / (this->lander_mass + this->current_fuel_mass)) - this->MOON_GRAVITATIONAL_CONSTANT); 49 | return velocity_change; 50 | } -------------------------------------------------------------------------------- /chapter_2/lunar_lander.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 8/17/18 at 7:06 PM. 3 | // Copyright (c) 2018 Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #ifndef CHAPTER_2_LUNAR_LANDER_H 7 | #define CHAPTER_2_LUNAR_LANDER_H 8 | 9 | class LunarLander { 10 | public: 11 | LunarLander() : fuel_flow_rate(0.0), 12 | vertical_speed(0.0), 13 | altitude(1000), 14 | current_fuel_mass(1700), 15 | lander_mass(900), 16 | max_fuel_consumption_rate(10), 17 | max_engine_thrust(5000) 18 | { 19 | 20 | } 21 | 22 | 23 | float get_fuel_flow_rate() const {return this->fuel_flow_rate;} 24 | 25 | float get_vertical_speed() const { return this->vertical_speed; } 26 | 27 | float get_altitude() const { return this->altitude; } 28 | 29 | float get_current_fuel_mass() const { return this->current_fuel_mass; } 30 | 31 | float get_lander_mass() const { return this->lander_mass; } 32 | 33 | float get_max_fuel_consumption_rate() const { return this->max_fuel_consumption_rate; } 34 | 35 | float get_max_engine_thrust() const { return this->max_engine_thrust; } 36 | 37 | void set_fuel_flow_rate(float desired_fuel_flow_rate) { this->fuel_flow_rate = desired_fuel_flow_rate; } 38 | 39 | void recalculate_values(float time_seconds); 40 | 41 | private: 42 | static constexpr float MOON_GRAVITATIONAL_CONSTANT = 1.625; 43 | 44 | float compute_velocity_change(float time_seconds); 45 | 46 | float fuel_flow_rate; // (none), fraction 47 | float vertical_speed; // m/s 48 | float altitude; // meters 49 | float current_fuel_mass; // kg 50 | float lander_mass; // kg 51 | float max_fuel_consumption_rate; // kg/sec 52 | float max_engine_thrust; // Newtons 53 | 54 | }; 55 | 56 | #endif //CHAPTER_2_LUNAR_LANDER_H 57 | -------------------------------------------------------------------------------- /chapter_2/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 8/26/18 at 9:21 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include 7 | #include "statistician.h" 8 | 9 | 10 | int main() { 11 | 12 | statistician ss; 13 | ss.next_number(1); 14 | ss.next_number(2); 15 | ss.next_number(3); 16 | 17 | std::cout << "Length: " << ss.get_length() << std::endl; 18 | std::cout << "Mean: " << ss.get_mean() << std::endl; 19 | std::cout << "Sum: " << ss.get_sum() << std::endl; 20 | std::cout << "Min/Max" << ss.get_minimum() << "/" << ss.get_maximum() << std::endl; 21 | 22 | } -------------------------------------------------------------------------------- /chapter_2/statistician.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 8/18/18 at 11:16 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "statistician.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | void statistician::next_number(float new_number) { 14 | 15 | (this->length)++; 16 | 17 | this->sum += new_number; 18 | 19 | 20 | if (this->length > 1) 21 | { 22 | if (this->maximum < new_number) { 23 | this->maximum = new_number; 24 | } 25 | 26 | if (this->minimum > new_number) { 27 | this->minimum = new_number; 28 | } 29 | 30 | } 31 | 32 | else 33 | { 34 | this->maximum = new_number; 35 | this->minimum = new_number; 36 | } 37 | 38 | this->mean = sum / length; 39 | 40 | } 41 | 42 | float statistician::get_mean() const 43 | { 44 | 45 | statistician::assert_not_empty(); 46 | 47 | return this->mean; 48 | } 49 | 50 | float statistician::get_maximum() const 51 | { 52 | 53 | statistician::assert_not_empty(); 54 | 55 | return this->maximum; 56 | } 57 | 58 | float statistician::get_minimum() const { 59 | 60 | statistician::assert_not_empty(); 61 | 62 | return this->minimum; 63 | } 64 | 65 | void statistician::erase_sequence() { 66 | 67 | std::cout << "Erasing sequence." << std::endl; 68 | 69 | this->length = 0; 70 | this->sum = 0; 71 | } 72 | 73 | void statistician::assert_not_empty() const { 74 | 75 | assert(this->length != 0); 76 | } 77 | 78 | statistician operator+(statistician const &s1, statistician const &s2) { 79 | 80 | statistician s3; 81 | 82 | s3.minimum = std::min(s1.minimum, s2.minimum); 83 | s3.maximum = std::max(s1.maximum, s2.maximum); 84 | 85 | s3.length = s1.length + s2.length; 86 | s3.sum = s1.sum + s2.sum; 87 | s3.mean = (s1.mean + s2.mean) / (s3.length); 88 | 89 | return s3; 90 | 91 | } 92 | -------------------------------------------------------------------------------- /chapter_2/statistician.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 8/18/18 at 6:34 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include 7 | 8 | #ifndef CHAPTER_2_STATISTICIAN_H 9 | #define CHAPTER_2_STATISTICIAN_H 10 | 11 | class statistician { 12 | 13 | friend statistician operator+ (statistician const &s1, statistician const &s2); 14 | 15 | public: 16 | 17 | statistician() : length(0), sum(0) 18 | { 19 | 20 | } 21 | 22 | void next_number(float new_number); 23 | 24 | int get_length() const { return length; } 25 | 26 | float get_sum() const { return sum; } 27 | 28 | float get_mean() const; 29 | 30 | float get_minimum() const; 31 | 32 | float get_maximum() const; 33 | 34 | void erase_sequence(); 35 | 36 | void assert_not_empty() const; 37 | 38 | private: 39 | 40 | int length; 41 | float sum; 42 | float mean; 43 | float minimum; 44 | float maximum; 45 | 46 | }; 47 | 48 | 49 | #endif //CHAPTER_2_STATISTICIAN_H 50 | -------------------------------------------------------------------------------- /chapter_3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | set (CMAKE_CXX_STANDARD 17) 3 | project (Towers) 4 | add_executable(Towers main.cpp peg.h towers.h towers.cpp peg.cpp) -------------------------------------------------------------------------------- /chapter_3/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/28/18 at 11:58 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "towers.h" 7 | 8 | 9 | int main() 10 | { 11 | std::size_t initial_pegs_number = 3; 12 | std::size_t initial_rings_number = 3; 13 | 14 | towers vietnam = towers(3, 3); 15 | 16 | std::cout << "At game start..." << std::endl; 17 | 18 | for (std::size_t i = 0; i < initial_pegs_number; i++) 19 | { 20 | std::cout << "Peg #" << i << " has " << vietnam.many_rings(i) << " rings on it." << std::endl; 21 | } 22 | 23 | vietnam.move(0, 1); 24 | 25 | 26 | } -------------------------------------------------------------------------------- /chapter_3/peg.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/29/18 at 12:17 AM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "peg.h" 7 | 8 | 9 | Peg::Peg(std::size_t rings) 10 | { 11 | for (std::size_t ring = 0; ring < rings; ring++) 12 | { 13 | ring_diameters.push_back(ring+1); 14 | } 15 | 16 | std::cout << "Peg initialized with " << ring_diameters.size() << " rings on it. " << std::endl; 17 | } 18 | 19 | std::size_t Peg::num_rings() const 20 | { 21 | return ring_diameters.size(); 22 | } 23 | 24 | std::size_t Peg::diameter_of_top_ring() const 25 | { 26 | if (ring_diameters.size() > 0) 27 | { 28 | return ring_diameters.back(); 29 | } 30 | 31 | else if (ring_diameters.size() == 0) 32 | { 33 | return 0; 34 | } 35 | } 36 | 37 | void Peg::push_ring(std::size_t ring_diameter) 38 | { 39 | std::cout << "Adding ring to peg." << std::endl; 40 | ring_diameters.push_back(ring_diameter); 41 | std::cout << "The current peg now has size: " << ring_diameters.size() << std::endl; 42 | } 43 | 44 | void Peg::pop_ring() 45 | { 46 | std::cout << "Deleting ring from top of peg." << std::endl; 47 | ring_diameters.pop_back(); 48 | std::cout << "Peg size is now: " << ring_diameters.size() << std::endl; 49 | } -------------------------------------------------------------------------------- /chapter_3/peg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/22/18 at 12:28 AM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | /* This class was defined in Problem 12 of Chapter 13, and is used as a helper class for Problem 13 (Towers). 7 | * 8 | * 9 | */ 10 | 11 | 12 | 13 | #ifndef CHAPTER_3_PEG_H 14 | #define CHAPTER_3_PEG_H 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | class Peg { 21 | 22 | public: 23 | // (a) 24 | Peg(std::size_t rings = 64); 25 | 26 | // (b) 27 | std::size_t num_rings() const; 28 | 29 | // (c) 30 | std::size_t diameter_of_top_ring() const; 31 | 32 | // (d) 33 | void push_ring(std::size_t ring_diameter); 34 | 35 | // (e) 36 | void pop_ring(); 37 | 38 | // (f) 39 | // an overloaded output function that prints some clever representatino of the peg and its rings 40 | 41 | 42 | private: 43 | std::vector ring_diameters; 44 | 45 | 46 | 47 | }; 48 | 49 | 50 | #endif //CHAPTER_3_PEG_HPP 51 | -------------------------------------------------------------------------------- /chapter_3/towers.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/9/18 at 4:44 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "towers.h" 7 | 8 | 9 | 10 | // Precondition: 1 <= n <= 64. 11 | // Postcondition: The towers have been initialized with n rings on the first peg and no rings on the other two pegs. 12 | // The diameters of the first peg's rings are from one inch (on the top) to n inches (on the bottom) 13 | towers::towers(std::size_t n, std::size_t num_pegs) 14 | { 15 | std::cout << "Initializing a tower with " << n << " rings on " << num_pegs << " pegs." << std::endl; 16 | 17 | Peg first_peg = Peg(n); 18 | pegs.push_back(first_peg); 19 | 20 | // first peg will always have n rings on it, so start from second peg 21 | for (int peg_num = 1; peg_num < num_pegs; ++peg_num) 22 | { 23 | Peg empty_peg = Peg(0); 24 | pegs.push_back(empty_peg); 25 | } 26 | } 27 | 28 | // Precondition: peg_number is 1, 2, or 3. 29 | // Postcondition: The return value is the number of rings on the specified peg. 30 | std::size_t towers::many_rings(int peg_number) const 31 | { 32 | Peg desired_peg = pegs[peg_number]; 33 | return desired_peg.num_rings(); 34 | 35 | } 36 | 37 | // Precondition: peg_number is 1, 2, or 3. 38 | // Postcondition: If many_rings(peg_number_ > 0, then the return value is the diameter of the top ring on the specified 39 | // peg; otherwise the return value is 0. 40 | std::size_t towers::top_diameter(int peg_number) const 41 | { 42 | Peg desired_peg = pegs[peg_number]; 43 | return desired_peg.diameter_of_top_ring(); 44 | 45 | } 46 | 47 | // Precondition: start_peg is a peg number (1, 2, or 3), and many_rings(start_peg) > 0; end_peg is a different 48 | // peg_number (not equal to start_peg), and top_diameter(end_peg) is eitehr 0 or more than top_diameter(start_peg). 49 | // Postcondition: The top ring has been moved from start_peg to end_peg. 50 | void towers::move(int start_peg, int end_peg) 51 | { 52 | 53 | std::cout << "Moving peg from peg #" << start_peg << " to peg #" << end_peg << std::endl; 54 | 55 | Peg starting_peg = pegs[start_peg]; 56 | 57 | Peg ending_peg = pegs[end_peg]; 58 | 59 | std::cout << "Before moving.. Start peg size: " << starting_peg.num_rings() << 60 | " End peg size: " << ending_peg.num_rings() << std::endl; 61 | 62 | std::size_t desired_ring_diameter = starting_peg.diameter_of_top_ring(); 63 | 64 | std::cout << "The ring at the top of the starting peg has size: " << desired_ring_diameter << std::endl; 65 | 66 | std::size_t destination_peg_top_ring_diameter = ending_peg.diameter_of_top_ring(); 67 | 68 | std::cout << "The ring on the top of the ending peg has size: " << destination_peg_top_ring_diameter << std::endl; 69 | 70 | if (destination_peg_top_ring_diameter != 0 ) 71 | { 72 | assert(destination_peg_top_ring_diameter > desired_ring_diameter); 73 | } 74 | 75 | starting_peg.pop_ring(); 76 | 77 | // add it to top of end_peg 78 | 79 | ending_peg.push_ring(desired_ring_diameter); 80 | 81 | std::cout << "The ring at the top of the ending peg has size: " << ending_peg.diameter_of_top_ring() << std::endl; 82 | 83 | std::cout << "After moving. Start peg size: " << starting_peg.num_rings() << 84 | " End peg size: " << ending_peg.num_rings() << std::endl; 85 | 86 | } 87 | -------------------------------------------------------------------------------- /chapter_3/towers.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/9/18 at 4:44 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | 7 | #include "peg.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef CHAPTER_3_TOWER_H 16 | #define CHAPTER_3_TOWER_H 17 | 18 | 19 | class towers { 20 | 21 | public: 22 | towers(std::size_t n = 64, std::size_t num_pegs = 3); 23 | 24 | std::size_t many_rings(int peg_number) const; 25 | 26 | std::size_t top_diameter(int peg_number) const; 27 | 28 | void move(int start_peg, int end_peg); 29 | 30 | std::vector pegs; 31 | 32 | }; 33 | 34 | #endif //CHAPTER_3_TOWER_H 35 | cd -------------------------------------------------------------------------------- /chapter_4/ChoreManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/29/18 at 11:22 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "ChoreManager.h" 7 | 8 | ChoreManager::ChoreManager(std::string chores_filename) { 9 | 10 | this->chores_filename = chores_filename; 11 | 12 | std::ifstream chore_file_object; 13 | chore_file_object.open(chores_filename); 14 | 15 | if (!chore_file_object) 16 | { 17 | std::cout << "Unable to open file." << std::endl; 18 | exit(1); 19 | } 20 | 21 | std::string current_chore; 22 | while (std::getline(chore_file_object, current_chore)) 23 | { 24 | chores.push_back(current_chore); 25 | } 26 | 27 | chore_file_object.close(); 28 | 29 | std::cout << "Finished adding " << chores.size() << " chores, read from file." << std::endl; 30 | } 31 | 32 | void ChoreManager::add_chore(std::string new_chore) { 33 | 34 | chores.push_back(new_chore); 35 | } 36 | 37 | std::size_t ChoreManager::count_chores() { 38 | 39 | return chores.size(); 40 | } 41 | 42 | void ChoreManager::print_chores() { 43 | 44 | std::sort(chores.begin(), chores.end()); 45 | 46 | for (auto it = chores.begin(); it != chores.end(); ++it) 47 | { 48 | std::cout << *it << std::endl; 49 | } 50 | 51 | } 52 | 53 | void ChoreManager::delete_chore(std::string chore_to_delete) { 54 | 55 | auto const chore_iterator = std::find(chores.begin(), chores.end(), chore_to_delete); 56 | 57 | assert(chore_iterator != chores.end()); 58 | 59 | //auto index = std::distance(chores.begin(), chore_iterator); 60 | 61 | chores.erase(chore_iterator); 62 | } 63 | 64 | void ChoreManager::exit_program() { 65 | 66 | std::ofstream chore_file_object; 67 | chore_file_object.open(chores_filename, std::ofstream::out | std::ofstream::trunc); 68 | 69 | for (auto it = chores.begin(); it != chores.end(); ++it) 70 | { 71 | chore_file_object << *it << std::endl; 72 | } 73 | 74 | chore_file_object.close(); 75 | 76 | } 77 | -------------------------------------------------------------------------------- /chapter_4/ChoreManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 9/29/18 at 11:18 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifndef CHAPTER_4_CHORE_H 14 | #define CHAPTER_4_CHORE_H 15 | 16 | class ChoreManager { 17 | public: 18 | ChoreManager(std::string chores_filename); 19 | 20 | void add_chore(std::string new_chore); 21 | 22 | std::size_t count_chores(); 23 | 24 | void print_chores(); 25 | 26 | void delete_chore(std::string chore_to_delete); 27 | 28 | void exit_program(); 29 | 30 | private: 31 | std::vector chores; 32 | std::string chores_filename; 33 | }; 34 | 35 | #endif //CHAPTER_4_CHORE_H -------------------------------------------------------------------------------- /chapter_4/chores.txt: -------------------------------------------------------------------------------- 1 | sleep 2 | -------------------------------------------------------------------------------- /chapter_4/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 10/6/18 at 10:41 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "ChoreManager.h" 7 | 8 | int main() 9 | { 10 | ChoreManager my_chores = ChoreManager("chores.txt"); 11 | 12 | std::size_t chore_count = my_chores.count_chores(); 13 | 14 | std::cout << "chore count: " << chore_count << std::endl; 15 | 16 | std::cout<< "chore add..." << std::endl; 17 | 18 | my_chores.add_chore("go to the gym"); 19 | chore_count = my_chores.count_chores(); 20 | 21 | std::cout << "chore count: " << chore_count << std::endl; 22 | 23 | my_chores.delete_chore("go to the gym"); 24 | 25 | std::cout << "chore count: " << my_chores.count_chores() << std::endl; 26 | 27 | my_chores.delete_chore("eat pizza"); 28 | 29 | my_chores.delete_chore("code"); 30 | 31 | my_chores.print_chores(); 32 | 33 | my_chores.exit_program(); 34 | 35 | } -------------------------------------------------------------------------------- /chapter_5/linked_digit_list.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 10/7/18 at 12:29 AM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | 7 | #include "linked_digit_list.h" 8 | 9 | Number::Number(long starting_number) { 10 | add_node(starting_number); 11 | } 12 | 13 | long Number::compute_number() const { 14 | 15 | std::string number_as_string = to_string(); 16 | 17 | return stol(number_as_string); 18 | 19 | } 20 | 21 | std::string Number::to_string() const { 22 | // iterate over the list backwards, (but still print the elements in correct order 23 | std::string my_number_as_string; 24 | for (auto iter = this->number.begin(); iter != this->number.end(); ++iter) { 25 | 26 | std::string current_node = std::to_string(*iter); 27 | 28 | if (current_node.length() < MAX_NUMBER_LENGTH) { 29 | // prepend zeros 30 | std::string prepended_zeros = std::string(MAX_NUMBER_LENGTH - current_node.length(), '0'); 31 | 32 | current_node.insert(0, prepended_zeros); 33 | 34 | } 35 | my_number_as_string += current_node; 36 | } 37 | 38 | return my_number_as_string; 39 | 40 | } 41 | 42 | void Number::add_node(long number) { 43 | 44 | if (number <= max_node_number()) { 45 | std::cout << "Added the number " << (number) << std::endl; 46 | this->number.push_back(number); 47 | } else { 48 | std::string number_as_string = std::to_string(number); 49 | for (unsigned int index = 0; index < number_as_string.length(); index += MAX_NUMBER_LENGTH) { 50 | // needed to ensure each node number length meets the specified requirements 51 | if (number_as_string.length() - index >= MAX_NUMBER_LENGTH) { 52 | std::string current_node = number_as_string.substr(index, MAX_NUMBER_LENGTH); 53 | assert(current_node.length() == MAX_NUMBER_LENGTH); 54 | this->number.push_back(stoi(current_node)); 55 | std::cout << "Added the number " << stoi(current_node) << std::endl; 56 | } else { 57 | // do the prepended zeros strategy 58 | std::string prepended_zeros = std::string(MAX_NUMBER_LENGTH - (number_as_string.length() - index), '0'); 59 | std::string current_node = 60 | prepended_zeros + number_as_string.substr(index, number_as_string.length() - index); 61 | assert(current_node.length() == MAX_NUMBER_LENGTH); 62 | this->number.push_back(stol(current_node)); 63 | std::cout << "Added the number " << stoi(current_node) << std::endl; 64 | } 65 | } 66 | } 67 | 68 | } 69 | 70 | Number Number::operator+(const Number &number) const { 71 | 72 | return Number(this->compute_number() + number.compute_number()); 73 | 74 | } 75 | 76 | Number Number::operator-(const Number &number) const { 77 | // Negative number subtraction is incompatible with the specifications of the problem 78 | assert(this->compute_number() - number.compute_number() > 0); 79 | 80 | return Number(this->compute_number() - number.compute_number()); 81 | } 82 | 83 | Number Number::operator*(const Number &number) const { 84 | 85 | return Number(this->compute_number() * number.compute_number()); 86 | } 87 | 88 | Number Number::operator/(const Number &number) const { 89 | // The problem specifies whole number arithmetic, which disallows floating point numbers 90 | assert(this->compute_number() / number.compute_number() != 0); 91 | 92 | return Number(this->compute_number() * number.compute_number()); 93 | } -------------------------------------------------------------------------------- /chapter_5/linked_digit_list.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Created by Raleigh Littles (raleigh) on 10/7/18 at 12:09 AM. 4 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 5 | // 6 | 7 | #ifndef CHAPTER_5_LINKED_DIGIT_LIST_H 8 | #define CHAPTER_5_LINKED_DIGIT_LIST_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | class Number { 17 | 18 | public: 19 | explicit Number(long starting_number); 20 | 21 | // TODO: implement operator overloads for addition, subtraction, and division? 22 | 23 | std::string to_string() const; 24 | 25 | void add_node(long number); 26 | 27 | long compute_number() const; 28 | 29 | Number operator+(const Number &number) const; 30 | 31 | Number operator-(const Number &number) const; 32 | 33 | Number operator/(const Number &number) const; 34 | 35 | Number operator*(const Number &number) const; 36 | 37 | 38 | private: 39 | //std::list number; 40 | std::list number; 41 | static const unsigned int MAX_NUMBER_LENGTH = 3; 42 | 43 | 44 | int max_node_number() { // if the maximum length of any node is X, then the largest possible node 45 | // in a number is obviously just X (d-1)'s where d is the base of the number system 46 | return (std::stoi(std::string(Number::MAX_NUMBER_LENGTH, '9'))); 47 | }; 48 | 49 | }; 50 | 51 | #endif //CHAPTER_5_LINKED_DIGIT_LIST_H -------------------------------------------------------------------------------- /chapter_6/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(chapter_6) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_FLAGS "-O0 -g") 6 | 7 | add_executable(chapter_6 main.cpp gift_list.h gift_list.cpp) 8 | -------------------------------------------------------------------------------- /chapter_6/gift_list.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 10/13/18 at 11:41 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include "gift_list.h" 7 | 8 | void GiftList::add_gift(std::string user, std::string gift) { 9 | 10 | 11 | auto user_iterator = gift_list.find(user); 12 | 13 | if (user_iterator == gift_list.end()) { 14 | std::cout << "User (" << user << ") not found." << std::endl; 15 | // user not found 16 | std::set new_user_gift_list = {gift}; 17 | auto new_user_pair = std::make_pair(user, new_user_gift_list); 18 | 19 | gift_list.insert(new_user_pair); 20 | } else { 21 | // user already exists 22 | std::cout << "User (" << user << ") already exists" << std::endl; 23 | 24 | auto current_user_gift_element = gift_list.find(user); 25 | 26 | current_user_gift_element->second.insert(gift); 27 | } 28 | 29 | } 30 | 31 | void GiftList::delete_gift(std::string user, std::string gift) { 32 | 33 | 34 | auto existing_users_gift_list = gift_list.find(user); 35 | 36 | if (existing_users_gift_list != gift_list.end()) { 37 | 38 | auto it = existing_users_gift_list->second.erase(gift); 39 | 40 | } 41 | 42 | } 43 | 44 | void GiftList::list_gifts(std::string user) { 45 | 46 | auto user_it = gift_list.find(user); 47 | 48 | if (user_it != gift_list.end()) { 49 | std::cout << "Listing gifts for user: " << user << std::endl; 50 | 51 | auto users_gift_list = user_it->second; 52 | 53 | for (auto gift_it = users_gift_list.begin(); gift_it != users_gift_list.end(); gift_it++) { 54 | std::cout << *gift_it << std::endl; 55 | } 56 | } else { 57 | std::cout << "User " << user << " does not exist." << std::endl; 58 | } 59 | } 60 | 61 | 62 | void GiftList::delete_user(std::string user) { 63 | 64 | gift_list.erase(user); 65 | 66 | } 67 | 68 | void GiftList::list_users() { 69 | 70 | 71 | for (auto user_it = gift_list.begin(); user_it != gift_list.end(); user_it++) { 72 | std::cout << user_it->first << std::endl; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /chapter_6/gift_list.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Raleigh Littles (raleigh) on 10/13/18 at 11:30 PM. 3 | // Copyright (c) 2018 ${ORGANIZATION_NAME} Raleigh Littles (raleigh). All rights reserved. 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | #ifndef CHAPTER_6_GIFT_LIST_H 18 | #define CHAPTER_6_GIFT_LIST_H 19 | 20 | class GiftList { 21 | public: 22 | GiftList() {}; 23 | 24 | void add_gift(std::string user, std::string gift); 25 | 26 | void delete_gift(std::string user, std::string gift); 27 | 28 | void list_gifts(std::string user); 29 | 30 | void delete_user(std::string user); 31 | 32 | void list_users(); 33 | 34 | private: 35 | 36 | std::map> gift_list; 37 | 38 | }; 39 | 40 | #endif //CHAPTER_6_GIFT_LIST_H 41 | -------------------------------------------------------------------------------- /chapter_6/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "gift_list.h" 3 | 4 | int main() { 5 | 6 | GiftList gl = GiftList(); 7 | 8 | gl.add_gift("raleigh", "apple"); 9 | 10 | gl.add_gift("raleigh", "banana"); 11 | 12 | gl.add_gift("raleigh", "cake"); 13 | 14 | gl.add_gift("kevin", "alpha"); 15 | 16 | gl.add_gift("kevin", "bravo"); 17 | 18 | gl.delete_gift("kevin", "alpha"); 19 | 20 | gl.delete_gift("raleigh", "banana"); 21 | 22 | gl.list_gifts("raleigh"); 23 | 24 | gl.list_gifts("kevin"); 25 | 26 | 27 | gl.delete_gift("raleigh", "apple"); 28 | 29 | gl.list_gifts("raleigh"); 30 | 31 | gl.delete_user("kevin"); 32 | 33 | gl.list_gifts("kevin"); 34 | 35 | gl.list_users(); 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /chapter_7/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(chapter_7) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(chapter_7 main.cpp expression_parser.cpp expression_parser.h) 7 | -------------------------------------------------------------------------------- /chapter_7/expression_parser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/4/18. 3 | // 4 | 5 | #include "expression_parser.h" 6 | 7 | std::array ExpressionParser::possible_right_delimeters = {")", "}", "]"}; 8 | 9 | std::array ExpressionParser::possible_left_delimeters = {"(", "{", "["}; 10 | 11 | std::map ExpressionParser::operators = {{"+", 1}, 12 | {"-", 1}, 13 | {"*", 2}, 14 | {"/", 2}}; 15 | 16 | bool ExpressionParser::is_right_delimeter(const std::string character) { 17 | 18 | return (std::find(ExpressionParser::possible_right_delimeters.begin(), 19 | ExpressionParser::possible_right_delimeters.end(), character) != 20 | ExpressionParser::possible_right_delimeters.end()); 21 | 22 | } 23 | 24 | bool ExpressionParser::is_left_delimeter(const std::string character) { 25 | 26 | return (std::find(ExpressionParser::possible_left_delimeters.begin(), 27 | ExpressionParser::possible_left_delimeters.end(), character) != 28 | ExpressionParser::possible_left_delimeters.end()); 29 | } 30 | 31 | bool ExpressionParser::is_operator(const std::string character) { 32 | std::array operators = {"+", "-", "/", "*"}; 33 | return (std::find(operators.begin(), operators.end(), character) != operators.end()); 34 | 35 | } 36 | 37 | bool ExpressionParser::is_lower_precedence(const std::string &operator1, const std::string &operator2) { 38 | 39 | return (ExpressionParser::operators.find(operator1)->second < ExpressionParser::operators.find(operator2)->second); 40 | } 41 | 42 | double ExpressionParser::parse_postfix_expression(const std::string &expression) { 43 | //std::vector splitted_results; 44 | 45 | std::array possible_operators = {"+", "-", "/", "*"}; 46 | 47 | std::stack expression_stack; 48 | 49 | std::istringstream string_stream(expression); 50 | 51 | for (std::string ch; string_stream >> ch;) { 52 | if (is_operator(ch)) { 53 | // all operators are binary operators, so they require two operands 54 | 55 | if (expression_stack.size() >= 2) { 56 | 57 | double operand1 = expression_stack.top(); 58 | expression_stack.pop(); 59 | 60 | double operand2 = expression_stack.top(); 61 | expression_stack.pop(); 62 | 63 | // C++ doesn't have reflection, which would make this a lot easier, and case statements don't support strings.. 64 | double result; 65 | 66 | if (ch == "+") { 67 | result = operand2 + operand1; 68 | 69 | } else if (ch == "*") { 70 | result = operand2 * operand1; 71 | } else if (ch == "-") { 72 | result = operand2 - operand1; 73 | } else if (ch == "/") { 74 | result = operand2 / operand1; 75 | } else { 76 | std::cout << "The operation you entered ( " << ch << ") is not supported." << std::endl; 77 | exit(1); 78 | } 79 | 80 | expression_stack.push(result); 81 | } 82 | } else { 83 | expression_stack.push(std::stod(ch)); 84 | } 85 | } 86 | 87 | assert(expression_stack.size() == 1); 88 | 89 | return expression_stack.top(); 90 | } 91 | 92 | std::string ExpressionParser::convert_parenthesized_infix_to_postfix( 93 | const std::string &parenthesized_infix_expression) { 94 | std::string postfix_expression; 95 | 96 | std::array possible_operators = {"/", "*", "-", "+"}; 97 | 98 | std::array possible_left_delimeters = {"(", "{", "["}; 99 | 100 | std::array possible_right_delimeters = {")", "}", "]"}; 101 | 102 | std::stack expression_stack; 103 | 104 | std::istringstream string_stream(parenthesized_infix_expression); 105 | 106 | for (std::string ch; string_stream >> ch;) { 107 | 108 | if (is_left_delimeter(ch)) { 109 | expression_stack.push(ch); 110 | } else if (std::all_of(ch.begin(), ch.end(), ::isalnum)) { 111 | postfix_expression += ch; 112 | } else if (is_operator(ch)) { 113 | expression_stack.push(ch); 114 | } else { 115 | // next symbol should be a right parenthesis 116 | assert(is_right_delimeter(ch)); 117 | 118 | // operation symbol should be on the top of the stack! 119 | assert(is_operator(expression_stack.top())); 120 | 121 | postfix_expression += expression_stack.top(); 122 | 123 | expression_stack.pop(); 124 | 125 | // after the operation symbol got popped, there should be a left parenthesis on the top 126 | 127 | assert(is_left_delimeter(expression_stack.top())); 128 | expression_stack.pop(); 129 | } 130 | } 131 | assert(expression_stack.empty()); 132 | 133 | return postfix_expression; 134 | } 135 | 136 | std::string ExpressionParser::convert_general_infix_to_postfix(const std::string &infix_expression) { 137 | std::string postfix_expression; 138 | std::stack expression_stack; 139 | 140 | std::istringstream string_stream(infix_expression); 141 | 142 | for (std::string ch; string_stream >> ch;) { 143 | 144 | if (is_left_delimeter(ch)) { 145 | expression_stack.push(ch); 146 | } else if (std::all_of(ch.begin(), ch.end(), ::isalnum)) { 147 | postfix_expression += ch; 148 | } else if (is_operator(ch)) { 149 | 150 | while (!expression_stack.empty() && 151 | !is_left_delimeter(expression_stack.top()) && 152 | !(is_operator(expression_stack.top()) && is_lower_precedence(expression_stack.top(), ch))) { 153 | postfix_expression += expression_stack.top(); 154 | expression_stack.pop(); 155 | } 156 | 157 | expression_stack.push(ch); 158 | 159 | } else { 160 | assert(is_right_delimeter(ch)); 161 | 162 | postfix_expression += expression_stack.top(); 163 | expression_stack.pop(); 164 | 165 | uint8_t count = 0; 166 | 167 | // keep printing and popping until the next symbol on the stack is a left parenthesis 168 | 169 | while ((expression_stack.empty() == false) and (is_left_delimeter(expression_stack.top()) == false)) { 170 | postfix_expression += expression_stack.top(); 171 | expression_stack.pop(); 172 | } 173 | 174 | assert(is_left_delimeter(expression_stack.top())); 175 | 176 | // now pop that left parenthesis 177 | expression_stack.pop(); 178 | 179 | 180 | } 181 | } 182 | 183 | while (expression_stack.empty() == false) { 184 | // there should be no remaining left parenthesis on the stack now, if there are then the expression was not balanced 185 | assert(is_left_delimeter(expression_stack.top()) == false); 186 | 187 | postfix_expression += expression_stack.top(); 188 | 189 | expression_stack.pop(); 190 | } 191 | 192 | return postfix_expression; 193 | 194 | } -------------------------------------------------------------------------------- /chapter_7/expression_parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/4/18. 3 | // 4 | 5 | #ifndef CHAPTER_7_EXPRESSION_PARSER_H 6 | #define CHAPTER_7_EXPRESSION_PARSER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | class ExpressionParser { 19 | 20 | public: 21 | 22 | static std::map operators; 23 | 24 | static std::array possible_right_delimeters; 25 | 26 | static std::array possible_left_delimeters; 27 | 28 | static bool is_right_delimeter( std::string character); 29 | 30 | static bool is_left_delimeter( std::string character); 31 | 32 | /* TODO: Combine this and the method below into one method */ 33 | static bool is_operator( std::string character); 34 | 35 | /* Is operator 1 a lower precedence than the 2nd operator? */ 36 | static bool is_lower_precedence(const std::string &operator1, const std::string &operator2); 37 | 38 | /* See: Figure 7.10, page 380 */ 39 | static double parse_postfix_expression(const std::string &expression); 40 | 41 | /* See: Figure 7.11, page 382 */ 42 | static std::string convert_parenthesized_infix_to_postfix(const std::string &parenthesized_infix_expression); 43 | 44 | 45 | /* See: Figure 7.12, page 385 */ 46 | static std::string convert_general_infix_to_postfix(const std::string &infix_expression); 47 | 48 | }; 49 | 50 | 51 | #endif //CHAPTER_7_EXPRESSION_PARSER_H 52 | -------------------------------------------------------------------------------- /chapter_7/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "expression_parser.h" 3 | 4 | int main() { 5 | std::cout << "Hello, World!" << std::endl; 6 | 7 | const std::string my_expr = "5 3 2 * + 4 - 5 +"; 8 | 9 | double result = ExpressionParser::parse_postfix_expression(my_expr); 10 | 11 | std::cout << result << std::endl; 12 | 13 | std::cout << "----------------------" << std::endl; 14 | 15 | const std::string example_infix_expression = "( ( ( A + 7 ) * ( B / C ) ) - ( 2 * D ) )"; 16 | 17 | std::string postfix_expression = ExpressionParser::convert_parenthesized_infix_to_postfix(example_infix_expression); 18 | 19 | std::cout << postfix_expression << std::endl; 20 | 21 | std::cout << "----------------------" << std::endl; 22 | 23 | const std::string example_general_infix_expression = "3 * X + ( Y - 12 ) - Z"; 24 | 25 | postfix_expression = ExpressionParser::convert_general_infix_to_postfix(example_general_infix_expression); 26 | 27 | std::cout << postfix_expression << std::endl; 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /chapter_8/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(chapter_8) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(chapter_8 main.cpp store_checkout.cpp store_checkout.h) -------------------------------------------------------------------------------- /chapter_8/main.cpp: -------------------------------------------------------------------------------- 1 | #include "store_checkout.h" 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | StoreCheckout costco = StoreCheckout(10, 100, 25); 8 | 9 | costco.simulate_passage_of_time(100); 10 | 11 | costco.print_lines_status(); 12 | 13 | } -------------------------------------------------------------------------------- /chapter_8/store_checkout.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/4/18. 3 | // 4 | 5 | 6 | 7 | #include "store_checkout.h" 8 | 9 | StoreCheckout::StoreCheckout(unsigned int num_of_lines, 10 | unsigned int max_wait_per_customer, 11 | unsigned int starting_customers_per_line) 12 | : MAX_WAIT_PER_CUSTOMER(max_wait_per_customer) { 13 | 14 | 15 | for (unsigned int i = 0; i < num_of_lines; i++) { 16 | std::deque checkout_line; 17 | 18 | for (unsigned int j = 0; j < starting_customers_per_line; j++) { 19 | checkout_line.push_back(0L); 20 | } 21 | checkout_system.push_back(checkout_line); 22 | } 23 | 24 | } 25 | 26 | void StoreCheckout::simulate_passage_of_time(unsigned int seconds) { 27 | for (unsigned int i = 0; i < seconds; i++) { 28 | unsigned int users_popped = 0; 29 | unsigned int users_added = 0; 30 | unsigned int users_left = 0; 31 | 32 | // Since we want to access original objects and modify them, you must use the address-of operator! 33 | for (auto &qu : checkout_system) { 34 | 35 | // flip a coin to decide if the line should have moved a user at that time 36 | 37 | bool transaction_complete = get_random_boolean(); 38 | 39 | if (transaction_complete) { 40 | users_popped++; 41 | qu.pop_front(); 42 | } 43 | 44 | // flip a coin to decide if a user has entered the line since the last moment of time 45 | bool user_arrived = get_random_boolean(); 46 | 47 | if (user_arrived) { 48 | users_added++; 49 | qu.push_back(0L); 50 | } 51 | 52 | for (auto &user : qu) { 53 | user++; 54 | if (user > this->MAX_WAIT_PER_CUSTOMER) { // user gets frustrated and leaves the store 55 | users_left++; 56 | qu.erase(std::find(qu.begin(), qu.end(), user)); 57 | } 58 | } 59 | 60 | qu.shrink_to_fit(); 61 | } 62 | } 63 | } 64 | 65 | bool StoreCheckout::get_random_boolean() { 66 | // pick a random number, then use that as the seed for the classic Mersenne Twister psuedo-random number generator with a period of 2^(19937)-1 67 | std::mt19937 random_number_generator; 68 | random_number_generator.seed(std::random_device()()); 69 | std::uniform_int_distribution boolean(0, 1); 70 | 71 | return static_cast(boolean(random_number_generator)); 72 | } 73 | 74 | void StoreCheckout::print_lines_status() { 75 | std::cout << "Printing report of " << checkout_system.size() << " lines." << std::endl; 76 | for (unsigned int queue_number = 0; queue_number < checkout_system.size(); queue_number++) { 77 | std::cout << "Current queue (#" << queue_number << ") has size : " << checkout_system[queue_number].size() 78 | << std::endl; 79 | 80 | } 81 | } -------------------------------------------------------------------------------- /chapter_8/store_checkout.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/4/18. 3 | // 4 | 5 | #ifndef CHAPTER_8_STORE_QUEUE_H 6 | #define CHAPTER_8_STORE_QUEUE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class StoreCheckout { 15 | // Problem description 16 | /* 17 | * Write a simulation program of the lines at a 18 | grocery store. The program will be similar to 19 | the car wash simulation, except that there 20 | are multiple queues instead of one. You might use a 21 | vector of queues to simulate the lines. Assume that 22 | there are five cashier lines at the grocery store. Cus- 23 | available line is chosen. Each transaction takes a 24 | random amount of time to complete. 25 | tomers enter randomly to check out, and then enter 26 | the shortest line. If the lines are equal, then the first 27 | For additional work, expand the grocery line pro- 28 | gram to allow shoppers to: 29 | • Avoid a line if all lines are a certain length 30 | • Leave a line if they have waited beyond a cer- 31 | tain time 32 | • Check if another line is shorter at specified 33 | time intervals 34 | • Switch lines if another line is shorter 35 | */ 36 | 37 | public: 38 | StoreCheckout(unsigned int num_of_lines, 39 | unsigned int max_wait_per_customer, 40 | unsigned int starting_customers_per_line); 41 | 42 | 43 | void simulate_passage_of_time(unsigned int seconds); 44 | 45 | void print_lines_status(); 46 | 47 | //void switch_line(unsigned int starting_line, unsigned int ending_line); 48 | 49 | // use a deque only because it supports iteration, while regular queues do not 50 | std::vector> checkout_system; 51 | const unsigned int MAX_WAIT_PER_CUSTOMER; 52 | 53 | void increment_all_times(); 54 | 55 | bool get_random_boolean(); 56 | 57 | 58 | }; 59 | 60 | #endif //CHAPTER_8_STORE_QUEUE_H 61 | -------------------------------------------------------------------------------- /chapter_9/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(chapter_9) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(chapter_9 main.cpp chapter_9_projects.hpp) -------------------------------------------------------------------------------- /chapter_9/chapter_9_projects.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by raleigh on 11/11/18. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | namespace RecursiveThinking { 9 | 10 | /* Problem description, problem 6 11 | * 12 | * The formula for computing the number of 13 | ways of choosing r different things from a 14 | set of n things is the following: 15 | n ! 16 | C ( n , r ) = ---------------------- 17 | r ! ( n – r ) ! 18 | In this formula, the factorial function is represented 19 | by an exclamation point (!), and defined as the product: 20 | n ! = n × ( n – 1 ) × ( n – 2 ) × ... × 1 21 | Discover a recursive version of the C(n, r) formula 22 | and write a recursive C++ function that computes 23 | the value of the formula. Embed the function in a 24 | program and test it. 25 | */ 26 | float recursive_combination_formula(int n, int r) { 27 | 28 | /* Remember that the C(n, r) formula is the same as the binomial coefficient. Then, we can use Pascal's Rule: 29 | C(n, r) = C(n-1, r) + C(n-1, r-1) to create a recursive implementation. */ 30 | 31 | // this is by definition of the binomial coefficient. 32 | // there are 0 ways to choose a sample of r items from a collection of n unless r is a non-negative number 33 | // similarly, it is impossible to choose a set of r elements from n if r is larger than n 34 | if ((r < 0) or (r > n)) { 35 | return 0; 36 | } 37 | 38 | // there is only 1 way to choose r items out of n if you wish to choose no items, and that is not choosing any items at all 39 | // similarly, if there are no elements to even choose from, there is obviously only way to choose (not choosing) 40 | if ((r == 0) or (n <= 1)) { 41 | return 1; 42 | } 43 | 44 | return (recursive_combination_formula(n - 1, r) + recursive_combination_formula(n - 1, r - 1)); 45 | 46 | } 47 | 48 | // std::string does have a way to create repeated characters, but it does NOT work with strings. E.g.: 49 | // std::string("a", 4) works, and will return "aaaa", but you cannot replace "a" with an std::string-like type 50 | std::string operator*(const std::string &str, std::size_t times) { 51 | std::stringstream stream; 52 | for (size_t i = 0; i < times; i++) { 53 | stream << str; 54 | } 55 | return stream.str(); 56 | } 57 | 58 | 59 | std::string recursive_function(uint8_t left_margin, uint8_t length) { 60 | if (length == 0) { 61 | return ""; 62 | } else { 63 | return (recursive_function(left_margin, length / 2) + std::string(" ") * left_margin + 64 | std::string("* ") * length + "\n" + 65 | recursive_function(left_margin + length / 2, length / 2)); 66 | } 67 | 68 | } 69 | 70 | /* Problem description, Problem 9 */ 71 | /* 72 | * Examine this pattern of asterisks and blanks, 73 | and write a recursive function that can gen- 74 | erate exactly this pattern: 75 | */ 76 | std::string generate_asterisk_pattern() { 77 | // Hint: If you're having trouble understanding the pattern, write out how many asterisks appear on each (horizontal) 78 | // line. 79 | 80 | return recursive_function(0, 8); 81 | 82 | } 83 | 84 | // Note: The Ackermann function is unique since its one of the earliest examples of a total computable function that isn't 85 | // primitive recursive, i.e. a function that can't be implemented using only do-loops 86 | int ackermann_function(int x, int y) 87 | { 88 | assert((x >= 0) and (y >= 0)); // ackermann function is only defined for non-negative integers 89 | 90 | if (x == 0) 91 | { 92 | return 2 * y; 93 | } 94 | 95 | else if (x >= 1) 96 | { 97 | if (y == 0) 98 | { 99 | return 0; 100 | } 101 | 102 | if (y == 1) 103 | { 104 | return 1; 105 | } 106 | 107 | if (y >= 2) 108 | { 109 | return ackermann_function(x-1, ackermann_function(x, y-1)); 110 | } 111 | } 112 | 113 | } 114 | 115 | 116 | } 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /chapter_9/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "chapter_9_projects.hpp" 3 | 4 | 5 | int main() { 6 | 7 | std::cout << RecursiveThinking::recursive_combination_formula(10, 6) << std::endl; 8 | 9 | std::cout << RecursiveThinking::generate_asterisk_pattern() << std::endl; 10 | } --------------------------------------------------------------------------------