From 153a2dbc21a72db33cc156f6a17b7e7c0fa60671 Mon Sep 17 00:00:00 2001 From: Winston Liu Date: Sun, 3 May 2020 23:43:23 -0400 Subject: [PATCH 1/6] Lab 5: Add hint about rotations --- labs/lab05/index.html | 9 +++++++-- labs/lab05/index.md | 13 ++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/labs/lab05/index.html b/labs/lab05/index.html index 696ef9a86..fae84b3fc 100644 --- a/labs/lab05/index.html +++ b/labs/lab05/index.html @@ -120,8 +120,10 @@

Test Files

Submission

You should submit your BST, any supporting C++ files, as well as a Makefile to compile everything into an a.out executable.

Hints

-

Private methods: Following the suggestion to implement private versions of all the public methods that take in BinaryNodes will help considerably. How should your public method interact with the private version? In other words, what node should the public method pass in?

-

Passing pointers by reference: When a pointer is passed by reference, that allows us to change not only the data that the pointer is pointing to, but also what the pointer is pointing to in the first place. This is one option that allows you to change the structure of your tree without having to use parent pointers.

+

Use private methods

+

Following the suggestion to implement private versions of all the public methods that take in BinaryNodes will help considerably. How should your public method interact with the private version? In other words, what node should the public method pass in?

+

Pass pointers by reference

+

When a pointer is passed by reference, that allows us to change not only the data that the pointer is pointing to, but also what the pointer is pointing to in the first place. This is one option that allows you to change the structure of your tree without having to use parent pointers.


Post-lab

The objective of this post-lab is to understand the run-time characteristics and trade-offs between normal Binary search trees and AVL trees. You will have to implement an AVL tree and write a brief report to compare its performance with the Binary search tree implemented for the in-lab.

@@ -158,5 +160,8 @@

Balancing

endif rotate right on node endif +

Rotating

+

As was the case for Lab 2's insertion methods, it is extremely helpful to draw out how each node changes during the rotation before attempting to write the C++ implementation.

+

Did you take into account that the parent should now be pointing to a different node? What about the heights of each node?

diff --git a/labs/lab05/index.md b/labs/lab05/index.md index 2bbb2954a..dd781d7b1 100644 --- a/labs/lab05/index.md +++ b/labs/lab05/index.md @@ -151,12 +151,14 @@ You should submit your BST, any supporting C++ files, as well as a Makefile to c ### Hints ### -**Private methods:** Following the suggestion to implement private +#### Use private methods #### +Following the suggestion to implement private versions of all the public methods that take in BinaryNodes will help considerably. How should your public method interact with the private version? In other words, what node should the public method pass in? -**Passing pointers by reference:** When a pointer is passed by +#### Pass pointers by reference #### +When a pointer is passed by reference, that allows us to change not only the data that the pointer is pointing to, but also _what the pointer is pointing to in the first place_. This is one option that allows you to change the structure of @@ -198,7 +200,6 @@ You should submit your AVL tree, any supporting C++ files, the Makefile, and you ### Hints ### #### Balancing #### - `balance(AVLNode*& node)` is crucial for both insert and remove, but has many cases that you must account for. To help avoid potential issues, here is some pseudocode for the balance method that you may use: ``` @@ -218,3 +219,9 @@ balance(node): rotate right on node endif ``` + +#### Rotating #### +As was the case for Lab 2's insertion methods, it is extremely helpful to draw out how each node changes during the rotation _before_ attempting to write the C++ implementation. + +Did you take into account that the parent should now be pointing to a different node? +What about the heights of each node? From 678888ac5236b0c5056bb0c1352c2f1b39bce757 Mon Sep 17 00:00:00 2001 From: Winston Liu Date: Sun, 3 May 2020 23:44:14 -0400 Subject: [PATCH 2/6] Lab 6: Add hint about duplicate words And explicitly state that unordered_set/map cannot be used --- labs/lab06/index.html | 9 +++++---- labs/lab06/index.md | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/labs/lab06/index.html b/labs/lab06/index.html index 37100bed2..d86398c22 100644 --- a/labs/lab06/index.html +++ b/labs/lab06/index.html @@ -75,7 +75,7 @@

Overview

  • Take in a dictionary file and grid file, in that order, using command line parameters
  • Read in a dictionary and store its words in a hash table
      -
    • Which is implemented without using a vector of vectors or any STL hash table
    • +
    • Which is implemented without using a vector of vectors, unordered_set, or unordered_map
  • Read in a word search grid no larger than 500x500 and store it in an appropriate data structure
  • Output every word of length three or greater and its location in the grid @@ -87,7 +87,7 @@

    Overview

    We are not as interested in how fast your program runs for the pre-lab; leave any optimizations for the post-lab.

    Storing the dictionary

    As there may be a large number of words in the dictionary, you will have to store those words in a hash table to facilitate efficient lookup.

    -

    You must write your own hash table for this lab. We leave the implementation up to you, as long as you do not use a vector of vectors or any STL hash table.

    +

    You must write your own hash table for this lab. We leave the implementation up to you, as long as you do not use a vector of vectors, unordered_set, or unordered_map.

    Manipulating the grid

    getWordInGrid.cpp (src) provides two useful functions: readInGrid() and getWordInGrid(). The former reads in a grid file using C++ streams; the latter returns a word in a 2D grid of letters in a given direction.

    You should make sure you understand how these functions work, as you will most likely be using them in your final program.

    @@ -123,8 +123,9 @@

    That hint didn't

    Start off by reading in the dictionary and printing out all of its words. Then store those words in a hashtable (probably the STL one for now). Great, you have a dictionary now.

    Next, read in the grid (use the functions in getWordInGrid.cpp to help you!). Plan out how to use the dictionary to find all the valid words in the grid. There are four variables that control what word is returned -- what does that imply in terms of loops?

    At this point, hopefully you have enough to get you going.

    -

    Storing the grid

    -

    Creating a dynamic 2D array in C++ is more difficult than it should be -- one solution is to create a vector of vectors, but that is not the most efficient means to do it. For this lab, you can just create a 500x500 static array, and can assume that you will not have your program run on larger input grids. This is not very elegant, but it will work until we go over dynamic array creation in lecture.

    +

    It almost works...but now I'm getting duplicate words

    +

    Take a closer look at the documentation for the getWordInGrid function!
    +In particular, does it say anything about what it returns when it reaches the end of the grid? How might you account for that in your code?

    Command-line parameters

    Your program must be able to run successfully if started with ./a.out <dictionary_file> <grid_file> and no input.

    See the slide set on arrays if you need a refresher on command-line parameters; you can also see the cmdlineparams.cpp (src) file.

    diff --git a/labs/lab06/index.md b/labs/lab06/index.md index 2ed1147ee..877ef3352 100644 --- a/labs/lab06/index.md +++ b/labs/lab06/index.md @@ -82,7 +82,7 @@ Your word search solver, implemented in `wordPuzzle.cpp`, should: - Take in a dictionary file and grid file, in that order, using command line parameters - Read in a dictionary and store its words in a hash table - - Which is implemented without using a vector of vectors or any STL hash table + - Which is implemented without using a vector of vectors, `unordered_set`, or `unordered_map` - Read in a word search grid no larger than 500x500 and store it in an appropriate data structure - Output every word of length three or greater and its location in the grid - The required output format is described below @@ -95,7 +95,7 @@ We are not as interested in how fast your program runs for the pre-lab; leave an As there may be a large number of words in the dictionary, you will have to store those words in a hash table to facilitate efficient lookup. You **must** write your own hash table for this lab. -We leave the implementation up to you, as long as you do not use a vector of vectors or any STL hash table. +We leave the implementation up to you, as long as you do not use a vector of vectors, `unordered_set`, or `unordered_map`. ### Manipulating the grid ### @@ -164,10 +164,10 @@ There are four variables that control what word is returned -- what does that im At this point, hopefully you have enough to get you going. -#### Storing the grid #### -Creating a dynamic 2D array in C++ is more difficult than it should be -- one solution is to create a vector of vectors, but that is not the most efficient means to do it. -For this lab, you can just create a 500x500 static array, and can assume that you will not have your program run on larger input grids. -This is not very elegant, but it will work until we go over dynamic array creation in lecture. +#### It almost works...but now I'm getting duplicate words #### +Take a closer look at the documentation for the `getWordInGrid` function!\ +In particular, does it say anything about what it returns when it reaches the end of the grid? +How might you account for that in your code? #### Command-line parameters #### Your program must be able to run successfully if started with `./a.out ` and no input. From e496454a787b98ed9df3237193e6bf6c74fca83a Mon Sep 17 00:00:00 2001 From: Winston Liu Date: Wed, 6 May 2020 16:33:03 -0400 Subject: [PATCH 3/6] Remove confusing "reduce # of instructions" note --- labs/lab09-64bit/index.html | 4 +--- labs/lab09-64bit/index.md | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/labs/lab09-64bit/index.html b/labs/lab09-64bit/index.html index 4fa182f1e..d7bc14564 100644 --- a/labs/lab09-64bit/index.html +++ b/labs/lab09-64bit/index.html @@ -105,9 +105,8 @@

    Testing and timing

    You should use an appropriate precision number to make sure you don't report zero when you divide the total time by the number of runs. Your timer code should only include the loop of n times that calls the routine with x as the parameter. Nothing else (including the print statement) should be inside the timer code.

    Optimization

    Now that you can time your program, you will need to optimize it as much as possible. Any optimization is valid, as long as it computes the correct result, is still a recursive subroutine, and follows the C calling convention. The grade on this pre-lab will be based both on the correctness of the subroutine and the optimizations included.

    -

    What optimizations do you use?

    +

    Put the optimizations used as a comment in the beginning of your assembly file. Here are a few suggestions:

      -
    • First, try to figure out how you can write the same routine using fewer x86 instructions. x86 has lots of complex instructions that can be used for this purpose -- Google is your friend, here.
    • lea can quickly add and multiply numbers in one instruction.
    • Multiplication and division are expensive. Try to use bit shifts whenever possible -- 4*x+x is likely to be faster than 5*x, as the former can be optimized to x << 2 + x.
    • Branching slows down the execution speed of a program as the branch condition must be checked every iteration. As much as possible, eliminate branching (if/else statements, loops, etc.). For loops, consider loop unrolling.
    • @@ -116,7 +115,6 @@

      Optimization

    • Reduce the registers that are backed up to the stack in the calling convention
    • Many optimizations are listed here, but most would not apply to this one program.
    -

    You will need to include at least one optimization beyond just figuring out how to write your subroutine with fewer instructions. You should put the optimizations used as a comment in the beginning of your assembly file.

    Note that we, too, can write the function in C++ and compile it with clang++ -O2 -S -mllvm --x86-asm-syntax=intel. And we will be looking at that assembly code when we grade the pre-lab. If you write your program this way, it constitutes an honor violation, so please hand-code the assembly yourself.

    You must list, as comments in your assembly file, the optimizations that you used! Just a brief description of what optimizations you used is fine.

    Different Architectures

    diff --git a/labs/lab09-64bit/index.md b/labs/lab09-64bit/index.md index 75eb77ea0..2ceae661d 100644 --- a/labs/lab09-64bit/index.md +++ b/labs/lab09-64bit/index.md @@ -114,9 +114,9 @@ You should use an appropriate precision number to make sure you don't report zer Now that you can time your program, you will need to optimize it as much as possible. Any optimization is valid, as long as it computes the correct result, is still a recursive subroutine, and follows the C calling convention. The grade on this pre-lab will be based both on the correctness of the subroutine and the optimizations included. -What optimizations do you use? +Put the optimizations used as a comment in the beginning of your assembly file. +Here are a few suggestions: -- First, try to figure out how you can write the same routine using fewer x86 instructions. x86 has lots of complex instructions that can be used for this purpose -- Google is your friend, here. - `lea` can quickly add and multiply numbers in one instruction. - Multiplication and division are expensive. Try to use bit shifts whenever possible -- `4*x+x` is likely to be faster than `5*x`, as the former can be optimized to `x << 2 + x`. - Branching slows down the execution speed of a program as the branch condition must be checked every iteration. As much as possible, eliminate branching (if/else statements, loops, etc.). For loops, consider [loop unrolling](http://en.wikipedia.org/wiki/Loop_unrolling). @@ -125,8 +125,6 @@ What optimizations do you use? - Reduce the registers that are backed up to the stack in the calling convention - Many optimizations are listed [here](http://en.wikipedia.org/wiki/Category:Compiler_optimizations), but most would not apply to this one program. -You will need to include at least one optimization beyond just figuring out how to write your subroutine with fewer instructions. You should put the optimizations used as a comment in the beginning of your assembly file. - Note that we, too, can write the function in C++ and compile it with `clang++ -O2 -S -mllvm --x86-asm-syntax=intel`. And we will be looking at that assembly code when we grade the pre-lab. If you write your program this way, it constitutes an honor violation, so please hand-code the assembly yourself. **You must list, as comments in your assembly file, the optimizations that you used!** Just a brief description of what optimizations you used is fine. From 690e0e427f9f3f44207724e7ec5786e6c760fb8a Mon Sep 17 00:00:00 2001 From: Winston Liu Date: Wed, 6 May 2020 16:55:45 -0400 Subject: [PATCH 4/6] Add hints for Postlab 9 --- tutorials/09-c/index.html | 18 +++++++++++++----- tutorials/09-c/index.md | 30 +++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/tutorials/09-c/index.html b/tutorials/09-c/index.html index e375e8be2..4b6865b52 100644 --- a/tutorials/09-c/index.html +++ b/tutorials/09-c/index.html @@ -166,7 +166,7 @@

    free()

    Any one of these errors will corrupt your heap. This corruption will manifest in unusual ways which will be very difficult to debug and which will not have any obvious relationship with the root cause (if you are lucky, it will cause a segmentation fault).

    The easiest way to debug a memory error is not to make it in the first place. With care, this is easier than it sounds. Firstly, know when you need dynamic allocation; don't use it if you don't have to. Secondly, as you would do in C++, write constructors and destructors for all of your data structures, and be consistent about using them. These functions should handle your heap control and error checking explicitly, so that they are implicit in the code that uses the storage.

    -

    If you do manage to develop memory errors, AddressSanitizer and LeakSanitizer work just as they did in C++.

    +

    If you do run into memory errors, AddressSanitizer and LeakSanitizer work just as they did in C++.

    Examples

    #include <stdio.h>
     #include <stdlib.h>
    @@ -271,10 +271,7 @@ 

    Exercise

    This exercise is to be developed in C, and compiled using clang (NOT clang++!). The exercise is to implement a very simple linked list. Your program should do the following:

    1. Read in an integer, which we'll call n
    2. -
    3. Read in n more ints, and put those into a linked list -
        -
      • The linked list must be dynamically allocated
      • -
    4. +
    5. Read in n more ints, and insert those into a dynamically allocated linked list
    6. Print out that linked list (we don't care about the order!)
    7. Properly deallocate the linked list
    @@ -289,5 +286,16 @@

    Exercise

    6 4 2
    +

    Hints

    +

    Keep it simple

    +

    A singly-linked list will probably cause you less trouble than a doubly-linked list. We really are looking for the bare minimum, here :).

    +

    Efficient insertion

    +

    While intuitively, you might want to add each node to the end of the list, take a moment to consider whether that's actually necessary.

    +

    Would it be equally as valid to prepend to the beginning of the list, since you always know where your head is?

    +

    In other words...from head -> node1, could we insert node2 as head -> node2 -> node1? Take a look at your insertAtTail all the way back from Lab 2 for some inspiration.

    +

    I thought delete was bad enough...how do I free my list?

    +

    Like in Lab 2, you'll want to iterate through your list, freeing every node as you go. Make sure you know what the next node is though before you free the one you're on!

    +

    Depending on your implementation, it might be easier to separate this from the loop where you print all the values.

    +

    Additionally, if you malloc anything else (a LinkedList struct, or an iterator, for example), you'll also need to free those at the end.

    diff --git a/tutorials/09-c/index.md b/tutorials/09-c/index.md index 5c5a53af7..f2a4b516a 100644 --- a/tutorials/09-c/index.md +++ b/tutorials/09-c/index.md @@ -168,7 +168,7 @@ Any one of these errors will corrupt your heap. This corruption will manifest in The easiest way to debug a memory error is not to make it in the first place. With care, this is easier than it sounds. Firstly, know when you need dynamic allocation; don't use it if you don't have to. Secondly, as you would do in C++, write constructors and destructors for all of your data structures, and be consistent about using them. These functions should handle your heap control and error checking explicitly, so that they are implicit in the code that uses the storage. -If you do manage to develop memory errors, AddressSanitizer and LeakSanitizer work just as they did in C++. +If you do run into memory errors, AddressSanitizer and LeakSanitizer work just as they did in C++. ### Examples ### @@ -337,8 +337,7 @@ Exercise This exercise is to be developed in C, and compiled using clang (NOT clang++!). The exercise is to implement a ***very simple*** linked list. Your program should do the following: 1. Read in an integer, which we'll call *n* -2. Read in *n* more ints, and put those into a linked list - - The linked list must be dynamically allocated +2. Read in *n* more ints, and insert those into a dynamically allocated linked list 3. Print out that linked list (we don't care about the order!) 4. Properly deallocate the linked list @@ -357,3 +356,28 @@ Enter value 4: 8 4 2 ``` + +### Hints ### + +#### Keep it simple #### +A singly-linked list will probably cause you less trouble than a doubly-linked list. +We really are looking for the bare minimum, here :). + +#### Efficient insertion #### +While intuitively, you might want to add each node to the end of the list, +take a moment to consider whether that's actually necessary. + +Would it be equally as valid to _prepend_ to the beginning of the list, +since you always know where your head is? + +In other words...from `head -> node1`, could we insert node2 as `head -> node2 -> node1`? +Take a look at your `insertAtTail` all the way back from Lab 2 for some inspiration. + +#### I thought delete was bad enough...how do I free my list? #### +Like in Lab 2, you'll want to iterate through your list, freeing every node as you go. +Make sure you know what the next node is though before you free the one you're on! + +Depending on your implementation, it might be easier to separate this from the loop where you print all the values. + +Additionally, if you malloc anything else (a LinkedList struct, or an iterator, for example), +you'll also need to free those at the end. From 3db6d91e9f49dd49756f657547e732ace5b531e0 Mon Sep 17 00:00:00 2001 From: Winston Liu Date: Wed, 6 May 2020 17:15:54 -0400 Subject: [PATCH 5/6] Add hints for inlab 11 --- labs/lab11/index.html | 7 +++++-- labs/lab11/index.md | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/labs/lab11/index.html b/labs/lab11/index.html index cabaedee2..819cfc776 100644 --- a/labs/lab11/index.html +++ b/labs/lab11/index.html @@ -151,8 +151,6 @@

    How to proceed

    Your final program should should print out the shortest path as the last thing printed. You can print out multiple paths as you find the shortest one, but you should NOT print out every path you try.

    Note that you are determining a cycle of cities to visit. So if your cycle has the cities in reverse, then it's still a valid solution.

    -

    Getting your itinerary correct

    -

    The starting city is not to be permuted, as you will always start (and end) at that city. It's the other cities that are going to be permuted through the calls to next_permutation().

    Timing your code

    Keep in mind that as you increase the size of the city tour, the running time increases exponentially. Modern-day computers can probably compute about 200,000 routes per second (with well written and optimized code). Our 10-route cycle took 18 seconds. A 15 route cycle would take 2.5 months. A 20 route cycle would take 385,734 years! Realistically, you shouldn't be trying anything with an itinerary greater than 9 or 10.

    And when you are planning on testing long paths, you should really compile your code with the -O2 compiler option. It can speed up the program by a factor of two.

    @@ -187,6 +185,11 @@

    Sample output

    Your final program needs to both be able to compile and run with the specified command-line parameters.

    Makefile

    Your Makefile should have only one target, which you can name anything you want. This target should do two things: compile your code, and run doxygen. You can have two tabbed lines after the target specifier, which is the easiest way to accomplish this. In other words, we are just going to call make, and we want it to both compile your code and create your doxygen documentation. The in-lab Makefile should have the same dual-purpose target.

    +

    Hints

    +

    Getting your itinerary correct

    +

    The starting city is not to be permuted, as you will always start (and end) at that city. It's the other cities that are going to be permuted through the calls to next_permutation().

    +

    Getting your itinerary correct, part 2

    +

    next_permutation assumes that it started out with a sorted vector! Did you sort your itinerary before starting to permute it?


    Post-lab

    There are two parts to the post-lab: a complexity analysis of your code, as well as an investigation into acceleration techniques for the traveling salesperson problem.

    diff --git a/labs/lab11/index.md b/labs/lab11/index.md index 3d384a8c7..ee838963b 100644 --- a/labs/lab11/index.md +++ b/labs/lab11/index.md @@ -201,10 +201,6 @@ Your final program should should print out the shortest path as the last thing p Note that you are determining a cycle of cities to visit. So if your cycle has the cities in reverse, then it's still a valid solution. -### Getting your itinerary correct ### - -The starting city is **not to be permuted**, as you will always start (and end) at that city. It's the *other* cities that are going to be permuted through the calls to `next_permutation()`. - ### Timing your code ### Keep in mind that as you increase the size of the city tour, the running time increases exponentially. Modern-day computers can probably compute about 200,000 routes per second (with well written and optimized code). Our 10-route cycle took 18 seconds. A 15 route cycle would take 2.5 months. A 20 route cycle would take 385,734 years! Realistically, you shouldn't be trying anything with an itinerary greater than 9 or 10. @@ -254,6 +250,18 @@ Your final program needs to both be able to compile and run with the specified c Your Makefile should have **only one** target, which you can name anything you want. This target should do **two** things: compile your code, and run doxygen. You can have two tabbed lines after the target specifier, which is the easiest way to accomplish this. In other words, we are just going to call `make`, and we want it to both compile your code and create your doxygen documentation. The in-lab Makefile should have the same dual-purpose target. +### Hints ### + +#### Getting your itinerary correct #### + +The starting city is **not to be permuted**, as you will always start (and end) at that city. +It's the *other* cities that are going to be permuted through the calls to `next_permutation()`. + +#### Getting your itinerary correct, part 2 #### + +`next_permutation` assumes that it started out with a sorted vector! +Did you sort your itinerary before starting to permute it? + ------------------------------------------------------------ Post-lab From 22db413ee5f96dc30000336faffda9d24a9c8cf2 Mon Sep 17 00:00:00 2001 From: Winston Liu Date: Mon, 11 May 2020 22:53:00 -0400 Subject: [PATCH 6/6] Prelab 9: Admit that we have no hints (yet) --- labs/lab09-64bit/index.html | 3 +++ labs/lab09-64bit/index.md | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/labs/lab09-64bit/index.html b/labs/lab09-64bit/index.html index d7bc14564..6354ad676 100644 --- a/labs/lab09-64bit/index.html +++ b/labs/lab09-64bit/index.html @@ -119,6 +119,9 @@

    Optimization

    You must list, as comments in your assembly file, the optimizations that you used! Just a brief description of what optimizations you used is fine.

    Different Architectures

    See the last lab for details, but all code must be submitted to run under Linux, which is the platform that does the compilation on the submission system.

    +

    Hints

    +

    No hints yet...

    +

    We haven't noticed anything that we feel warrants a hint. If you think something belongs here, please let the professors know!


    In-lab

    The general activity of this in-lab will be to write small snippets of C++ code, compile them so that you can look at the generated assembly code, then make modifications and recompile as needed in order to deduce the representation of a number of C++ constructs, listed below. Remember that we are compiling using clang++ -S -m64 -mllvm --x86-asm-syntax=intel.

    diff --git a/labs/lab09-64bit/index.md b/labs/lab09-64bit/index.md index 2ceae661d..a644c39b8 100644 --- a/labs/lab09-64bit/index.md +++ b/labs/lab09-64bit/index.md @@ -133,6 +133,12 @@ Note that we, too, can write the function in C++ and compile it with `clang++ -O See the [last lab](../lab08/index.html) for details, but all code must be submitted to run under Linux, which is the platform that does the compilation on the submission system. +### Hints ### + +#### No hints yet... #### +We haven't noticed anything that we feel warrants a hint. +If you think something belongs here, please let the professors know! + ------------------------------------------------------------ In-lab