& matches) {
57 | // STUDENT TODO: Implement this function.
58 | }
59 |
60 | /* #### Please don't remove this line! #### */
61 | #include "autograder/utils.hpp"
62 |
--------------------------------------------------------------------------------
/assignment2/short_answer.txt:
--------------------------------------------------------------------------------
1 | Before submitting this file, make sure that there are no more TODO
2 | placeholders remaining in the file (and remove this comment too).
3 |
4 | Marriage Pact
5 | -------------
6 |
7 | Q1. It is your choice to use either an ordered or unordered set. In a few sentences, what are some of the tradeoffs between the two? Additionally, please give an example (that has not been shown in lecture) of a valid hash function that could be used to hash student names for an unordered set.
8 | A1. TODO
9 |
10 | Q2. Note that we are saving pointers to names in the queue, not names themselves. Why might this be desired in this problem? What happens if the original set where the names are stored goes out of scope and the pointers are referenced?
11 | A2. TODO
--------------------------------------------------------------------------------
/assignment3/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Assignment 3: Make a Class
6 |
7 | Due Friday, May 9th at 11:59PM
8 |
9 | ## Overview
10 |
11 |
12 | (\_/)
13 | (•x•) Howdy
14 | (<☕)
15 |
16 |
17 | Now that we've learned about classes, it’s time for you to make your own! Have fun with this, let the creative juices flow. Your class can represent anything, and feel free to make more than one if you'd like. There are some requirements though. As long as you meet these requirements, you’ll get credit for this assignment! 🙂
18 |
19 | There are four files you'll work with for this assignment:
20 |
21 | * `class.h` - This is the header file for your class, where the class **declaration** will go.
22 | * `class.cpp` - This is the `.cpp` file for your class, where the class **definition** will go.
23 | * `sandbox.cpp` - You'll construct an instance of your class here.
24 | * `short_answer.txt` - You'll answer a few short answer questions here.
25 |
26 | To download the starter code for this assignment, please see the instructions for [**Getting Started**](../README.md#getting-started) on the course assignments repository.
27 |
28 | ## Running your code
29 |
30 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit Ctrl+\` or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment3/` directory and run:
31 |
32 | ```sh
33 | g++ -std=c++20 main.cpp class.cpp -o main
34 | ```
35 |
36 | Assuming that your code compiles without any compiler errors, you can now do:
37 |
38 | ```sh
39 | ./main
40 | ```
41 |
42 | which will actually run the `main` function in `main.cpp`.
43 |
44 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track!
45 |
46 | > [!NOTE]
47 | >
48 | > ### Note for Windows
49 | >
50 | > On Windows, you may need to compile your code using
51 | >
52 | > ```sh
53 | > g++ -static-libstdc++ -std=c++20 main.cpp class.cpp -o main
54 | > ```
55 | >
56 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with:
57 | >
58 | > ```sh
59 | > ./main.exe
60 | > ```
61 |
62 | ## Part 1: Making your class
63 |
64 | Let your creative juices flow! Fill in `class.h` and `class.cpp` to create your own custom class. Please refer to the relevant slides from Tuesday's lecture on classes for more information. Your class can represent pretty much anything you want, as long as it meets the following requirements.
65 |
66 | > [!IMPORTANT]
67 | > ### Class Requirements
68 | >
69 | > Your class must:
70 | > 1. Have a custom constructor taking **one or more** parameters.
71 | > 2. Have a default (parameterless) constructor (i.e. constructor overloading).
72 | > 3. Have one or more private member fields (i.e. variables).
73 | > 4. Have one or more private member functions.
74 | > - Remember, private functions are like what happens underneath the hood of your car! They are a necessary part of the implementation of a class, but shouldn't be exposed in the public interface. Try to think of a private member function that logically makes sense in the context of your class.
75 | > 5. Have **at least one** public getter function for one of the private fields.
76 | > - E.g. if `int data` is the field, you must have a function called `get_data` or `getData` with the signature int getData();
77 | > - The getter function should also be marked `const`. Refer to Thursday's lecture on `const` correctness if you are unfamiliar!
78 | > 6. Have at least one public setter function for one of the private fields.
79 | > - E.g. if `int data` is the field, you must have a function called `set_data` or `setData` with the signature void setData(int value);
80 |
81 | Note that this is the bare minimum to get credit for the assignment. Please feel free to go above and beyond these requirements or create more than one class if you want extra practice!
82 |
83 | > [!NOTE]
84 | > For brownie points, you can choose to create a class template instead of a regular class using the `template ` notation discussed on Thursday's lecture. This is totally optional!
85 | >
86 | > Note that if you do decide to create a class template, you **must remove class.cpp
87 | > from the compilation command.** For example, on Mac/Linux, the compilation
88 | > command will be:
89 | >
90 | > ```sh
91 | > g++ -std=c++20 main.cpp -o main
92 | > ```
93 | >
94 | > Remember to also swap the includes so that the `.h` file includes the `.cpp`
95 | > file at the end of the file, as discussed in Thursday's lecture.
96 |
97 | Now that you've created your class, let's actually use it. **Inside of the `sandbox` function in `sandbox.cpp`, construct an instance of your class!** You can do so however you like (call default constructor, use uniform initialization, etc.).
98 |
99 | To see if you did everything correctly, compile and run your code! The autograder will give you feedback on your class and check if it meets the specifications above.
100 |
101 | ## Part 2: Short answer questions
102 |
103 | Please answer the following questions inside `short_answer.txt`. We expect about 2-3 sentences per question.
104 |
105 | > [!IMPORTANT]
106 | > `short_answer.txt`
107 | > - **Q1:** What’s const-correctness and why is it important?
108 | > - **Q2:** Is your class const-correct? How do you know?
109 |
110 | ## 🚀 Submission Instructions
111 |
112 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/BMHGeNPjotMS9njK7). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading.
113 |
114 | Your deliverable should be:
115 |
116 | * `class.h`
117 | * `class.cpp`
118 | * `sandbox.cpp`
119 | * `short_answer.txt`
120 |
121 | You may resubmit as many times as you'd like before the deadline.
122 |
--------------------------------------------------------------------------------
/assignment3/autograder/autograder.py:
--------------------------------------------------------------------------------
1 | from typing import Callable, List, Set, Tuple, TypeVar
2 | from utils import Autograder, get_declarations, install_castxml
3 | install_castxml()
4 |
5 | import os
6 | import re
7 |
8 | from pygccxml import declarations
9 | from colorama import Fore, Style, Back
10 |
11 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
12 | AUTOGRADER_DIR = os.path.join(PATH, "autograder")
13 |
14 | class_decl: declarations.class_t = None
15 | definitions: Set[Tuple[str, str]] = None
16 |
17 |
18 | def norm_path(path: os.PathLike) -> os.PathLike:
19 | return os.path.normpath(os.path.relpath(path, os.getcwd()))
20 |
21 |
22 | def get_definitions(source_file: os.PathLike) -> Set[Tuple[str, str]]:
23 | if not os.path.exists(source_file):
24 | raise RuntimeError(f"Could not find source file: {source_file}")
25 |
26 | with open(source_file, "r") as file:
27 | content = file.read()
28 |
29 | comment_pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)"
30 | regex = re.compile(comment_pattern, re.MULTILINE | re.DOTALL)
31 | content = regex.sub(
32 | lambda match: "" if match.group(2) is not None else match.group(1), content
33 | )
34 |
35 | string_pattern = r"\".*?\""
36 | regex = re.compile(string_pattern, re.MULTILINE | re.DOTALL)
37 | content = regex.sub("", content)
38 |
39 | def remove_nested_braces(content):
40 | result, stack = [], []
41 | for char in content:
42 | if char == "{":
43 | stack.append("{")
44 | elif char == "}":
45 | stack.pop() if stack else None
46 | elif not stack:
47 | result.append(char)
48 | return "".join(result)
49 |
50 | content = remove_nested_braces(content)
51 |
52 | definition_pattern = r"(\w+)(?:<.*>)?::(\w+)"
53 | regex = re.compile(definition_pattern)
54 | matches = regex.findall(content)
55 |
56 | return matches
57 |
58 |
59 | def assert_defined(decl: declarations.declarated_t):
60 | class_name = class_decl.name
61 | class_name = re.sub(r"<.*>", "", class_name)
62 |
63 | decl_name: str = None
64 | if isinstance(decl, declarations.constructor_t):
65 | decl_name = decl.name
66 | elif isinstance(decl, declarations.member_function_t):
67 | decl_name = decl.name
68 | else:
69 | raise RuntimeError(
70 | f"Unhandled declaration type: {type(decl)}. Please reach out on Ed!"
71 | )
72 |
73 | decl_tuple = (class_name, decl_name)
74 | if decl_tuple not in definitions:
75 | raise RuntimeError(
76 | f"Found declaration for {decl}, but could not find a matching definition in class.cpp"
77 | )
78 |
79 |
80 | def skip_decl(decl: declarations.declaration_t, reason: str):
81 | print(f"⏩ Disregarding {decl}, {reason}")
82 |
83 |
84 | def find_decl(decl: declarations.declaration_t):
85 | print(f"🔍 Found {decl}!")
86 |
87 |
88 | T = TypeVar("T")
89 |
90 |
91 | def get_decls(getter: Callable[[], T], kind_plural: str, scope: str = "class") -> T:
92 | try:
93 | return getter()
94 | except RuntimeError as err:
95 | if "query returned 0 declarations" in str(err):
96 | raise RuntimeError(f"Could not find any {kind_plural} in {scope}")
97 |
98 |
99 | def setup():
100 | sandbox_cpp_path = os.path.join(PATH, "sandbox.cpp")
101 | class_h_path = os.path.join(PATH, "class.h")
102 | class_cpp_path = os.path.join(PATH, "class.cpp")
103 |
104 | if not os.path.isfile(sandbox_cpp_path):
105 | raise RuntimeError(
106 | "Couldn't find '{main.cpp}'. Did you delete it from the starter code?"
107 | )
108 |
109 | decls = get_declarations(norm_path(sandbox_cpp_path))
110 | global_namespace = declarations.get_global_namespace(decls)
111 |
112 | # Try to find a class inside of class.h
113 | def find_class_decl() -> declarations.class_t:
114 | try:
115 | classes = global_namespace.classes()
116 | except:
117 | classes = []
118 | cls_decl: declarations.class_t
119 | for cls_decl in classes:
120 | location: declarations.location_t = cls_decl.location
121 | if norm_path(class_h_path) == norm_path(location.file_name):
122 | return cls_decl
123 | raise Exception(
124 | "Couldn't find a class inside of class.h. Possible reasons:\n"
125 | " - Did you define one?\n"
126 | ' - Did you #include "class.h" inside main.cpp?\n'
127 | " - Did you construct an instance of the class inside main.cpp?"
128 | )
129 |
130 | global class_decl
131 | class_decl = find_class_decl()
132 | print(
133 | f"{Fore.GREEN}Autograder found class {Back.LIGHTGREEN_EX}{class_decl.name}{Back.RESET} inside class.h!{Style.RESET_ALL}"
134 | )
135 |
136 | global definitions
137 | definitions = get_definitions(class_cpp_path)
138 |
139 |
140 | def test_parameterized_constructor():
141 | constructors = get_decls(class_decl.constructors, "constructors")
142 | cons: declarations.constructor_t
143 | for cons in constructors:
144 | if cons.is_artificial:
145 | continue
146 | if cons.access_type != declarations.ACCESS_TYPES.PUBLIC:
147 | skip_decl(cons, "not public")
148 | continue
149 | if len(cons.arguments) == 0:
150 | skip_decl(cons, "has no arguments")
151 | continue
152 |
153 | assert_defined(cons)
154 | find_decl(cons)
155 | return
156 |
157 | raise RuntimeError(
158 | "Could not find a public constructor taking one or more arguments"
159 | )
160 |
161 |
162 | def test_parameterless_constructor():
163 | constructors = get_decls(class_decl.constructors, "constructors")
164 | cons: declarations.constructor_t
165 | for cons in constructors:
166 | if cons.is_artificial:
167 | continue
168 | if cons.access_type != declarations.ACCESS_TYPES.PUBLIC:
169 | skip_decl(cons, "not public")
170 | continue
171 | if len(cons.arguments) > 0:
172 | skip_decl(cons, "has one or more parameters")
173 | continue
174 |
175 | assert_defined(cons)
176 | find_decl(cons)
177 | return
178 |
179 | raise RuntimeError("Could not find a public parameterless constructor")
180 |
181 |
182 | def test_private_member_fields():
183 | fields = get_decls(class_decl.variables, "fields")
184 | field: declarations.variable_t
185 | for field in fields:
186 | if field.is_artificial:
187 | continue
188 | if field.type_qualifiers.has_static:
189 | skip_decl(field, "marked static")
190 | continue
191 | if field.type_qualifiers.has_extern:
192 | skip_decl(field, "marked extern")
193 | continue
194 | if field.access_type == declarations.ACCESS_TYPES.PUBLIC:
195 | skip_decl(
196 | field,
197 | "not private. Note: In general, it is bad practice to define public fields!",
198 | )
199 | continue
200 | if field.access_type != declarations.ACCESS_TYPES.PRIVATE:
201 | skip_decl(field, "not private")
202 | continue
203 |
204 | find_decl(field)
205 | return
206 |
207 | raise RuntimeError("Could not find a private field")
208 |
209 |
210 | def test_private_member_functions():
211 | functions = get_decls(class_decl.member_functions, "member functions")
212 | function: declarations.member_function_t
213 | for function in functions:
214 | if function.is_artificial:
215 | continue
216 | if function.has_static:
217 | skip_decl(function, "marked static")
218 | continue
219 | if function.has_extern:
220 | skip_decl(function, "marked extern")
221 | continue
222 | if function.access_type != declarations.ACCESS_TYPES.PRIVATE:
223 | skip_decl(function, "not private")
224 | continue
225 |
226 | find_decl(function)
227 | assert_defined(function)
228 | return
229 |
230 | raise RuntimeError("Could not find a private member function")
231 |
232 |
233 | def get_private_fields() -> List[declarations.variable_t]:
234 | def is_private(field: declarations.variable_t) -> bool:
235 | if field.is_artificial:
236 | return False
237 | if field.type_qualifiers.has_static:
238 | return False
239 | if field.type_qualifiers.has_extern:
240 | return False
241 | return field.access_type == declarations.ACCESS_TYPES.PRIVATE
242 |
243 | fields = get_decls(class_decl.variables, "fields")
244 | return [field for field in fields if is_private(field)]
245 |
246 |
247 | def get_prefix_functions(
248 | prefix: str,
249 | ) -> List[Tuple[declarations.member_function_t, str]]:
250 | name_regex = re.compile(rf"{prefix}_?([a-zA-Z]\w*)")
251 |
252 | funcs = []
253 | func: declarations.member_function_t
254 | for func in get_decls(class_decl.member_functions, "member functions"):
255 | if func.is_artificial:
256 | continue
257 | if func.has_static:
258 | continue
259 | if func.has_extern:
260 | continue
261 | if func.access_type != declarations.ACCESS_TYPES.PUBLIC:
262 | continue
263 | match = name_regex.match(func.name)
264 | if match is None:
265 | continue
266 | funcs.append((func, match.group(1)))
267 | return funcs
268 |
269 |
270 | def find_matching_function(prefix: str, type: str):
271 | fields = get_private_fields()
272 | field_names = {f.name.lower().strip("_"): f for f in fields}
273 | funcs = get_prefix_functions(prefix)
274 |
275 | for func, field_name in funcs:
276 | field_name_lower = field_name.lower()
277 | if field_name_lower not in field_names:
278 | skip_decl(func, f"{field_name} did not match a private field")
279 | continue
280 |
281 | find_decl(func)
282 | return func, field_names[field_name_lower]
283 |
284 | raise RuntimeError(
285 | f"No {type} function found for a private field. "
286 | f"Options were:\n - {type.title()}s: [{', '.join(p[0].name for p in funcs)}]"
287 | f"\n - Private fields: [{', '.join(f.name for f in fields)}]"
288 | )
289 |
290 |
291 | def test_getter_function():
292 | function, field = find_matching_function("get", "getter")
293 | assert len(function.arguments) == 0, "A getter function must have no arguments"
294 | assert (
295 | function.return_type == field.decl_type
296 | ), f"The return type of a getter function must match its field. Found {function.return_type} but expected {field.decl_type}"
297 | assert (
298 | function.has_const
299 | ), "A getter function should be marked as const (refer to Thursday's lecture on const correctness)"
300 | assert_defined(function)
301 |
302 |
303 | def test_setter_function():
304 | function, field = find_matching_function("set", "setter")
305 | assert (
306 | len(function.arguments) == 1
307 | ), "A setter should have a single argument matching the type of its field"
308 |
309 | assert declarations.base_type(function.argument_types[0]) == declarations.base_type(
310 | field.decl_type
311 | ), (
312 | f"The argument of a setter should be the type of its field. "
313 | f"Found {function.argument_types[0]} but expected {field.decl_type}"
314 | )
315 | assert (
316 | function.return_type == declarations.void_t()
317 | ), "A setter should have a void return type"
318 | assert_defined(function)
319 |
320 |
321 | if __name__ == "__main__":
322 | grader = Autograder()
323 | grader.setup = setup
324 | grader.add_part(
325 | "#1 / Public parameterized constructor", test_parameterized_constructor
326 | )
327 | grader.add_part(
328 | "#2 / Public parameterless constructor", test_parameterless_constructor
329 | )
330 | grader.add_part("#3 / Private field", test_private_member_fields)
331 | grader.add_part("#4 / Private member function", test_private_member_functions)
332 | grader.add_part("#5 / Public getter function", test_getter_function)
333 | grader.add_part("#6 / Public setter function", test_setter_function)
334 | grader.run()
335 |
--------------------------------------------------------------------------------
/assignment3/autograder/requirements.txt:
--------------------------------------------------------------------------------
1 | colorama==0.4.6
2 | pygccxml==2.5.0
3 | requests==2.32.3
4 | py-cpuinfo==9.0.0
--------------------------------------------------------------------------------
/assignment3/class.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment3/class.cpp
--------------------------------------------------------------------------------
/assignment3/class.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment3/class.h
--------------------------------------------------------------------------------
/assignment3/docs/bjarne.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment3/docs/bjarne.jpg
--------------------------------------------------------------------------------
/assignment3/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * CS106L Assignment 3: Make a Class
3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca.
4 | */
5 |
6 | /* ========================================================================= *
7 | * Don't worry about anything beyond this point. *
8 | * (unless you are really curious and want to!) *
9 | * ========================================================================= */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | #include "sandbox.cpp"
17 |
18 | int run_autograder() {
19 | auto run_program = [](std::string program, std::initializer_list args,
20 | bool silent = false) {
21 | std::stringstream ss;
22 |
23 | ss << program;
24 | for (const auto& arg : args) {
25 | ss << ' ' << arg;
26 | }
27 |
28 | if (silent) {
29 | #ifdef _WIN32
30 | ss << " >nul 2>&1";
31 | #else
32 | ss << " >/dev/null 2>&1";
33 | #endif
34 | }
35 |
36 | std::cout.flush();
37 | return system(ss.str().c_str());
38 | };
39 |
40 | std::string python;
41 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) {
42 | if (run_program(option, {"--version"}, true) == 0) {
43 | python = option;
44 | break;
45 | }
46 | }
47 |
48 | if (python.empty()) {
49 | std::cerr << "Python was not found on your system. Please install Python and "
50 | "try again."
51 | << "\n";
52 | std::exit(1);
53 | }
54 |
55 | return run_program(python, {"autograder/autograder.py"});
56 | }
57 |
58 | int main() {
59 | sandbox();
60 | return run_autograder();
61 | }
--------------------------------------------------------------------------------
/assignment3/sandbox.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * CS106L Assignment 3: Make a Class
3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca.
4 | */
5 |
6 | void sandbox() {
7 | // STUDENT TODO: Construct an instance of your class!
8 | }
--------------------------------------------------------------------------------
/assignment3/short_answer.txt:
--------------------------------------------------------------------------------
1 | Before submitting this file, make sure that there are no more TODO
2 | placeholders remaining in the file (and remove this comment too).
3 |
4 | Make a Class
5 | ------------
6 |
7 | Q1. What’s const-correctness and why is it important?
8 | A1. TODO
9 |
10 | Q2. Is your class const-correct? How do you know?
11 | A2. TODO
--------------------------------------------------------------------------------
/assignment3/utils.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * CS106L Assignment 3: Make a Class
3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca.
4 | */
5 |
6 | /* ========================================================================= *
7 | * Don't worry about anything beyond this point. *
8 | * (unless you are really curious and want to!) *
9 | * ========================================================================= */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | int run_autograder() {
17 | auto run_program = [](std::string program, std::initializer_list args,
18 | bool silent = false) {
19 | std::stringstream ss;
20 |
21 | ss << program;
22 | for (const auto& arg : args) {
23 | ss << ' ' << arg;
24 | }
25 |
26 | if (silent) {
27 | #ifdef _WIN32
28 | ss << " >nul 2>&1";
29 | #else
30 | ss << " >/dev/null 2>&1";
31 | #endif
32 | }
33 |
34 | std::cout.flush();
35 | return system(ss.str().c_str());
36 | };
37 |
38 | std::string python;
39 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) {
40 | if (run_program(option, {"--version"}, true) == 0) {
41 | python = option;
42 | break;
43 | }
44 | }
45 |
46 | if (python.empty()) {
47 | std::cerr << "Python was not found on your system. Please install Python and "
48 | "try again."
49 | << "\n";
50 | std::exit(1);
51 | }
52 |
53 | return run_program(python, {"autograder/autograder.py"});
54 | }
--------------------------------------------------------------------------------
/assignment4/autograder/autograder.py:
--------------------------------------------------------------------------------
1 | from utils import Autograder
2 |
3 | from typing import Dict, Iterable, Union
4 |
5 | from colorama import Fore, Style
6 | import difflib
7 | import os
8 | import re
9 | import subprocess
10 | import shutil
11 | import sys
12 |
13 | PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
14 | AUTOGRADER_DIR = os.path.join(PATH, "autograder")
15 | CODE_PATH = os.path.join(PATH, "spellcheck.cpp")
16 | EXAMPLES_PATH = os.path.join(PATH, "examples")
17 | EXAMPLES_GOLD_PATH = os.path.join(AUTOGRADER_DIR, "gold")
18 |
19 | # =============================================================================
20 | # Verifying source code
21 | # =============================================================================
22 |
23 |
24 | FUNCTION_MATCHERS: Dict[str, Iterable[Union[str, Iterable[str]]]] = {
25 | "tokenize": [
26 | "find_all",
27 | "std::transform",
28 | "std::inserter",
29 | "std::erase_if",
30 | "#noloops",
31 | ],
32 | "spellcheck": [
33 | ["std::ranges::views::filter", "rv::filter"],
34 | ["std::ranges::views::transform", "rv::transform"],
35 | "!std::copy_if",
36 | "!std::transform",
37 | "levenshtein",
38 | "#noloops",
39 | ],
40 | }
41 |
42 |
43 | def remove_comments_strings(content):
44 | comment_pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)"
45 | comment_regex = re.compile(comment_pattern, re.MULTILINE | re.DOTALL)
46 | string_pattern = r"\".*?\""
47 | string_regex = re.compile(string_pattern, re.MULTILINE | re.DOTALL)
48 |
49 | content = comment_regex.sub(
50 | lambda match: "" if match.group(2) is not None else match.group(1), content
51 | )
52 | content = string_regex.sub("", content)
53 |
54 | return content
55 |
56 |
57 | def tokenize_source(input_code: str) -> Iterable[str]:
58 | tokens = []
59 |
60 | pattern_fqn = re.compile(r"^(::)?[a-zA-Z_][a-zA-Z0-9_]*(::[a-zA-Z_][a-zA-Z0-9_]*)*")
61 | pattern_non_word = re.compile(r"^\W+")
62 |
63 | while input_code:
64 | fqn_match = pattern_fqn.match(input_code)
65 | if fqn_match:
66 | tokens.append(fqn_match.group().strip())
67 | input_code = input_code[len(fqn_match.group()) :]
68 | else:
69 | non_word_match = pattern_non_word.match(input_code)
70 | if non_word_match:
71 | tokens.append(non_word_match.group().strip())
72 | input_code = input_code[len(non_word_match.group()) :]
73 | else:
74 | tokens.append(input_code[0])
75 | input_code = input_code[1:]
76 |
77 | return [t for t in tokens if t]
78 |
79 |
80 | def parse_methods(file_path):
81 | with open(file_path, "r") as file:
82 | content = file.read()
83 | content = remove_comments_strings(content)
84 |
85 | method_pattern = re.compile(r"\b(\w+)\s*\([^)]*\)\s*\{")
86 | methods = {}
87 | pos = 0
88 |
89 | while True:
90 | match = method_pattern.search(content, pos)
91 | if not match:
92 | break
93 |
94 | method_name = match.group(1)
95 | if method_name in methods:
96 | raise RuntimeError(
97 | f"Duplicate method definition of '{method_name}'. Have you tried recompiling your code?"
98 | )
99 |
100 | start_idx = match.end() - 1
101 | brace_stack = 1
102 | end_idx = start_idx + 1
103 |
104 | while brace_stack > 0:
105 | if end_idx >= len(content):
106 | raise RuntimeError(
107 | f"Unmatched braces in method definition of '{method_name}'. Have you tried recompiling your code?"
108 | )
109 | if content[end_idx] == "{":
110 | brace_stack += 1
111 | elif content[end_idx] == "}":
112 | brace_stack -= 1
113 | end_idx += 1
114 |
115 | method_body = content[start_idx + 1 : end_idx - 1].strip()
116 | methods[method_name] = tokenize_source(method_body)
117 | pos = end_idx
118 |
119 | return methods
120 |
121 |
122 | def add_matcher_tests(grader: Autograder, file: str):
123 | student_methods = parse_methods(file)
124 | for method, matchers in FUNCTION_MATCHERS.items():
125 |
126 | def generate_test_method(method_copy, matchers_copy):
127 | def test():
128 | if method_copy not in student_methods:
129 | raise RuntimeError(
130 | f"Could not find a definition for required method '{method_copy}' in {file}"
131 | )
132 |
133 | method_body = student_methods[method_copy]
134 |
135 | for matcher in matchers_copy:
136 | if matcher == "#noloops":
137 | for loop_type in ["for", "while", "goto"]:
138 | if loop_type in method_body:
139 | raise RuntimeError(
140 | f"Method {method_copy} may not contain any explicit for/while loops! You must use the STL instead! Found loop: {loop_type}"
141 | )
142 | print(f"🔎 {method_copy} has no for/while loops!")
143 | continue
144 |
145 | if isinstance(matcher, str):
146 | matcher = [matcher]
147 |
148 | for m in matcher:
149 | if m.startswith("!"):
150 | m = m[1:]
151 | if m in method_body:
152 | raise RuntimeError(f"Method '{method_copy}' is not allowed to call method: {m}")
153 | elif m in method_body:
154 | print(f"🔎 {method_copy} called method {m}")
155 | break
156 | else:
157 | if not any(m.startswith("!") for m in matcher):
158 | raise RuntimeError(
159 | f"Method '{method_copy}' must call one of the following methods: {matcher}."
160 | )
161 |
162 | return test
163 |
164 | grader.add_part(method, generate_test_method(method, matchers))
165 |
166 | # Ensure no helper function were used
167 | def test_no_helper_functions():
168 | present = set(student_methods.keys())
169 | expected = set(FUNCTION_MATCHERS.keys())
170 | extra = present - expected
171 | if extra:
172 | raise RuntimeError(
173 | f"You may not use any helper functions for this assignment. You must implement all your code in the following functions: {', '.join(expected)}. \n\nFound extra functions: {', '.join(extra)}"
174 | )
175 |
176 | grader.add_part(
177 | "Check submission has no helper functions", test_no_helper_functions
178 | )
179 |
180 |
181 | def no_obvious_namespace_std():
182 | with open(CODE_PATH, "r") as file:
183 | content = file.read()
184 | content = remove_comments_strings(content)
185 | content = content.replace("\n", " ")
186 | using_namespace = re.compile(r"using\s+namespace\s+std\s*;")
187 | if using_namespace.search(content):
188 | raise RuntimeError(
189 | "You should not use 'using namespace std;' for this assignment. In general, this is considered bad practice as it can lead to naming conflicts, and will affect the autograder for this assignment."
190 | )
191 |
192 |
193 | # =============================================================================
194 | # Verifying program correctness
195 | # =============================================================================
196 |
197 |
198 | import os
199 | import subprocess
200 |
201 |
202 | def find_executable(containing_dir):
203 | # Search for the executable in the given directory
204 | for filename in ("main", "main.exe"):
205 | exe_path = os.path.join(containing_dir, filename)
206 | if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK):
207 | return exe_path
208 | raise FileNotFoundError(
209 | f"No executable named 'main' or 'main.exe' found in '{containing_dir}'."
210 | )
211 |
212 |
213 | def spellcheck(file_path):
214 | exe_path = find_executable(PATH)
215 | command = [exe_path, "--stdin", "--unstyled"]
216 |
217 | with open(file_path, "r", encoding="utf-8") as file:
218 | result = subprocess.run(
219 | command,
220 | stdin=file,
221 | stdout=subprocess.PIPE,
222 | stderr=subprocess.STDOUT,
223 | text=True,
224 | encoding="utf-8"
225 | )
226 |
227 | return result.stdout
228 |
229 |
230 | def generate_gold_dir():
231 | if os.path.exists(EXAMPLES_GOLD_PATH):
232 | shutil.rmtree(EXAMPLES_GOLD_PATH)
233 | os.makedirs(EXAMPLES_GOLD_PATH)
234 |
235 | for example_file in os.listdir(EXAMPLES_PATH):
236 | example_file_path = os.path.join(EXAMPLES_PATH, example_file)
237 | if not os.path.isfile(example_file_path):
238 | continue
239 | try:
240 | output = spellcheck(example_file_path)
241 | gold_file_path = os.path.join(EXAMPLES_GOLD_PATH, example_file)
242 | with open(gold_file_path, "w", encoding="utf-8") as gold_file:
243 | gold_file.write(output)
244 |
245 | print(f"Processed {example_file} -> {gold_file_path}")
246 | except Exception as e:
247 | print(f"Failed to process {example_file}: {e}")
248 |
249 |
250 | def assert_contents_equal(expected, actual, filename):
251 | if expected != actual:
252 | expected_lines = expected.split("\n")
253 | actual_lines = actual.split("\n")
254 |
255 | diff = list(
256 | difflib.unified_diff(
257 | expected_lines,
258 | actual_lines,
259 | fromfile="Expected in solution, missing in your output",
260 | tofile="Present in your output, missing in solution",
261 | )
262 | )
263 |
264 | diff = [f"\t{l}" for l in diff]
265 | diff[0] = diff[0].rstrip()
266 | diff_output = "\n".join(diff)
267 |
268 | def matcher(fore):
269 | return (
270 | lambda match: f"{fore}{Style.BRIGHT}{match.group(0)}{Style.RESET_ALL}"
271 | )
272 |
273 | diff_output = re.sub(
274 | r"^\s*-+", matcher(Fore.RED), diff_output, flags=re.MULTILINE
275 | )
276 | diff_output = re.sub(
277 | r"^\s*\++", matcher(Fore.GREEN), diff_output, flags=re.MULTILINE
278 | )
279 |
280 | error_lines = [
281 | f"Contents do not match solution:",
282 | diff_output,
283 | "",
284 | f"\t{Fore.CYAN}To see the output of your submission on this file, run:",
285 | "",
286 | f'\t\t ./main --stdin < "examples/{filename}"',
287 | "",
288 | f'\tTo see the expected solution output, open "autograder/gold/{filename}"{Fore.RESET}',
289 | ]
290 |
291 | raise RuntimeError("\n".join(error_lines))
292 |
293 |
294 | def test_spellcheck():
295 | for example_file in os.listdir(EXAMPLES_GOLD_PATH):
296 | gold_path = os.path.join(EXAMPLES_GOLD_PATH, example_file)
297 | input_path = os.path.join(EXAMPLES_PATH, example_file)
298 |
299 | if not os.path.isfile(input_path):
300 | raise RuntimeError(
301 | f"Could not find gold file for example '{example_file}'. Did you modify the examples/ directory?"
302 | )
303 |
304 | with open(gold_path, "r", encoding="utf-8") as f:
305 | gold_output = f.read()
306 |
307 | spellcheck_result = spellcheck(input_path)
308 |
309 | assert_contents_equal(gold_output, spellcheck_result, example_file)
310 | print(f"🔎 {example_file} spellcheck matched solution!")
311 |
312 |
313 | # =============================================================================
314 | # Autograder setup
315 | # =============================================================================
316 |
317 | if __name__ == "__main__":
318 | if "--gold" in sys.argv:
319 | generate_gold_dir()
320 | sys.exit(0)
321 |
322 | grader = Autograder()
323 | grader.setup = no_obvious_namespace_std
324 | add_matcher_tests(grader, CODE_PATH)
325 | grader.add_part("Spellcheck", test_spellcheck)
326 | grader.run()
327 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/(kafka).txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 121 tokens.
3 |
4 | Someone must have slandered Josef K., for one morning, without having
5 | done anything wrong, he was arrested.
6 |
7 | The cook, who always <> him his breakfast at eight o’clock, did
8 | not come this time. That had never happened before. For a moment he
9 | lay still, looking at the pillow and the old woman who lived opposite
10 | and who was watching him with an inquisitiveness quite unusual for
11 | her. Then, with <>, he noticed that a man he had never seen
12 | before was in his room. He was wearing a tight black suit which,
13 | with its various <>, looked like a travelling outfit that had
14 | gone out of fashion years ago.
15 |
16 | “What do you want?” asked K., raising himself half up in bed.
17 |
18 | broght: {bright, brocht, brogh, brought}
19 | astonshment: {astonishment}
20 | trimings: {primings, timings, trimmings}
21 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/(marquez).txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 84 tokens.
3 |
4 | Many years later, as he faced the firing squad, Colonel <> Buendía
5 | was to remember that distant afternoon when his father took him to discover ice.
6 |
7 | At that time Macondo was a <> of twenty adobe houses, built on the bank
8 | of a river of clear water that ran along a bed of <> stones, which were
9 | white and enormous, like prehistoric eggs. The world was so recent that many
10 | things lacked names, and in order to indicate them it was <> to point.
11 |
12 | Aureliano: {aurelian}
13 | vilage: {milage, pilage, silage, viage, village, vinage, visage, volage}
14 | pollished: {polished}
15 | necesary: {necessary}
16 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/(morrison).txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 128 tokens.
3 |
4 | 124 WAS SPITEFUL. Full of a <> venom. The women in the house knew
5 | it and so did the children. For years each put up with the spite in his
6 | own way, but by 1873 <> and her daughter Denver were its only victims.
7 | The grandmother, Baby <>, was dead, and the sons, Howard and <>,
8 | had run away by the time they were thirteen years old--as soon as merely
9 | looking in a mirror shattered it (that was the signal for <>); as soon
10 | as two tiny hand prints appeared in the cake (that was it for Howard). Neither
11 | boy waited to see more; another kettleful of chickpeas smoking in a heap on
12 | the floor; soda crackers crumbled and strewn in a line next to the door sill.
13 |
14 | baby's: {babe's}
15 | Sethe: {bethe, ethe, lethe, rethe, seathe, seethe, setae, seth, sethi, sithe, smethe}
16 | Suggs: {muggs, sugg, sughs, sugis, vuggs}
17 | Buglar: {bugler, burglar, juglar}
18 | Buglar: {bugler, burglar, juglar}
19 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/gibberish.txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 22 tokens.
3 |
4 | This is a bunch of gibberish:
5 |
6 | ansdka nakdlsnakln lnfgklanrf nksladnksal nkglrnkadf nklsadn
7 |
8 | These are definitely misspelled, but probably don't have any suggestions.
9 |
10 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/melville.txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 56 tokens.
3 |
4 | Call me Ishmael. Some years ago—never mind how long precisely—having little
5 | or no money in my purse, and nothing particular to interest me on shore,
6 | I thought I would sail about a little and see the watery part of the world.
7 | It is a way I have of driving off the spleen and regulating the circulation.
8 |
9 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/orwell.txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 55 tokens.
3 |
4 | It was a bright cold day in April, and the clocks were striking thirteen.
5 | Winston Smith, his chin nuzzled into his breast in an effort to escape the vile wind,
6 | slipped quickly through the glass doors of Victory Mansions, though not quickly
7 | enough to prevent a swirl of gritty dust from entering along with him.
8 |
9 |
--------------------------------------------------------------------------------
/assignment4/autograder/gold/tolstoy.txt:
--------------------------------------------------------------------------------
1 | Loading dictionary... loaded 464811 unique words.
2 | Tokenizing input... got 66 tokens.
3 |
4 | All happy families are alike; each unhappy family is unhappy in its own way.
5 |
6 | Everything was in confusion in the Oblonskys' house. The wife had discovered
7 | that the husband was carrying on an intrigue with a French girl who had been
8 | a governess in their family, and she had announced to her husband that she
9 | could not go on living in the same house with him.
10 |
11 |
--------------------------------------------------------------------------------
/assignment4/autograder/utils.hpp:
--------------------------------------------------------------------------------
1 | /* ========================================================================= *
2 | * Don't worry about anything beyond this point. *
3 | * (unless you are really curious and want to!) *
4 | * ========================================================================= */
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | int run_autograder() {
14 | auto run_program = [](std::string program, std::initializer_list args,
15 | bool silent = false) {
16 | std::stringstream ss;
17 |
18 | ss << program;
19 | for (const auto& arg : args) {
20 | ss << ' ' << arg;
21 | }
22 |
23 | if (silent) {
24 | #ifdef _WIN32
25 | ss << " >nul 2>&1";
26 | #else
27 | ss << " >/dev/null 2>&1";
28 | #endif
29 | }
30 |
31 | std::cout.flush();
32 | return system(ss.str().c_str());
33 | };
34 |
35 | std::string python;
36 | for (const auto& option : {"python", "python3", "/usr/bin/python3", "/usr/bin/python"}) {
37 | if (run_program(option, {"--version"}, true) == 0) {
38 | python = option;
39 | break;
40 | }
41 | }
42 |
43 | if (python.empty()) {
44 | std::cerr << "Python was not found on your system. Please install Python and "
45 | "try again."
46 | << "\n";
47 | std::exit(1);
48 | }
49 |
50 | return run_program(python, {"autograder/autograder.py"});
51 | }
52 |
53 | namespace ansi {
54 |
55 | const int style_idx = std::ios_base::xalloc();
56 |
57 | template
58 | constexpr std::basic_ostream& styled(std::basic_ostream& os) {
59 | os.iword(style_idx) = 1;
60 | return os;
61 | }
62 |
63 | template
64 | constexpr std::basic_ostream& unstyled(std::basic_ostream& os) {
65 | os.iword(style_idx) = 0;
66 | return os;
67 | }
68 |
69 | #define make_style(NAME, VALUE) \
70 | template \
71 | constexpr std::basic_ostream& NAME(std::basic_ostream& os) { \
72 | if (os.iword(style_idx) > 0) \
73 | os << VALUE; \
74 | return os; \
75 | }
76 |
77 | make_style(reset, "\033[0m\e[0m");
78 | make_style(fg_red, "\033[31m");
79 | make_style(fg_lightred, "\033[91m");
80 | make_style(fg_green, "\033[92m");
81 | make_style(fg_gray, "\033[90m");
82 | make_style(bg_yellow, "\e[43m");
83 |
84 | } // namespace ansi
85 |
86 | std::string read_stream(std::istream& is) {
87 | std::istreambuf_iterator begin(is), end;
88 | return std::string(begin, end);
89 | }
90 |
91 | struct TimerResult {
92 | std::string name;
93 | size_t trials;
94 | std::chrono::nanoseconds ns;
95 | };
96 |
97 | class TimerSummary {
98 | public:
99 | TimerSummary() : trial_noun{"trial"}, enabled{false} {}
100 | void add(const TimerResult& result) { results.push_back(result); }
101 | void set_trial_noun(const std::string& trial_noun) { this->trial_noun = trial_noun; }
102 |
103 | TimerSummary& operator=(const TimerSummary&) = delete;
104 | ~TimerSummary() {
105 | if (!enabled)
106 | return;
107 |
108 | std::cout << "\n";
109 | std::cout << ansi::bg_yellow << std::left << std::setw(120) << "Timing Results:" << ansi::reset << '\n';
110 | std::cout << ansi::fg_gray;
111 | for (const auto& [name, trials, ns] : results) {
112 | std::cout << " · " << name << " took ";
113 | format_time(ns);
114 | if (trials > 1) {
115 | std::cout << ", averaging ";
116 | format_time(ns / trials);
117 | std::cout << " per " << trial_noun;
118 | }
119 | std::cout << " (" << trials << " " << trial_noun;
120 | if (trials != 1) std::cout << "s";
121 | std::cout << ")\n";
122 | }
123 | std::cout << ansi::reset;
124 | }
125 |
126 | void enable() { enabled = true; }
127 | void disable() { enabled = false; }
128 |
129 | private:
130 | bool enabled;
131 | std::string trial_noun;
132 | std::vector results;
133 |
134 | void format_time(const std::chrono::nanoseconds& ns) {
135 | using namespace std::chrono;
136 |
137 | if (ns < 10us) {
138 | std::cout << ns.count() << "ns";
139 | } else if (ns < 1s) {
140 | std::cout << std::fixed << std::setprecision(2)
141 | << duration_cast(ns).count() / 1000.0 << "ms" << std::defaultfloat;
142 | } else {
143 | std::cout << std::fixed << std::setprecision(3)
144 | << duration_cast(ns).count() / 1000.0 << "s" << std::defaultfloat;
145 | }
146 | }
147 | };
148 |
149 | class Timer {
150 | public:
151 | Timer(TimerSummary& summary, const std::string& name, size_t trials = 1)
152 | : summary{summary}, name{name}, trials{trials},
153 | start{std::chrono::high_resolution_clock::now()}, stopped{false} {}
154 |
155 | ~Timer() {
156 | stop();
157 | auto ns = std::chrono::duration_cast(end - start);
158 | summary.add({name, trials, ns});
159 | }
160 |
161 | Timer& operator=(const Timer&) = delete;
162 |
163 | void set_trials(size_t trials) { this->trials = trials; }
164 |
165 | void stop() {
166 | if (stopped)
167 | return;
168 | stopped = true;
169 | end = std::chrono::high_resolution_clock::now();
170 | }
171 |
172 | private:
173 | std::string name;
174 | size_t trials;
175 | std::chrono::high_resolution_clock::time_point start;
176 | std::chrono::high_resolution_clock::time_point end;
177 | bool stopped;
178 |
179 | TimerSummary& summary;
180 | };
181 |
--------------------------------------------------------------------------------
/assignment4/docs/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment4/docs/header.png
--------------------------------------------------------------------------------
/assignment4/docs/mispelled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment4/docs/mispelled.png
--------------------------------------------------------------------------------
/assignment4/docs/spellcheck.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment4/docs/spellcheck.png
--------------------------------------------------------------------------------
/assignment4/examples/(kafka).txt:
--------------------------------------------------------------------------------
1 | Someone must have slandered Josef K., for one morning, without having
2 | done anything wrong, he was arrested.
3 |
4 | The cook, who always broght him his breakfast at eight o’clock, did
5 | not come this time. That had never happened before. For a moment he
6 | lay still, looking at the pillow and the old woman who lived opposite
7 | and who was watching him with an inquisitiveness quite unusual for
8 | her. Then, with astonshment, he noticed that a man he had never seen
9 | before was in his room. He was wearing a tight black suit which,
10 | with its various trimings, looked like a travelling outfit that had
11 | gone out of fashion years ago.
12 |
13 | “What do you want?” asked K., raising himself half up in bed.
--------------------------------------------------------------------------------
/assignment4/examples/(marquez).txt:
--------------------------------------------------------------------------------
1 | Many years later, as he faced the firing squad, Colonel Aureliano Buendía
2 | was to remember that distant afternoon when his father took him to discover ice.
3 |
4 | At that time Macondo was a vilage of twenty adobe houses, built on the bank
5 | of a river of clear water that ran along a bed of pollished stones, which were
6 | white and enormous, like prehistoric eggs. The world was so recent that many
7 | things lacked names, and in order to indicate them it was necesary to point.
--------------------------------------------------------------------------------
/assignment4/examples/(morrison).txt:
--------------------------------------------------------------------------------
1 | 124 WAS SPITEFUL. Full of a baby's venom. The women in the house knew
2 | it and so did the children. For years each put up with the spite in his
3 | own way, but by 1873 Sethe and her daughter Denver were its only victims.
4 | The grandmother, Baby Suggs, was dead, and the sons, Howard and Buglar,
5 | had run away by the time they were thirteen years old--as soon as merely
6 | looking in a mirror shattered it (that was the signal for Buglar); as soon
7 | as two tiny hand prints appeared in the cake (that was it for Howard). Neither
8 | boy waited to see more; another kettleful of chickpeas smoking in a heap on
9 | the floor; soda crackers crumbled and strewn in a line next to the door sill.
--------------------------------------------------------------------------------
/assignment4/examples/gibberish.txt:
--------------------------------------------------------------------------------
1 | This is a bunch of gibberish:
2 |
3 | ansdka nakdlsnakln lnfgklanrf nksladnksal nkglrnkadf nklsadn
4 |
5 | These are definitely misspelled, but probably don't have any suggestions.
--------------------------------------------------------------------------------
/assignment4/examples/melville.txt:
--------------------------------------------------------------------------------
1 | Call me Ishmael. Some years ago—never mind how long precisely—having little
2 | or no money in my purse, and nothing particular to interest me on shore,
3 | I thought I would sail about a little and see the watery part of the world.
4 | It is a way I have of driving off the spleen and regulating the circulation.
--------------------------------------------------------------------------------
/assignment4/examples/orwell.txt:
--------------------------------------------------------------------------------
1 | It was a bright cold day in April, and the clocks were striking thirteen.
2 | Winston Smith, his chin nuzzled into his breast in an effort to escape the vile wind,
3 | slipped quickly through the glass doors of Victory Mansions, though not quickly
4 | enough to prevent a swirl of gritty dust from entering along with him.
--------------------------------------------------------------------------------
/assignment4/examples/tolstoy.txt:
--------------------------------------------------------------------------------
1 | All happy families are alike; each unhappy family is unhappy in its own way.
2 |
3 | Everything was in confusion in the Oblonskys' house. The wife had discovered
4 | that the husband was carrying on an intrigue with a French girl who had been
5 | a governess in their family, and she had announced to her husband that she
6 | could not go on living in the same house with him.
--------------------------------------------------------------------------------
/assignment4/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "autograder/utils.hpp"
11 | #include "spellcheck.h"
12 |
13 | void print_output(const std::string& source, const std::set& Misspellings) {
14 | std::string_view sv(source);
15 | size_t last_ofs = 0;
16 | for (const auto& Misspelling : Misspellings) {
17 | // Print text before the Misspelling
18 | std::cout << sv.substr(last_ofs, Misspelling.token.src_offset - last_ofs);
19 |
20 | std::cout << ansi::fg_red << "<<";
21 | std::cout << sv.substr(Misspelling.token.src_offset, Misspelling.token.content.size());
22 | std::cout << ">>" << ansi::reset;
23 | last_ofs = Misspelling.token.src_offset + Misspelling.token.content.size();
24 | }
25 |
26 | std::cout << sv.substr(last_ofs) << "\n\n";
27 |
28 | for (const auto& Misspelling : Misspellings) {
29 | std::cout << ansi::fg_red;
30 | std::cout << sv.substr(Misspelling.token.src_offset, Misspelling.token.content.size());
31 | std::cout << ansi::reset;
32 |
33 | std::cout << ": {";
34 |
35 | bool first = true;
36 | for (const auto& suggestion : Misspelling.suggestions) {
37 | if (!first)
38 | std::cout << ", ";
39 | std::cout << suggestion;
40 | first = false;
41 | }
42 |
43 | std::cout << "}\n";
44 | }
45 | }
46 |
47 | void print_success() {
48 | std::vector messages = {
49 | "You're a spelling wizard! If words had a kingdom, you'd rule it.",
50 | "Are you secretly a dictionary in disguise? Impressive!",
51 | "If spelling were an Olympic sport, you'd have gold by now.",
52 | "Your spelling skills are so good, even autocorrect is taking notes.",
53 | "The spelling bee champions fear you. Rightfully so.",
54 | "Your grasp of spelling is tighter than a jar lid no one can open.",
55 | "I checked the dictionary, and your name is under 'spelling genius.'",
56 | "Shakespeare just sent a 'Well done!' from the beyond.",
57 | "You spell so well, even Scrabble pieces arrange themselves for you.",
58 | "Your spelling game is so strong, spellcheck just quit its job."};
59 |
60 | std::random_device rd;
61 | std::mt19937 gen(rd());
62 | std::uniform_int_distribution dist(0, messages.size() - 1);
63 |
64 | std::cout << ansi::fg_green << messages[dist(gen)] << ansi::reset << std::endl;
65 | }
66 |
67 | int main(int argc, char** argv) {
68 | if (argc == 1) {
69 | return run_autograder();
70 | }
71 |
72 | bool read_stdin = false;
73 | std::string input;
74 | std::string dictionary_file = "words.txt";
75 | bool styled = true;
76 |
77 | TimerSummary summary;
78 | summary.set_trial_noun("token");
79 |
80 | for (int i = 1; i < argc; ++i) {
81 | std::string arg = argv[i];
82 | if (arg == "--dict" && i + 1 < argc) {
83 | dictionary_file = argv[++i];
84 | } else if (arg == "--unstyled") {
85 | styled = false;
86 | } else if (arg == "--stdin") {
87 | read_stdin = true;
88 | } else if (arg == "--profile") {
89 | summary.enable();
90 | } else {
91 | input += argv[i];
92 | }
93 | }
94 |
95 | if (styled)
96 | std::cout << ansi::styled;
97 | else
98 | std::cout << ansi::unstyled;
99 |
100 | if (read_stdin)
101 | input += read_stream(std::cin);
102 |
103 | std::ifstream dict_stream(dictionary_file);
104 | if (!dict_stream.is_open()) {
105 | std::cerr << "Failed to open dict file '" << dictionary_file << "'" << std::endl;
106 | return EXIT_FAILURE;
107 | }
108 |
109 | std::cout << ansi::fg_gray << "Loading dictionary... ";
110 |
111 | std::string dict_contents = read_stream(dict_stream);
112 |
113 | Timer tokenize_dict_timer { summary, "Tokenizing dictionary" };
114 | Corpus dictionary_tokens = tokenize(dict_contents);
115 | tokenize_dict_timer.stop();
116 | tokenize_dict_timer.set_trials(dictionary_tokens.size());
117 |
118 | Dictionary dictionary;
119 | std::for_each(dictionary_tokens.begin(), dictionary_tokens.end(),
120 | [&](const Token& t) { dictionary.insert(t.content); });
121 |
122 | std::cout << "loaded " << dictionary.size() << " unique words." << std::endl;
123 | std::cout << "Tokenizing input... ";
124 |
125 | Timer tokenize_source_timer { summary, "Tokenizing input"};
126 | Corpus source = tokenize(input);
127 | tokenize_source_timer.stop();
128 | tokenize_source_timer.set_trials(source.size());
129 |
130 | std::cout << "got " << source.size() << " tokens." << ansi::reset << "\n\n";
131 |
132 | Timer spellcheck_timer { summary, "Spellcheck", source.size() };
133 | std::set Misspellings = spellcheck(source, dictionary);
134 | spellcheck_timer.stop();
135 |
136 | print_output(input, Misspellings);
137 |
138 | if (styled && !dictionary.empty() && Misspellings.empty())
139 | print_success();
140 |
141 | return Misspellings.empty() ? 0 : EXIT_FAILURE;
142 | }
--------------------------------------------------------------------------------
/assignment4/spellcheck.cpp:
--------------------------------------------------------------------------------
1 | #include "spellcheck.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | template
11 | std::vector find_all(Iterator begin, Iterator end, UnaryPred pred);
12 |
13 | Corpus tokenize(std::string& source) {
14 | /* TODO: Implement this method */
15 | return Corpus();
16 | }
17 |
18 | std::set spellcheck(const Corpus& source, const Dictionary& dictionary) {
19 | /* TODO: Implement this method */
20 | return std::set();
21 | };
22 |
23 | /* Helper methods */
24 |
25 | #include "utils.cpp"
--------------------------------------------------------------------------------
/assignment4/spellcheck.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | struct Token {
8 |
9 | std::string content;
10 | size_t src_offset;
11 |
12 | template
13 | Token(std::string& source, It begin, It end)
14 | : src_offset{static_cast(std::distance(source.begin(), begin))},
15 | content{std::string(begin, end)} {
16 | clean(source);
17 | }
18 |
19 | private:
20 | void clean(const std::string& source);
21 | };
22 |
23 | struct Misspelling {
24 | Token token;
25 | std::set suggestions;
26 | };
27 |
28 | using Corpus = std::set;
29 | using Dictionary = std::unordered_set;
30 |
31 | Corpus tokenize(std::string& input);
32 | std::set spellcheck(const Corpus& source, const Dictionary& dictionary);
33 |
34 | /* Helper methods */
35 |
36 | size_t levenshtein(const std::string&, const std::string&);
37 | bool operator<(const Token&, const Token&);
38 | bool operator<(const Misspelling&, const Misspelling&);
--------------------------------------------------------------------------------
/assignment4/utils.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | bool operator<(const Token& a, const Token& b) {
6 | return std::tie(a.src_offset, a.content) < std::tie(b.src_offset, b.content);
7 | }
8 |
9 | bool operator<(const Misspelling& a, const Misspelling& b) { return a.token < b.token; }
10 |
11 | template
12 | std::vector find_all(Iterator begin, Iterator end, UnaryPred pred) {
13 | std::vector its{begin};
14 | for (auto it = begin; it != end; ++it) {
15 | if (pred(*it))
16 | its.push_back(it);
17 | }
18 | its.push_back(end);
19 | return its;
20 | }
21 |
22 | void Token::clean(const std::string& source) {
23 | auto begin = source.begin() + src_offset;
24 | auto end = source.begin() + src_offset + content.size();
25 |
26 | auto left = std::find_if(begin, end, ::isalnum);
27 | auto right =
28 | std::find_if(std::make_reverse_iterator(end), std::make_reverse_iterator(begin), ::isalnum)
29 | .base();
30 | src_offset += std::distance(begin, left);
31 |
32 | if (left < right) {
33 | content = std::string(left, right);
34 | } else {
35 | content = "";
36 | }
37 |
38 | std::transform(content.begin(), content.end(), content.begin(), ::tolower);
39 | }
40 |
41 | /* ========================================================================= *
42 | * Damerau-Levenshtein Distance Algorithm *
43 | * *
44 | * Taken from: *
45 | * https://github.com/sp1ff/damerau-levenshtein *
46 | * ========================================================================= */
47 |
48 | size_t levenshtein(const std::string& s1, const std::string& s2) {
49 | /* If size differences are extreme, then we know D-L exceeds 1 */
50 | size_t diff = std::abs((ptrdiff_t)s1.size() - (ptrdiff_t)s2.size());
51 | if (diff > 1)
52 | return diff;
53 |
54 | /* If strings have same size, we'll count mismatches and potentially early exit */
55 | if (diff == 0) {
56 | size_t mismatches = std::inner_product(s1.begin(), s1.end(), s2.begin(), 0UL, std::plus<>(),
57 | std::not_equal_to<>());
58 | if (mismatches <= 1)
59 | return mismatches;
60 | }
61 |
62 | /* Otherwise we run an optimized DP D-L algorithm.
63 | * This will early exit if the min possible D-L distance exceeds 1 */
64 | const std::size_t l1 = s1.size(), l2 = s2.size();
65 | std::vector col(l2 + 1), prevCol(l2 + 1);
66 | for (size_t i = 0; i < prevCol.size(); i++)
67 | prevCol[i] = i;
68 | for (size_t i = 0; i < l1; i++) {
69 | col[0] = i + 1;
70 | size_t row_min = col[0];
71 | for (size_t j = 0; j < l2; j++) {
72 | col[j + 1] =
73 | std::min({prevCol[1 + j] + 1, col[j] + 1, prevCol[j] + (s1[i] == s2[j] ? 0 : 1)});
74 | row_min = std::min(row_min, col[j + 1]);
75 | }
76 | if (row_min > 1)
77 | return row_min;
78 | col.swap(prevCol);
79 | }
80 |
81 | return prevCol[l2];
82 | }
--------------------------------------------------------------------------------
/assignment5/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Assignment 5: Treebook
6 |
7 | Due Friday, May 23rd at 11:59PM
8 |
9 | ## Overview
10 |
11 | The newest Stanford social media startup is Treebook, and you’re a founding member of the team! To get the product off the ground and compete with an unnamed, completely legally unaffiliated app from Harvard, you’ve been assigned the task of implementing user profiles.
12 |
13 | For this assignment, you will be implementing parts of a class to allow for operator overloads, as well as to modify some aspects of the special member functions.
14 |
15 | There are two files you'll work with for this assignment:
16 |
17 | * `user.h` - Contains the declaration for the `User` class that you will extend with special member functions and operators.
18 | * `user.cpp` - Contains the definition of the `User` class.
19 |
20 | To download the starter code for this assignment, please see the instructions for [**Getting Started**](../README.md#getting-started) on the course assignments repository.
21 |
22 | ## Running your code
23 |
24 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit Ctrl+\` or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assign5/` directory and run:
25 |
26 | ```sh
27 | g++ -std=c++20 main.cpp user.cpp -o main
28 | ```
29 |
30 | Assuming that your code compiles without any compiler errors, you can now do:
31 |
32 | ```sh
33 | ./main
34 | ```
35 |
36 | which will actually run the `main` function in `main.cpp`.
37 |
38 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track!
39 |
40 | > [!NOTE]
41 | >
42 | > ### Note for Windows
43 | >
44 | > On Windows, you may need to compile your code using
45 | >
46 | > ```sh
47 | > g++ -static-libstdc++ -std=c++20 main.cpp user.cpp -o main
48 | > ```
49 | >
50 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with:
51 | >
52 | > ```sh
53 | > ./main.exe
54 | > ```
55 |
56 | ## Part 1: Viewing Profiles
57 |
58 | Take a look at the `user.h` header file. Your coworkers have begun to write a `User` class that will store the name and friends list of each user who joins your social media platform! In order to keep this class super efficient, they have chosen to represent the list of friends as a raw pointer array of `std::string` (kind of like how a `std::vector` stores its elements behind the scenes). Thankfully, they have already written logic for creating a new `User` and for adding friends to an existing `User`'s friend list (`add_friend`), but they've begun to experience some strange issues when working with `User` objects.
59 |
60 | To begin with, there's no easy way to print information about each `User` object to the console, which has made debugging at Treebook difficult. To help your coworkers out, write an `operator<<` method that prints a `User` to a `std::ostream`. **This operator should be declared as a friend function in `user.h` and implemented in `user.cpp`.** For example, a user named `"Alice"` with friends `"Bob"` and `"Charlie"` should give the following output when printed to the console:
61 |
62 | ```
63 | User(name=Alice, friends=[Bob, Charlie])
64 | ```
65 |
66 | Note: `operator<<` should not print any newline characters.
67 |
68 | > [!IMPORTANT]
69 | > In your implementation of `operator<<`, you will need to access and loop through the `_friends` private field of the `User` class in order to print out a user's friends. Normally, you cannot access private fields inside of a class in a non-member function—in this case, we can get around this restriction by marking `operator<<` as a **friend function inside of the `User` class.** See the slides for Tuesday's lecture for more information!
70 |
71 | ## Part 2: Unfriendly Behaviour
72 |
73 | With the help of your `operator<<`, your coworkers have been able to make good progress on the social media app. However, they can't quite wrap their head around some seemingly bizzare issues that occur when they try to make copies of `User` objects in memory. Having recently taken CS106L, you suspect that it might have something to do with the special member functions (or the lack thereof) on the `User` class. To fix this issue, we'll implement our own versions of the special member functions (SMFs) for the `User` class, and remove some of the others for which the compiler generated versions are insufficient.
74 |
75 | To be specific, you will need to:
76 |
77 | 1. Implement a destructor for the `User` class. To do so, implement the `~User()` SMF.
78 | 2. Make the `User` class copy constructible. To do so, implement the `User(const User& user)` SMF.
79 | 3. Make the `User` class copy assignable. To do so, implement the `User& operator=(const User& user)` SMF.
80 | 4. Prevent the `User` class from being move constructed. To do so, delete the `User(User&& user)` SMF.
81 | 5. Prevent the `User` class from being move assigned. To do so, delete the `User& operator=(User&& user)` SMF.
82 |
83 | In performing these tasks, you are expected to make changes to **both** the `user.h` and `user.cpp` files.
84 |
85 | > [!IMPORTANT]
86 | > In your implementations of points 2 and 3 above, you will need to copy the contents of the `_friends` array. Recall from Thursday's lecture on special member functions that you can copy a pointer array by first allocating memory for a new one (possibly within a member initializer list), and then copying over the elements with a for loop.
87 | > Make sure that you also set the `_size`, `_capacity`, and `_name` of the instance you are changing as well!
88 |
89 | ## Part 3: Always Be Friending
90 |
91 | After making changes to the special member functions, you've been able to scale out Treebook across Stanford and word has started to spread at other universities! However, your coworkers and you have found that some common use cases for the `User` class are either inconvenient or impossible given how the class is currently written, and you think you might be able to fix this by implementing some custom operators.
92 |
93 | You will overload two operators for the `User` class. **Please implement both of these operators as member functions** (i.e. declare them inside of the `User` class in `user.h` and provide implementations in `user.cpp`).
94 |
95 | ### `operator+=`
96 |
97 | The `+=` operator will representing adding a user to another user's friend list. This should be symmetric, meaning that adding, for example, Charlie to Alice's friend list should cause Alice to also be in Charlie's list. For example, consider this code:
98 |
99 | ```cpp
100 | User alice("Alice");
101 | User charlie("Charlie");
102 |
103 | alice += charlie;
104 | std::cout << alice << std::endl;
105 | std::cout << charlie << std::endl;
106 |
107 | // Expected output:
108 | // User(name=Alice, friends=[Charlie])
109 | // User(name=Charlie, friends=[Alice])
110 | ```
111 |
112 | The function signature for this operator should be `User& operator+=(User& rhs)`. Note that like the copy-assignment operator, it returns a reference to itself.
113 |
114 | ### `operator<`
115 |
116 | Recall that the `<` operator is required to store users in a `std::set`, as `std::set` is implemented in terms of the comparison operator. Implement `operator<` to compare users alphabetically by name. For example:
117 |
118 | ```cpp
119 | User alice("Alice");
120 | User charlie("Charlie");
121 |
122 | if (alice < charlie)
123 | std::cout << "Alice is less than Charlie";
124 | else
125 | std::cout << "Charlie is less than Alice";
126 |
127 | // Expected output:
128 | // Alice is less than Charlie
129 | ```
130 |
131 | The function signature for this operator should be `bool operator<(const User& rhs) const`.
132 |
133 | ## 🚀 Submission Instructions
134 |
135 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/YFA8Z6GwBp976irm7). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading.
136 |
137 | Your deliverable should be:
138 |
139 | - `user.h`
140 | - `user.cpp`
141 |
142 | You may resubmit as many times as you'd like before the deadline.
143 |
--------------------------------------------------------------------------------
/assignment5/autograder/autograder.py:
--------------------------------------------------------------------------------
1 | import functools
2 | from typing import List
3 | from utils import Autograder, ASSIGNMENT_DIR
4 |
5 | import os
6 | import subprocess
7 |
8 |
9 | def get_main_exe_path() -> os.PathLike:
10 | main_path = os.path.join(ASSIGNMENT_DIR, "main")
11 | if not os.path.isfile(main_path):
12 | main_path = os.path.join(ASSIGNMENT_DIR, "main.exe")
13 | if not os.path.isfile(main_path):
14 | raise RuntimeError(
15 | "Could not find a main executable. Did you compile your code with the command in the README?"
16 | )
17 | return main_path
18 |
19 |
20 | @functools.lru_cache
21 | def get_memory_leak_exit_code() -> int:
22 | main_path = get_main_exe_path()
23 | result = subprocess.check_output([main_path, "memory_leak_exit_code"], text=True)
24 | return int(result.strip())
25 |
26 |
27 | def verify_output(test_case: str, expected: List[str]):
28 | main_path = get_main_exe_path()
29 | result = subprocess.run(
30 | [main_path, test_case],
31 | stdout=subprocess.PIPE,
32 | stderr=subprocess.PIPE,
33 | text=True,
34 | )
35 |
36 | if result.returncode == get_memory_leak_exit_code():
37 | raise RuntimeError(f"💧Memory leak detected: {result.stderr}")
38 |
39 | if result.returncode != 0:
40 | raise RuntimeError(result.stderr)
41 |
42 | actual = result.stdout.strip()
43 | if not actual:
44 | actual = []
45 | else:
46 | actual = actual.split("\n")
47 |
48 | if actual != expected:
49 | nl = "\n\t"
50 | raise RuntimeError(
51 | f"Test output did not match expected. Expected: \n{nl}{nl.join(expected)}\n\nGot: \n{nl}{nl.join(actual)}"
52 | )
53 |
54 |
55 | if __name__ == "__main__":
56 | grader = Autograder()
57 | grader.add_part(
58 | "Part 1: operator<<",
59 | lambda: verify_output(
60 | "insertion_operator",
61 | [
62 | "User(name=Alice, friends=[])",
63 | "User(name=Bob, friends=[Alice])",
64 | "User(name=Charlie, friends=[Alice, Bob])",
65 | ],
66 | ),
67 | )
68 |
69 | grader.add_part("Part 2: Destructor", lambda: verify_output("destructor", []))
70 |
71 | grader.add_part(
72 | "Part 2: Copy Constructor",
73 | lambda: verify_output(
74 | "copy_constructor",
75 | [
76 | "User(name=Alice, friends=[F, F, F, F, F, F, F, F, F])",
77 | "User(name=Alice, friends=[Bob, Alice, Charlie, F, F, F, F, F, F])",
78 | ],
79 | ),
80 | )
81 |
82 | grader.add_part(
83 | "Part 2: Copy Assignment Operator",
84 | lambda: verify_output(
85 | "copy_assignment",
86 | [
87 | "User(name=Bob, friends=[BF, BF, BF, BF, BF, BF, BF, BF, BF])",
88 | "User(name=Bob, friends=[BF, BF, BF, BF, BF, BF, BF, BF, BF])",
89 | ],
90 | ),
91 | )
92 |
93 | grader.add_part(
94 | "Part 2: Move Constructor", lambda: verify_output("move_constructor", [])
95 | )
96 | grader.add_part(
97 | "Part 2: Move Assignment Operator", lambda: verify_output("move_assignment", [])
98 | )
99 |
100 | grader.add_part(
101 | "Part 3: operator+=",
102 | lambda: verify_output(
103 | "compound_assignment",
104 | ["User(name=Alice, friends=[Bob])", "User(name=Bob, friends=[Alice])"],
105 | ),
106 | )
107 |
108 | grader.add_part(
109 | "Part 3: operator<",
110 | lambda: verify_output(
111 | "comparable",
112 | [
113 | "A < B is true",
114 | "B < A is false",
115 | "B < C is true",
116 | "C < B is false",
117 | "C < A is false",
118 | "A < C is true",
119 | ],
120 | ),
121 | )
122 |
123 | grader.run(),
124 |
--------------------------------------------------------------------------------
/assignment5/autograder/diagnostics.hpp:
--------------------------------------------------------------------------------
1 | /** Contains memory diagnostics to track memory allocations and report memory leaks. */
2 |
3 | #pragma once
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | namespace MemoryDiagnostics {
13 | namespace detail {
14 |
15 | /**
16 | * In order to initialize an unordered_map inside of the MemoryTracker class below,
17 | * we must make dynamic allocations. However, the default allocator for unordered_map
18 | * calls operator new and delete, which would cause infinite recursion. To avoid this,
19 | * we define a custom allocator that uses std::malloc and std::free instead and define
20 | * the unordered_map in MemoryTracker to use this allocator.
21 | *
22 | * See https://en.cppreference.com/w/cpp/named_req/Allocator for more information.
23 | */
24 | template struct Mallocator {
25 | using value_type = T;
26 |
27 | Mallocator() = default;
28 |
29 | template constexpr Mallocator(const Mallocator&) noexcept {}
30 |
31 | [[nodiscard]] T* allocate(std::size_t n) {
32 | if (n > std::numeric_limits::max() / sizeof(T))
33 | throw std::bad_array_new_length();
34 |
35 | if (auto p = static_cast(std::malloc(n * sizeof(T)))) {
36 | return p;
37 | }
38 |
39 | throw std::bad_alloc();
40 | }
41 |
42 | void deallocate(T* p, std::size_t n) noexcept { std::free(p); }
43 | };
44 |
45 | template bool operator==(const Mallocator&, const Mallocator&) {
46 | return true;
47 | }
48 |
49 | template bool operator!=(const Mallocator&, const Mallocator&) {
50 | return false;
51 | }
52 |
53 | class MemoryTracker {
54 | using AllocationsMap = std::unordered_map, std::equal_to,
55 | Mallocator>>;
56 | size_t bytes_outstanding = 0;
57 | AllocationsMap allocations;
58 |
59 | public:
60 | void* allocate(size_t size) {
61 | if (size == 0)
62 | ++size; // avoid std::malloc(0) which may return nullptr on success
63 |
64 | void* ptr = std::malloc(size);
65 | if (ptr == nullptr)
66 | throw std::bad_alloc{}; // required by [new.delete.single]/3
67 |
68 | bytes_outstanding += size;
69 | allocations[ptr] = size;
70 | return ptr;
71 | }
72 |
73 | void deallocate(void* ptr) {
74 | std::free(ptr);
75 | auto it = allocations.find(ptr);
76 | if (it != allocations.end()) {
77 | bytes_outstanding -= it->second;
78 | allocations.erase(it);
79 | }
80 | }
81 |
82 | size_t get_bytes_outstanding() const { return bytes_outstanding; }
83 | };
84 |
85 | MemoryTracker& get_tracker() {
86 | static MemoryTracker tracker;
87 | return tracker;
88 | }
89 |
90 | } // namespace detail
91 |
92 | /**
93 | * MemoryGuard makes sure that the dynamic allocation count at construction
94 | * matches the count at destruction. If the counts do not match, MemoryGuard
95 | * will print an error message to stderr and exit the program with a predefined
96 | * exit code.
97 | */
98 | class MemoryGuard {
99 | public:
100 | MemoryGuard(const char* message)
101 | : message(message), initial_bytes_outstanding(detail::get_tracker().get_bytes_outstanding()) {
102 | }
103 |
104 | ~MemoryGuard() {
105 | if (initial_bytes_outstanding != detail::get_tracker().get_bytes_outstanding()) {
106 | if (message)
107 | std::cerr << message << std::endl;
108 | std::exit(exit_code);
109 | }
110 | }
111 |
112 | static int get_exit_code() { return exit_code; }
113 |
114 | private:
115 | const char* message = nullptr;
116 | size_t initial_bytes_outstanding;
117 | static constexpr const int exit_code = 106;
118 | };
119 |
120 | } // namespace MemoryDiagnostics
121 |
122 | void* operator new(std::size_t sz) { return MemoryDiagnostics::detail::get_tracker().allocate(sz); }
123 |
124 | void* operator new[](std::size_t sz) {
125 | return MemoryDiagnostics::detail::get_tracker().allocate(sz);
126 | }
127 |
128 | void operator delete(void* ptr) noexcept {
129 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr);
130 | }
131 |
132 | void operator delete(void* ptr, std::size_t size) noexcept {
133 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr);
134 | }
135 |
136 | void operator delete[](void* ptr) noexcept {
137 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr);
138 | }
139 |
140 | void operator delete[](void* ptr, std::size_t size) noexcept {
141 | MemoryDiagnostics::detail::get_tracker().deallocate(ptr);
142 | }
--------------------------------------------------------------------------------
/assignment5/autograder/utils.hpp:
--------------------------------------------------------------------------------
1 | int
2 | run_autograder()
3 | {
4 | auto run_program = [](std::string program,
5 | std::initializer_list args,
6 | bool silent = false) {
7 | std::stringstream ss;
8 |
9 | ss << program;
10 | for (const auto& arg : args) {
11 | ss << ' ' << arg;
12 | }
13 |
14 | if (silent) {
15 | #ifdef _WIN32
16 | ss << " >nul 2>&1";
17 | #else
18 | ss << " >/dev/null 2>&1";
19 | #endif
20 | }
21 |
22 | std::cout.flush();
23 | return system(ss.str().c_str());
24 | };
25 |
26 | std::string python;
27 | for (const auto& option :
28 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) {
29 | if (run_program(option, { "--version" }, true) == 0) {
30 | python = option;
31 | break;
32 | }
33 | }
34 |
35 | if (python.empty()) {
36 | std::cerr
37 | << "Python was not found on your system. Please install Python and "
38 | "try again."
39 | << "\n";
40 | std::exit(1);
41 | }
42 |
43 | return run_program(python, { "autograder/autograder.py" });
44 | }
--------------------------------------------------------------------------------
/assignment5/docs/logo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cs106l/cs106l-assignments/d6340bb351e2c36bad6efb64a5d2a681b24b7a8e/assignment5/docs/logo.jpeg
--------------------------------------------------------------------------------
/assignment5/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * CS106L Assignment 5: TreeBook
3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca.
4 | */
5 |
6 | /* ========================================================================= *
7 | * Don't worry about anything beyond this point. *
8 | * (unless you are really curious and want to!) *
9 | * ========================================================================= */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include "autograder/diagnostics.hpp"
22 | #include "autograder/utils.hpp"
23 | #include "user.h"
24 |
25 | void test_memory_leak_exit_code() {
26 | std::cout << MemoryDiagnostics::MemoryGuard::get_exit_code() << "\n";
27 | }
28 |
29 | template
30 | concept printable = requires(T t) {
31 | { std::cout << t } -> std::same_as;
32 | };
33 |
34 | template void test_insertion_operator() {
35 | if constexpr (printable) {
36 | TUser alice("Alice");
37 | std::cout << alice << "\n";
38 |
39 | TUser bob("Bob");
40 | bob.add_friend("Alice");
41 | std::cout << bob << "\n";
42 |
43 | TUser charlie("Charlie");
44 | charlie.add_friend("Alice");
45 | charlie.add_friend("Bob");
46 | std::cout << charlie << "\n";
47 | } else {
48 | std::cerr << "User does not have a valid operator<<." << "\n";
49 | std::exit(1);
50 | }
51 | }
52 |
53 | template void test_destructor() {
54 | MemoryDiagnostics::MemoryGuard guard(
55 | "Did you remember to delete[] _friends inside the destructor?");
56 | TUser alice("Alice");
57 | for (size_t i = 0; i < 100; ++i) {
58 | alice.add_friend("Friend");
59 | }
60 | }
61 |
62 | template void test_copy_constructor() {
63 | if constexpr (printable) {
64 | if constexpr (std::is_copy_constructible_v) {
65 | TUser a("Alice");
66 | for (size_t i = 0; i < 9; i++) {
67 | a.add_friend("F");
68 | }
69 |
70 | TUser b(a);
71 | b.set_friend(0, "Bob");
72 | b.set_friend(1, "Alice");
73 | b.set_friend(2, "Charlie");
74 |
75 | std::cout << a << "\n";
76 | std::cout << b << "\n";
77 | } else {
78 | std::cerr << "User does not have a valid copy constructor." << "\n";
79 | std::exit(1);
80 | }
81 | } else {
82 | std::cerr << "User does not have a valid operator<<." << "\n";
83 | std::exit(1);
84 | }
85 | }
86 |
87 | template void test_copy_assignment() {
88 | if constexpr (printable) {
89 | if constexpr (std::is_copy_assignable_v) {
90 | MemoryDiagnostics::MemoryGuard guard(
91 | "Did you remember to delete[] the old value of _friends inside the copy "
92 | "assignment operator?");
93 |
94 | TUser a("Alice");
95 | for (size_t i = 0; i < 9; i++) {
96 | a.add_friend("AF");
97 | }
98 |
99 | TUser b("Bob");
100 | for (size_t i = 0; i < 9; i++) {
101 | b.add_friend("BF");
102 | }
103 |
104 | a = b;
105 |
106 | std::cout << a << "\n";
107 | std::cout << b << "\n";
108 | } else {
109 | std::cerr << "User does not have a valid copy constructor." << "\n";
110 | std::exit(1);
111 | }
112 | } else {
113 | std::cerr << "User does not have a valid operator<<." << "\n";
114 | std::exit(1);
115 | }
116 | }
117 |
118 | template void test_move_constructor() {
119 | if constexpr (std::move_constructible) {
120 | std::cerr << "User should not be move constructible. Did you remember to "
121 | "delete the move constructor?\n";
122 | std::exit(1);
123 | }
124 | }
125 |
126 | template void test_move_assignment() {
127 | if constexpr (std::is_move_assignable_v) {
128 | std::cerr << "User should not be move assignable. Did you remember to "
129 | "delete the move assignment operator?\n";
130 | std::exit(1);
131 | }
132 | }
133 |
134 | template
135 | concept compound_assignable = requires(T a, T b) {
136 | { a += b } -> std::same_as;
137 | };
138 |
139 | template void test_compound_assignment() {
140 | if constexpr (printable) {
141 | if constexpr (compound_assignable) {
142 | TUser a("Alice");
143 | TUser b("Bob");
144 | a += b;
145 | std::cout << a << "\n";
146 | std::cout << b << "\n";
147 | } else {
148 | std::cerr << "User does not have a valid operator+=. Function signature "
149 | "should be:\n\n\tUser& operator+=(User& "
150 | "other);\n\ninside the User class."
151 | << "\n";
152 | std::exit(1);
153 | }
154 | } else {
155 | std::cerr << "User does not have a valid operator<<." << "\n";
156 | std::exit(1);
157 | }
158 | }
159 |
160 | template
161 | concept comparable = requires(TUser a, TUser b) {
162 | { a < b } -> std::same_as;
163 | };
164 |
165 | template void test_comparable() {
166 | if constexpr (comparable) {
167 | TUser a("A");
168 | TUser b("B");
169 | TUser c("C");
170 |
171 | std::vector> pairs = {{a, b}, {b, a}, {b, c}, {c, b}, {c, a}, {a, c}};
172 | std::cout << std::boolalpha;
173 | for (const auto& [left, right] : pairs) {
174 | std::cout << left.get_name() << " < " << right.get_name() << " is " << (left < right) << "\n";
175 | }
176 | } else {
177 | std::cerr << "User does not have a valid operator<. Function signature "
178 | "should be:\n\n\tbool operator<(const "
179 | "User& other);\n\ninside the User class."
180 | << "\n";
181 | std::exit(1);
182 | }
183 | }
184 |
185 | const std::unordered_map> test_functions = {
186 | {"memory_leak_exit_code", test_memory_leak_exit_code},
187 | {"insertion_operator", test_insertion_operator},
188 | {"destructor", test_destructor},
189 | {"copy_constructor", test_copy_constructor},
190 | {"copy_assignment", test_copy_assignment},
191 | {"move_constructor", test_move_constructor},
192 | {"move_assignment", test_move_assignment},
193 | {"compound_assignment", test_compound_assignment},
194 | {"comparable", test_comparable}};
195 |
196 | int main(int argc, char* argv[]) {
197 | if (argc == 2) {
198 | if (test_functions.find(argv[1]) != test_functions.end()) {
199 | test_functions.at(argv[1])();
200 | return 0;
201 | }
202 |
203 | std::cerr << "Test '" << argv[1] << "' not found." << "\n";
204 | return 1;
205 | }
206 |
207 | return run_autograder();
208 | }
--------------------------------------------------------------------------------
/assignment5/user.cpp:
--------------------------------------------------------------------------------
1 | #include "user.h"
2 |
3 | /**
4 | * Creates a new User with the given name and no friends.
5 | */
6 | User::User(const std::string& name)
7 | : _name(name)
8 | , _friends(nullptr)
9 | , _size(0)
10 | , _capacity(0)
11 | {
12 | }
13 |
14 | /**
15 | * Adds a friend to this User's list of friends.
16 | * @param name The name of the friend to add.
17 | */
18 | void
19 | User::add_friend(const std::string& name)
20 | {
21 | if (_size == _capacity) {
22 | _capacity = 2 * _capacity + 1;
23 | std::string* newFriends = new std::string[_capacity];
24 | for (size_t i = 0; i < _size; ++i) {
25 | newFriends[i] = _friends[i];
26 | }
27 | delete[] _friends;
28 | _friends = newFriends;
29 | }
30 |
31 | _friends[_size++] = name;
32 | }
33 |
34 | /**
35 | * Returns the name of this User.
36 | */
37 | std::string
38 | User::get_name() const
39 | {
40 | return _name;
41 | }
42 |
43 | /**
44 | * Returns the number of friends this User has.
45 | */
46 | size_t
47 | User::size() const
48 | {
49 | return _size;
50 | }
51 |
52 | /**
53 | * Sets the friend at the given index to the given name.
54 | * @param index The index of the friend to set.
55 | * @param name The name to set the friend to.
56 | */
57 | void User::set_friend(size_t index, const std::string& name)
58 | {
59 | _friends[index] = name;
60 | }
61 |
62 | /**
63 | * STUDENT TODO:
64 | * The definitions for your custom operators and special member functions will go here!
65 | */
66 |
--------------------------------------------------------------------------------
/assignment5/user.h:
--------------------------------------------------------------------------------
1 | /*
2 | * CS106L Assignment 5: TreeBook
3 | * Created by Fabio Ibanez with modifications by Jacob Roberts-Baca.
4 | */
5 |
6 | #include
7 | #include
8 |
9 | class User
10 | {
11 | public:
12 | User(const std::string& name);
13 | void add_friend(const std::string& name);
14 | std::string get_name() const;
15 | size_t size() const;
16 | void set_friend(size_t index, const std::string& name);
17 |
18 | /**
19 | * STUDENT TODO:
20 | * Your custom operators and special member functions will go here!
21 | */
22 |
23 | private:
24 | std::string _name;
25 | std::string* _friends;
26 | size_t _size;
27 | size_t _capacity;
28 | };
--------------------------------------------------------------------------------
/assignment6/README.md:
--------------------------------------------------------------------------------
1 | # Assignment 6: Explore Courses
2 |
3 | Due Friday, May 30th at 11:59PM
4 |
5 | ## Overview
6 |
7 | In this assignment you will be exercising your understanding of `std::optional`. We'll be making use of the same `courses.csv` from assignment 1. You are tasked to write one function for this assignment, which attempts to find the a `Course` in the `CourseDatabase` object, and return it.
8 | You'll also explore the monadic operations that come with the `std::optional` class. Take a look at the code and review the `CourseDatabase` class to understand the interface.
9 |
10 | ## Running your code
11 |
12 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit Ctrl+\` or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assignment6/` directory and run:
13 |
14 | ```sh
15 | g++ -std=c++23 main.cpp -o main
16 | ```
17 |
18 | Assuming that your code compiles without any compiler errors, you can now do:
19 |
20 | ```sh
21 | ./main
22 | ```
23 |
24 | which will actually run the `main` function in `main.cpp`.
25 |
26 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track!
27 |
28 | > [!NOTE]
29 | >
30 | > ### Note for Windows
31 | >
32 | > On Windows, you may need to compile your code using
33 | >
34 | > ```sh
35 | > g++ -static-libstdc++ -std=c++23 main.cpp -o main
36 | > ```
37 | >
38 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with:
39 | >
40 | > ```sh
41 | > ./main.exe
42 | > ```
43 |
44 | ## Part 0: Include ``
45 |
46 | At the top of the `main.cpp` include ``, we're going to make use of `std::optional` in this assignment!
47 |
48 | ## Part 1: Write the `find_course` function
49 |
50 | This function takes in a string `course_title`, and the function should try to find the `course` inside of the private `courses` member of the `CourseDatabase` object. What should the return type be? (hint: there may or may not be a `Course` for the `course_title` passed in)
51 |
52 | > [!NOTE]
53 | > You need to change the type returned by `find_course` which is currenty `FillMeIn`.
54 |
55 | ## Part 2: Modifying the `main` function
56 |
57 | Notice that we call the `find_course` here in the `main` function:
58 |
59 | ```cpp
60 | auto course = db.find_course(argv[1]);
61 | ```
62 |
63 | Now, you need to make use of the [monadic operations](https://en.cppreference.com/w/cpp/utility/optional) to populate the `output` string properly. Let's walk through how to do this.
64 |
65 | Here's the behavior that you want to recreate, **without using any conditionals** like `if` statements:
66 | ```cpp
67 | if (course.has_value()) {
68 | std::cout << "Found course: " << course->title << ","
69 | << course->number_of_units << "," << course->quarter << "\n";
70 | } else {
71 | std::cout << "Course not found.\n";
72 | }
73 | ```
74 |
75 | Very simply, if there is a course then the line at the bottom of `main`
76 |
77 | ```cpp
78 | std::cout << output << std::end;
79 | ```
80 |
81 | Should produce:
82 | ```bash
83 | Found course: ,,
84 | ```
85 |
86 | if there is no course then
87 |
88 | ```cpp
89 | std::cout << output << std::end;
90 | ```
91 |
92 | Should produce:
93 | ```bash
94 | Course not found.
95 | ```
96 |
97 | ### Monadic Operations
98 |
99 | There are three monadic operations: [`and_then`](https://en.cppreference.com/w/cpp/utility/optional/and_then), [`transform`](https://en.cppreference.com/w/cpp/utility/optional/transform), and [`or_else`](https://en.cppreference.com/w/cpp/utility/optional/or_else). Read the description of each of them in the lecture slides, and take a look at [the standard library documentation](https://en.cppreference.com/w/cpp/utility/optional). You will only need to use 2 of the mondadic operations.
100 |
101 | Your code should end up looking something like this:
102 |
103 | ```cpp
104 | std::string output = course
105 | ./* monadic function one */ (/* ... */)
106 | ./* monadic function two */ (/* ... */)
107 | .value(); // OR `.value_or(...)`, see below
108 | ```
109 |
110 | It can help to **think about what the type of `output` is and work backwards from there**. Pay attention to what each of the monadic functions does, as described in the hint below.
111 |
112 | > [!NOTE]
113 | > Recall what the role is of each of the monadic functions. The official C++ library doesn't do a good job explaining this, so we have included a short reference here. Suppose `T` and `U` are arbitrary types.
114 | >
115 | > ```cpp
116 | > /**
117 | > * tl;dr;
118 | > * Calls a function to produce a new optional if there is a value; otherwise, returns nothing.
119 | > *
120 | > * The function passed to `and_then` takes a non-optional instance of type `T` and returns a `std::optional`.
121 | > * If the optional has a value, `and_then` applies the function to its value and returns the result.
122 | > * If the optional doesn't have a value (i.e. it is `std::nullopt`), it returns `std::nullopt`.
123 | > */
124 | > template
125 | > std::optional std::optional::and_then(std::function(T)> func);
126 | >
127 | > /**
128 | > * tl;dr;
129 | > * Applies a function to the stored value if present, wrapping the result in an optional, or returns nothing otherwise.
130 | > *
131 | > * The function passed to `transform` takes a non-optional instance of type `T` and returns a non-optional instance of type `U`.
132 | > * If the optional has a value, `transform` applies the function to its value and returns the result wrapped in an `std::optional`.
133 | > * If the optional doesn't have a value (i.e. it is `std::nullopt`), it returns `std::nullopt`.
134 | > */
135 | > template
136 | > std::optional std::optional::transform(std::function func);
137 | >
138 | > /**
139 | > * tl;dr;
140 | > * Returns the optional itself if it has a value; otherwise, it calls a function to produce a new optional.
141 | > *
142 | > * The opposite of `and_then`.
143 | > * The function passed to `or_else` takes in no arguments and returns a `std::optional`.
144 | > * If the optional has a value, `or_else` returns it.
145 | > * If the optional doesn't have a value (i.e. it is `std::nullopt`), `or_else invokes the function and returns the result.
146 | > */
147 | > template
148 | > std::optional std::optional::or_else(std::function(T)> func);
149 | > ```
150 | >
151 | > For example, given a `std::optional opt` object, the monadic operations could be invoked as follows:
152 | >
153 | > ```cpp
154 | > opt
155 | > .and_then([](T value) -> std::optional { return /* ... */; })
156 | > .transform([](T value) -> U { return /* ... */; });
157 | > .or_else([]() -> std::optional { return /* ... */; })
158 | > ```
159 | >
160 | > Note that the `->` notation in the lambda function is a way of explicitly writing out the return type of the function!
161 | >
162 | > Notice that since each method returns an `std::optional`, you can chain them together. If you are certain that the optional will have a value at the end of the chain, you could call [`.value()`](https://en.cppreference.com/w/cpp/utility/optional/value) to get the value. Otherwise, you could call [`.value_or(fallback)`](https://en.cppreference.com/w/cpp/utility/optional/value_or) to get the result or some other `fallback` value if the optional doesn't have a value.
163 |
164 |
165 |
166 | ## 🚀 Submission Instructions
167 |
168 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/cZGTbEQ8bp8awoA3A). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading.
169 |
170 | Your deliverable should be:
171 |
172 | - `main.cpp`
173 |
174 | You may resubmit as many times as you'd like before the deadline.
175 |
--------------------------------------------------------------------------------
/assignment6/autograder/autograder.py:
--------------------------------------------------------------------------------
1 | from utils import Autograder, ASSIGNMENT_DIR
2 | from typing import List
3 |
4 | import os
5 | import subprocess
6 |
7 |
8 | def get_main_exe_path() -> os.PathLike:
9 | main_path = os.path.join(ASSIGNMENT_DIR, "main")
10 | if not os.path.isfile(main_path):
11 | main_path = os.path.join(ASSIGNMENT_DIR, "main.exe")
12 | if not os.path.isfile(main_path):
13 | raise RuntimeError(
14 | "Could not find a main executable. Did you compile your code with the command in the README?"
15 | )
16 | return main_path
17 |
18 |
19 | def verify_output(courses: List[str], found: bool):
20 | main_path = get_main_exe_path()
21 |
22 | course_names = [course.split(",")[0] for course in courses]
23 | for course_name, course in zip(course_names, courses):
24 | result = subprocess.run(
25 | [main_path, course_name],
26 | stdout=subprocess.PIPE,
27 | stderr=subprocess.PIPE,
28 | text=True,
29 | )
30 |
31 | if result.returncode != 0:
32 | raise RuntimeError(result.stderr)
33 |
34 | actual = result.stdout.strip()
35 |
36 | expected = f"Found course: {course}" if found else "Course not found."
37 |
38 | if actual != expected:
39 | raise RuntimeError(
40 | f"For course {course_name}, expected output:\n\t{expected}\nbut got:\n\t{actual}"
41 | )
42 |
43 |
44 | if __name__ == "__main__":
45 | grader = Autograder()
46 | grader.add_part(
47 | "Present Courses",
48 | lambda: verify_output(
49 | [
50 | "Introduction to Mobile Augmented Reality Design and Development,1,2023-2024 Spring",
51 | "Design for Play (SYMSYS 195G),4,2023-2024 Spring",
52 | "Practical Machine Learning,4,null",
53 | "Human-Centered AI,3,null",
54 | "Seminar in Artificial Intelligence in Healthcare,1,2023-2024 Autumn",
55 | "Introduction to Python Programming,1,2023-2024 Autumn",
56 | "Machine Learning for Discrete Optimization (MS&E 236),3,2023-2024 Spring",
57 | "AI for Social Good,2,2023-2024 Spring",
58 | "Software Project Experience with Corporate Partners,4,2023-2024 Winter",
59 | "The Future of Mechanical Engineering (ME 228),1,null",
60 | ],
61 | True,
62 | ),
63 | )
64 |
65 | grader.add_part(
66 | "Missing Courses",
67 | lambda: verify_output(
68 | [
69 | "Introduction to Quantum Mechanics for Engineers",
70 | "Fundamentals of Blockchain and Cryptography",
71 | "Applied Data Science for Social Impact",
72 | "Advanced Robotics and Autonomous Systems",
73 | "Neural Networks and Deep Learning Fundamentals",
74 | "Exploring the Future of Artificial General Intelligence",
75 | "Data Ethics and Privacy in the Digital Age",
76 | "Introduction to Cloud Computing with AWS",
77 | "Creative Coding for Interactive Media",
78 | "Virtual Reality and Augmented Reality for Healthcare",
79 | ],
80 | False,
81 | ),
82 | )
83 |
84 | grader.run(),
85 |
--------------------------------------------------------------------------------
/assignment6/autograder/utils.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | struct Course;
7 | using FillMeIn = Course*;
8 |
9 | int
10 | run_autograder()
11 | {
12 | auto run_program = [](std::string program,
13 | std::initializer_list args,
14 | bool silent = false) {
15 | std::stringstream ss;
16 |
17 | ss << program;
18 | for (const auto& arg : args) {
19 | ss << ' ' << arg;
20 | }
21 |
22 | if (silent) {
23 | #ifdef _WIN32
24 | ss << " >nul 2>&1";
25 | #else
26 | ss << " >/dev/null 2>&1";
27 | #endif
28 | }
29 |
30 | std::cout.flush();
31 | return system(ss.str().c_str());
32 | };
33 |
34 | std::string python;
35 | for (const auto& option :
36 | { "python", "python3", "/usr/bin/python3", "/usr/bin/python" }) {
37 | if (run_program(option, { "--version" }, true) == 0) {
38 | python = option;
39 | break;
40 | }
41 | }
42 |
43 | if (python.empty()) {
44 | std::cerr
45 | << "Python was not found on your system. Please install Python and "
46 | "try again."
47 | << "\n";
48 | std::exit(1);
49 | }
50 |
51 | return run_program(python, { "autograder/autograder.py" });
52 | }
53 |
54 | std::vector split(std::string s, char delim) {
55 | std::vector return_vec;
56 | std::stringstream ss(s);
57 | std::string token;
58 | while (getline(ss, token, delim)) {
59 | return_vec.push_back(token);
60 | }
61 | return return_vec;
62 | }
63 |
64 | std::vector read_lines(std::string filename) {
65 | std::vector lines;
66 | std::ifstream file(filename);
67 | if (!file.is_open()) {
68 | std::cerr << "Could not open file " << filename << "\n";
69 | std::exit(1);
70 | }
71 |
72 | std::string line;
73 | while (std::getline(file, line)) lines.push_back(line);
74 | return lines;
75 | }
--------------------------------------------------------------------------------
/assignment6/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * CS106L Assignment 6: Explore Courses
3 | * Created by Haven Whitney with modifications by Jacob Roberts-Baca and Fabio
4 | * Ibanez.
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | /** STUDENT_TODO: You will need to include a relevant header file here! */
12 |
13 | #include "autograder/utils.hpp"
14 |
15 | /**
16 | * A course. This should be familiar from Assignment 1!
17 | */
18 | struct Course
19 | {
20 | std::string title;
21 | std::string number_of_units;
22 | std::string quarter;
23 |
24 | /**
25 | * You don't have to ignore this anymore! We're defining the `==` operator for
26 | * the Course struct.
27 | */
28 | bool operator==(const Course& other) const
29 | {
30 | return title == other.title && number_of_units == other.number_of_units &&
31 | quarter == other.quarter;
32 | }
33 | };
34 |
35 | class CourseDatabase
36 | {
37 | public:
38 | CourseDatabase(std::string filename)
39 | {
40 | auto lines = read_lines(filename);
41 | std::transform(lines.begin(),
42 | lines.end(),
43 | std::back_inserter(courses),
44 | [](std::string line) {
45 | auto parts = split(line, ',');
46 | return Course{ parts[0], parts[1], parts[2] };
47 | });
48 | }
49 |
50 | /**
51 | * Finds a course in the database with the given title, if it exists.
52 | * @param course_title The title of the course to find.
53 | * @return You will need to figure this out!
54 | */
55 | FillMeIn find_course(std::string course_title)
56 | {
57 | /* STUDENT_TODO: Implement this method! You will need to change the return
58 | * type. */
59 | }
60 |
61 | private:
62 | std::vector courses;
63 | };
64 |
65 | int
66 | main(int argc, char* argv[])
67 | {
68 | static_assert(
69 | !std::is_same_v,
71 | FillMeIn>,
72 | "You must change the return type of CourseDatabase::find_course to "
73 | "something other than FillMeIn.");
74 |
75 | if (argc == 2) {
76 | CourseDatabase db("autograder/courses.csv");
77 | auto course = db.find_course(argv[1]);
78 |
79 | /********************************************************
80 | STUDENT_TODO: Populate the output string with the right information to print
81 | Please pay special attention to the README here
82 | ********************************************************/
83 |
84 | std::string output = /* STUDENT_TODO */
85 |
86 | /********************************************************
87 | DO NOT MODIFY ANYTHING BELOW THIS LINE PLEASE
88 | ********************************************************/
89 |
90 | std::cout << output << std::endl;
91 | return 0;
92 | }
93 |
94 | return run_autograder();
95 | }
--------------------------------------------------------------------------------
/assignment7/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Assignment 7: Unique Pointer
6 |
7 | Due Friday, June 6th at 11:59PM
8 |
9 | ## Overview
10 |
11 | In this assignment, you will implement a custom version of `unique_ptr` in order to gain some exposure to concepts like RAII and smart pointers that were introduced in lecture this week. In addition, you will exercise some of the skills we've seen throughout the course: templates, operator overloading, and move semantics.
12 |
13 | There are three files you'll work with for this assignment:
14 |
15 | - `unique_ptr.h` - Contains all the code for your `unique_ptr` implementation.
16 | - `main.cpp` - Contains some code that uses your `unique_ptr`. You'll write one function here!
17 | - `short_answer.txt` - Contains a few short answer questions you'll answer as you work on the assignment.
18 |
19 | ## Running your code
20 |
21 | To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit Ctrl+\` or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assign7/` directory and run:
22 |
23 | ```sh
24 | g++ -std=c++20 main.cpp -o main
25 | ```
26 |
27 | Assuming that your code compiles without any compiler errors, you can now do:
28 |
29 | ```sh
30 | ./main
31 | ```
32 |
33 | which will actually run the `main` function in `main.cpp`.
34 |
35 | As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track!
36 |
37 | > [!NOTE]
38 | >
39 | > ### Note for Windows
40 | >
41 | > On Windows, you may need to compile your code using
42 | >
43 | > ```sh
44 | > g++ -static-libstdc++ -std=c++20 main.cpp -o main
45 | > ```
46 | >
47 | > in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with:
48 | >
49 | > ```sh
50 | > ./main.exe
51 | > ```
52 |
53 | ## Part 1: Implementing `unique_ptr`
54 |
55 | In the first part of the assignment, you will implement one of the smart pointers we discussed in Thursday's lecture: `unique_ptr`. The `unique_ptr` you will implement is a simpler version of the standard library's [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr). Recall that a `unique_ptr` represents a pointer to dynamically allocated memory that is owned by a single (*unique*) variable. When that variable goes out of scope, it automatically cleans up the allocated memory that it owns by calling `delete`. This behaviour is known as RAII (resource acquisition is initialization). **For our purposes, you may assume that `unique_ptr` points to a single element of type T. You will not have to call `delete[]` at any point or handle pointers to dynamically allocated arrays.**
56 |
57 | > [!IMPORTANT]
58 | > ##### `short_answer.txt`
59 | > **Q1:** List one or two benefits of using RAII to manage memory instead manually calling `new` and `delete`.
60 |
61 | > [!NOTE]
62 | > While our `unique_ptr` will not support pointers to arrays, we could add this behaviour if we wanted to. For example, the C++ standard library `std::unique_ptr` uses a *template specialization* to implement different behaviour for array pointers. Such a template specialization might look like this:
63 | >
64 | > ```cpp
65 | > template
66 | > class unique_ptr;
67 | > ```
68 | >
69 | > In effect, we would have two versions of `unique_ptr`: one for single elements and one for arrays of elements. Each version supports different operations; for example, the array version provides a subscript operator (`operator[]`) to deference elements in the array, while the single element version does not.
70 |
71 | ### Implementing `unique_ptr` functionality
72 |
73 | Take a moment to scan over the provided code for `unique_ptr` in `unique_ptr.h`. We have provided the basic interface for a `unique_ptr`: you will implement this interface. Remember, a `unique_ptr` should look and behave like a regular pointer, supporting operations like dereferencing (`operator*`) and member access (`operator->`). Several of these methods have both `const` and non-`const` versions in order for our class to be fully const-correct.
74 |
75 | You will implement the basic pointer interface for a `unique_ptr` by implementing the following points. Each of these tasks should be relatively straightforward and can be completed by adding/changing 1-2 lines in `unique_ptr.h`:
76 |
77 | * The `private` section of `unique_ptr`
78 | * `unique_ptr(T* ptr)` (constructor)
79 | * `unique_ptr(std::nullptr_t)` (constructor for `nullptr`)
80 | * `T& operator*()`
81 | * `const T& operator*() const`
82 | * `T* operator->()`
83 | * `const T* operator->() const`
84 | * `operator bool() const`
85 |
86 | ### Implementing RAII
87 |
88 | At this point, our `unique_ptr` will behave as if it were a raw pointer, but it will not actually do any automatic memory management such as deallocating memory when a `unique_ptr` variable goes out of scope. Add to that, our pointer is not *unique*: multiple copies of it (all pointing to the same memory) can be made indiscriminantly. For example, let's assume that our `unique_ptr` properly cleans up its data when it goes out of scope. Consider the following code block:
89 |
90 | ```cpp
91 | int main()
92 | {
93 | unique_ptr ptr1 = make_unique(5);
94 |
95 | // ptr1 points to 5 (dynamically allocated on the heap)
96 |
97 | {
98 |
99 | unique_ptr ptr2 = ptr1; // shallow copy
100 |
101 | } // <-- data for ptr2 deallocated here
102 |
103 | std::cout << *ptr1 << std::endl;
104 | return 0;
105 | }
106 | ```
107 |
108 | Since `ptr1` and `ptr2` point to the same memory, when `ptr2` goes out of scope, it takes `ptr1`'s data with it! As a result, `*ptr1` is undefined behaviour.
109 |
110 | On the other hand, we should still be able to **move** a `unique_ptr`. Recall that move semantics allows us to take ownership of an object's resources without making an expensive copy. Moving a unique pointer is valid because it preserves the uniqueness of the pointer — at any point in time, we still have only one pointer to the underlying memory. We simply change who (what variable) owns that memory.
111 |
112 | In order to achieve these goals — automatic deallocation of memory, no copying, and move semantics — we must implement some special member functions on the `unique_ptr` class. **Specifically, implement the following SMFs:**
113 |
114 | * `~unique_ptr()`: Deallocates the pointer's memory
115 | * `unique_ptr(const unique_ptr& other)`: Copies a unique pointer. Should be deleted.
116 | * `unique_ptr& operator=(const unique_ptr& other)`: Copy assigns a unique pointer. Should be deleted.
117 | * `unique_ptr(unique_ptr&& other)`: Moves a unique pointer.
118 | * `unique_ptr& operator=(unique_ptr&& other)`: Move assigns a unique pointer.
119 |
120 | After implementing the above functions, you should be passing all of the autograder tests for **Part 1**.
121 |
122 | > [!IMPORTANT]
123 | > ##### `short_answer.txt`
124 | > **Q2:** When implementing move semantics for a `unique_ptr`, for example in the move constructor `unique_ptr(unique_ptr&& other)`, it is essential that we set the underlying pointer of the `other` parameter to `nullptr` before exiting the function. Explain in your own words what problem would arise if we did not.
125 |
126 | ## Part 2: Using `unique_ptr`
127 |
128 | Now that we have a `unique_ptr` implementation, let's use it! Take a look at `main.cpp`. We have given you a complete implementation of a singly-linked list (`ListNode`) that utilizes `unique_ptr` to ensure that all nodes in the list get deallocated properly. For example, this code produces the following output:
129 |
130 | ```cpp
131 | int main()
132 | {
133 |
134 | auto head = cs106l::make_unique>(1);
135 | head->next = cs106l::make_unique>(2);
136 | head->next->next = cs106l::make_unique>(3);
137 |
138 | // memory of head:
139 | //
140 | // head -> (1) -> (2) -> (3) -> nullptr
141 | //
142 | //
143 |
144 | } // <- `head` destructed here!
145 |
146 | // Output:
147 | // Constructing node with value '1'
148 | // Constructing node with value '2'
149 | // Constructing node with value '3'
150 | // Destructing node with value '1'
151 | // Destructing node with value '2'
152 | // Destructing node with value '3'
153 | ```
154 |
155 | Notice that we didn't have to make any calls to `delete`! The RAII behaviour of `unique_ptr` guarantees that all memory in the list is deallocated recursively. When `head` goes out of scope, it calls the destructor of node `(1)`, which calls the destructor of `(2)`, which calls the destructor of `(3)`.
156 |
157 | > [!IMPORTANT]
158 | > ##### `short_answer.txt`
159 | > **Q3:** This method of recursive deallocation through RAII works great for small lists, but may pose a problem for longer lists. Why? Hint: what is the limit for how "deep" a recursive function's call stack can grow?
160 |
161 | **Your task is to implement the function `create_list` which converts a `std::vector` into a `unique_ptr>`.** The order of elements in the vector should be preserved in the list, and `nullptr` should be returned for an empty vector. There are many ways you could go about this; one is to construct the list in reverse (starting at the tail and working towards the head). **Note that you must use the `cs106l::unique_ptr` under the `cs106l` namespace, and not the `std::unique_ptr`!** Here is an algorithm you should follow in your implementation:
162 |
163 | 1. Initialize a `cs106l::unique_ptr> head = nullptr`.
164 | 2. Iterate through the `std::vector` **backwards.** For each element in the vector:
165 | - 2a. Create a new `cs106l::unique_ptr> node` whose value is the element in the vector.
166 | - 2b. Set `node->next` to `head`.
167 | - 2c. Set `head` to `node`
168 | 3. Finally, return `head`
169 |
170 | > [!IMPORTANT]
171 | > ##### `short_answer.txt`
172 | > **Q4.** In your implementation of points 2b and 2c, you may have a hard time getting the compiler to allow you to assign, for example, `node->next` to `head` as it will complain that there is no copy assignment operator. That is exactly right, as `unique_ptr` cannot be copied as we discussed previously!
173 | >
174 | > In order to get the behaviour we want, we must force the compiler to **move assign** `head` into `node->next` rather than copy assign. Recall from the move semantics lecture that we can do this by writing `node->next = std::move(head)`.
175 | >
176 | > What does `std::move` do in this context? Why is it safe to use `std::move` and move semantics here?
177 |
178 | > [!NOTE]
179 | > Be careful of trying to use `size_t` as an index while looping backwards through a vector. `size_t` can only be a non-negative integer, and attempting to go below zero while checking the for loop bounds can lead to unexpected behaviour.
180 | > To fix this issue, try using an `int` instead.
181 |
182 | Once you've implemented `create_list`, we can now create a list and print it out. For brownie points, take a look at the `map_list()` and `linked_list_example()` functions which together call your `create_list` function and print out its elements each on their own line. At this point, you should pass all of the tests in **Part 2**.
183 |
184 | ## 🚀 Submission Instructions
185 |
186 | Before you submit the assignment, please fill out this [short feedback form](https://forms.gle/TXzLEgdKYnEPes22A). **Completion of the form is required to receive credit for the assignment.** After filling out the form, please upload the files to Paperless under the correct assignment heading.
187 |
188 | Your deliverable should be:
189 |
190 | - `unique_ptr.h`
191 | - `main.cpp`
192 | - `short_answer.txt`
193 |
194 | You may resubmit as many times as you'd like before the deadline.
195 |
--------------------------------------------------------------------------------
/assignment7/autograder/autograder.py:
--------------------------------------------------------------------------------
1 | import functools
2 | from typing import List
3 | from utils import Autograder, ASSIGNMENT_DIR
4 |
5 | import os
6 | import subprocess
7 |
8 |
9 | def get_main_exe_path() -> os.PathLike:
10 | main_path = os.path.join(ASSIGNMENT_DIR, "main")
11 | if not os.path.isfile(main_path):
12 | main_path = os.path.join(ASSIGNMENT_DIR, "main.exe")
13 | if not os.path.isfile(main_path):
14 | raise RuntimeError(
15 | "Could not find a main executable. Did you compile your code with the command in the README?"
16 | )
17 | return main_path
18 |
19 |
20 | @functools.lru_cache
21 | def get_memory_leak_exit_code() -> int:
22 | main_path = get_main_exe_path()
23 | result = subprocess.check_output([main_path, "memory_leak_exit_code"], text=True)
24 | return int(result.strip())
25 |
26 |
27 | def verify_output(
28 | test_case: str, *, expected: List[str] = [], hint: str = "", empty_hint: str = "", show_diff: bool = True
29 | ):
30 | hint = hint.strip()
31 | has_hint = bool(hint)
32 | if has_hint:
33 | hint += " "
34 |
35 | main_path = get_main_exe_path()
36 | result = subprocess.run(
37 | [main_path, test_case],
38 | stdout=subprocess.PIPE,
39 | stderr=subprocess.PIPE,
40 | text=True,
41 | )
42 |
43 | if result.returncode == get_memory_leak_exit_code():
44 | raise RuntimeError(f"💧Memory leak detected: {result.stderr}")
45 |
46 | if result.returncode != 0:
47 | error = result.stderr.strip()
48 | if not error and empty_hint:
49 | raise RuntimeError(empty_hint)
50 | if has_hint:
51 | if error:
52 | raise RuntimeError(f"{hint}Got the following error message:\n\n{error}")
53 | raise RuntimeError(f"{hint}")
54 | raise RuntimeError(error)
55 |
56 | actual = result.stdout.strip()
57 | if not actual:
58 | actual = []
59 | else:
60 | actual = actual.split("\n")
61 |
62 | if actual != expected:
63 | nl = "\n\t"
64 | if show_diff:
65 | raise RuntimeError(
66 | f"Test output did not match expected. Expected: \n{nl}{nl.join(expected)}\n\nGot: \n{nl}{nl.join(actual)}\n\n{hint}"
67 | )
68 | raise RuntimeError(hint)
69 |
70 |
71 | if __name__ == "__main__":
72 | grader = Autograder()
73 | grader.add_part(
74 | "Part 1: Destructor",
75 | lambda: verify_output("destructor", hint="Did you make sure to set the underlying pointer to `nullptr` in the `nullptr` version of the constructor? If you didn't, you might be trying to delete an arbitrary region of memory!"),
76 | )
77 |
78 | grader.add_part(
79 | "Part 1: Deleted copy constructor and assignment", lambda: verify_output("copy")
80 | )
81 |
82 | grader.add_part(
83 | "Part 1: Move constructor",
84 | lambda: verify_output(
85 | "move_constructor",
86 | expected=["400308000"],
87 | hint="Did you make sure to set the other unique_ptr to nullptr in the move constructor? It may help to review the lecture on move semantics!",
88 | show_diff=False,
89 | ),
90 | )
91 |
92 | grader.add_part(
93 | "Part 1: Move assignment",
94 | lambda: verify_output(
95 | "move_assignment",
96 | hint="Did you make sure to deallocate the existing data before assigning the private fields? It may help to review the lecture on move semantics!",
97 | show_diff=False,
98 | ),
99 | )
100 |
101 | grader.add_part(
102 | "Part 1: Move assignment (self-assignment)",
103 | lambda: verify_output(
104 | "move_self_assignment",
105 | hint="Did you make sure to check for self-assignment in the move assignment operator? You should have code that checks `if (this == &other) ...`. It may help to review the lecture on move semantics!",
106 | expected=["400308000"],
107 | show_diff=False,
108 | ),
109 | )
110 |
111 | grader.add_part(
112 | "Part 2: Linked List",
113 | lambda: verify_output(
114 | "linked_list",
115 | expected=[
116 | "Constructing node with value 'Sean'",
117 | "Constructing node with value 'Chris'",
118 | "Constructing node with value 'Keith'",
119 | "Constructing node with value 'Fabio'",
120 | "Constructing node with value 'Jacob'",
121 | "Jacob",
122 | "Fabio",
123 | "Keith",
124 | "Chris",
125 | "Sean",
126 | "Destructing node with value 'Jacob'",
127 | "Destructing node with value 'Fabio'",
128 | "Destructing node with value 'Keith'",
129 | "Destructing node with value 'Chris'",
130 | "Destructing node with value 'Sean'",
131 | ],
132 | empty_hint="Hmm... that didn't work. Make sure that you are passing all of the previous tests! You might have an issue with your `unique_ptr` implementation."
133 | ),
134 | )
135 |
136 | grader.run(),
137 |
--------------------------------------------------------------------------------
/assignment7/autograder/diagnostics.hpp:
--------------------------------------------------------------------------------
1 | /** Contains memory diagnostics to track memory allocations and report memory leaks. */
2 |
3 | #pragma once
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | namespace MemoryDiagnostics {
13 | namespace detail {
14 |
15 | /**
16 | * In order to initialize an unordered_map inside of the MemoryTracker class below,
17 | * we must make dynamic allocations. However, the default allocator for unordered_map
18 | * calls operator new and delete, which would cause infinite recursion. To avoid this,
19 | * we define a custom allocator that uses std::malloc and std::free instead and define
20 | * the unordered_map in MemoryTracker to use this allocator.
21 | *
22 | * See https://en.cppreference.com/w/cpp/named_req/Allocator for more information.
23 | */
24 | template struct Mallocator {
25 | using value_type = T;
26 |
27 | Mallocator() = default;
28 |
29 | template constexpr Mallocator(const Mallocator