Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • cdis/cs/courses/cs320/s24
  • EBBARTELS/s24
  • kenninger/s24
  • hbartle/s24
  • jvoegeli/s24
  • chin6/s24
  • lallo/s24
  • cbjensen/s24
  • bjhicks/s24
  • JPERLOFF/s24
  • RMILLER56/s24
  • sswain2/s24
  • SHINEGEORGE/s24
  • SKALMAZROUEI/s24
  • nkempf2/s24
  • kmalovrh/s24
  • alagiriswamy/s24
  • SWEINGARTEN2/s24
  • SKALMAZROUEI/s-24-fork
  • jchasco/s24
20 results
Show changes
Showing
with 579 additions and 0 deletions
# Git Merging (and conflicts)
1. In an SSH session, run `export EDITOR=nano` so that `nano` is your default editor for the following practice.
2. Clone the directory first. If you have already cloned it, `cd` into the directory and then run `git pull` to update the directory.
3. Navigate (with `cd`) to `Labs/Lab2/git-conflict` within the semester repo. Run `unzip repo.zip` to create a `repo` directory, which contains an `adder.py` program. (If you cannot run `unzip` correctly, try to install unzip by running `sudo apt-get install unzip`. Enter "9" for the prompt.)
4. `cd` to the `repo` directory and run the program: `python3 adder.py`.
5. Use `ls` and `cat` (or `nano`) to browse the file(s) in the repo.
6. Run `git branch`, making a note of what branch the HEAD is currently on (the `*` indicates the `HEAD`). Make a note of the other branches.
7. Your job is to merge the other branches into the main branch, using `git merge ????` commands. After each merge, check what files are in the directory you're working and what they contain.
**Notes:**
* The first merge you do will be easiest, because it is a roll forward merge. Each of the three branches share a common history with `main`, so `main` can just catch up with the latest commits
* The changes on the `docs` branch are on a separate file (README.txt), so that will never conflict with the other changes
* There will be a conflict once you've tried to merge both `args` and `func-rename`. Resolve it like we did in class.
**Conflict resolution hints:**
* Use `nano` to open the file with the conflict. The file will contain conflicting code. Edit everything so you have the version you want, and the extra characters added by git are removed.
* Whenever you aren't sure of the next step, run `git status` to get the hint about how to "mark resolution" or "conclude the merge"
When you're all done, the `adder.py` program should look like this:
```python
import sys
print("Welcome to the adder program!")
def add(x, y):
print(f"{x} plus {y} is {x+y}")
if len(sys.argv) == 3:
add(int(sys.argv[1]), int(sys.argv[2]))
else:
print("Usage: python3 adder.py <x> <y>")
print("Bye")
```
And it should be runnable like this (promt will vary):
```
PROMPT$ python3 adder.py 3 5
Welcome to the adder program!
3 plus 5 is 8
Bye
```
File added
Labs/Lab2/git-pr/0.png

63.9 KiB

Labs/Lab2/git-pr/1.png

169 KiB

Labs/Lab2/git-pr/2.png

195 KiB

Labs/Lab2/git-pr/3.png

38.7 KiB

# GitLab
In this lab, you'll practice using git. You'll create your own repo, and push changes there.
## Background
A **repo** contains a bunch of commits, and usually a few branches to
label different commits. In order to support collaboration and
offline work, it is common to have multiple copies of the same repo.
For example, say there is a team of 3 people working on an open-source
project. There will probably be six copies of the repo: one on each
person's laptop or virtual machine and one on each person's GitLab
account (with one of the GitLab ones being the primary home for the
code).
Various git commands and tools can be used to syncronize the copies.
For example, running `git push origin test` uploads the `test` branch,
and all the associated commits, to `origin`:
<img src="0.png" width=800>
`origin` is an example of a **remote**, a shorthand name to use instead
of a full URL for a repo somewhere else (like GitLab). When you
`clone` a repo from GitLab, you automatically get a remote called
`origin`, but you can setup more yourself.
`clone` is a one-time thing to make a new copy of a GitLab repo on
your computer and download all the commits/branches/etc. If new
changes are made on the GitLab repo, you can instead run `git pull` to
download these without creating a whole new copy of the repo.
In addition to these three general git commands (**clone**, **pull**, **push**),
GitLab has two key tools for syncing repos:
* **fork**: copy somebody else's GitLab repo to a new repo (called a fork) on your account
* **pull request**: ask somebody to bring some new code you uploaded to your fork back into the main repo
Consider a concrete example of how to use all these commands. Say you find a bug in `pandas`, fix it, and want to share the fix back. You might do the following:
1. Find the pandas repo on GitLab
2. Clone the pandas repo to a copy on your computer
3. Fork the pandas repo to a copy in your own GitLab account
4. Make the change to the copy on your computer
5. Add a remote so that you can push from your computer to your GitLab fork
6. Do a push to upload your changes from your computer to your fork
7. Do a pull request from your fork to the main pandas repo
8. Somebody in charge of main repo will consider your changes, and probably (a) click a button on GitLab to incorporate your changes, or (b) give you feedback to make the code better first, in which case you go back to step 4
## Step 1: SSH Keys
These steps are similar to the previous lab. There, we created SSH keys
on your laptop, allowing you to connect laptop=>VM. Now, we're
creating SSH keys on your VM, allowing you to connect VM=>GitLab.
Connect via SSH to your VM.
Run `ssh-keygen` on your VM and repeatedly hitting `ENTER` to accept
all the defaults (including an empty password).
Run the following and **copy** the output:
```
cat ~/.ssh/id_rsa.pub
```
Select "New SSH key".
Name the key "cs320-vm" (or whatever you like, really) and paste the
contents of `id_rsa.pub` to the "Key" box.
<img src="1.png" width=800>
Click "Add SSH Key" to finish adding it.
## Step 2: Create a Repo
Create a project repo called
"cs320-lab2". Pick the group as your netID.
<img src="2.png" width=800>
This should create a project. Go to that project. Click the **Clone** button, and then click **https**. Copy the commands in it.
<img src="3.png" width=800>
We want to run those in a new directory on your computer. So run this in the terminal:
```
mkdir cs320-lab2
cd cs320-lab2
```
Then paste and run `git clone [what you copied earlier]`. You shouldn't be
asked for a password (if so, double check you did the parts of step
2+3 related to SSH correctly). If you see "Are you sure you want to
continue connecting?", type "yes" and ENTER.
If prompted, you can configure your username/email:
```
git config --global user.name "your_gitLab_username"
git config --global user.email "your_email"
```
Refresh the GitLab page for your repo at https://git.doit.wisc.edu/jwang2775/lab2 (with your username instead of "jwang2775"). You should now see the first commit.
Make another change to README.md on your computer (for example, say
"hello world"), push those changes to GitLab, then refresh the page.
Like this (one step at a time):
```
nano README.md # make some changes, then save
git status
git add README.md
git commit -m 'say hello'
git push
```
Keep in mind `nano` is an in-terminal text editor so you can only use
keyboard shortcuts (not the mouse). Do control-O to write the file.
"^" means CONTROL, and the bottom of the screen should provide hints.
\ No newline at end of file
Labs/Lab2/git-sim/1.png

28.5 KiB

Labs/Lab2/git-sim/2.png

41.2 KiB

# Git Simulator
Let's start by practicing in the Git simulator <a
href="https://tyler.caraza-harter.com/cs320/learnGitBranching/index.html"
target="_blank">here</a>. Try to run commands to get to the following state (if you get stuck, check the [solution here](solution.md)):
<img src="1.png" width=500>
Useful commands for the above problem:
* `git commit`: make a new commit
* `git branch bname`: create a branch named `bname`
* `git checkout bname`: move `HEAD` to the commit referenced by the `bname`
* `git checkout c1`: move `HEAD` to the `c1` commit
* `git merge bname`: merge changes on the `bname` branch into the current branch
* `git branch -D bname`: delete the branch named `bname`
If you have some free time at the end of the lab, you can try out the following challenge.
Try to get to this state (no answer to check for this one, so you'll need to work for it!):
<img src="2.png" width=500>
**Hint:** Start by creating commits on four branches, b1, b2, b3, and b4.
Merge b2 into b1 and b4 into b3. Then merge the two merge commits
with a third merge commit.
# Solution
## Steps:
1. git commit
2. git checkout c1
3. git branch test
4. git checkout test
5. git commit
5. git commit
6. git checkout c3
7. git checkout -b feature
8. git commit
9. git checkout test
10. git merge feature
11. git branch -D feature
## Result:
<img src="./1.png" width=600>
# Lab 2: Git
1. Practice using the [Git Simulator](./git-sim)
2. Try [merging branches](./git-conflict) and conflict resolution
3. Learn how to create [your own repo](./git-pr)
Labs/Lab3/big-o/1.png

24.3 KiB

Labs/Lab3/big-o/2.png

30.6 KiB

Labs/Lab3/big-o/3.png

31.3 KiB

Labs/Lab3/big-o/4.png

32.3 KiB

# Visualizing Order of Growth
In this part, you'll get to visually experiment with different C
values and lower bounds on N in order to show that a function fits a
certain order of growth. To start, do some imports:
```python
import pandas as pd
import matplotlib
from matplotlib import pyplot as plt
from math import log2, log10, ceil
```
Also remember to do `%matplotlib inline`
Now paste the following code in a cell (you don't need to understand it for the purposes of this lab):
```python
matplotlib.rcParams["font.size"] = 20
def get_ax():
fig, ax = plt.subplots(figsize=(8,6))
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.set_xlim(1, 10)
return ax
def scale_ax():
ax = get_ax()
ax.set_xlabel("N (data size)")
ax.set_ylabel("Steps")
return ax
def plot_func(ax, f, C=1, color="k", label="work"):
start = ax.get_xlim()[0]
width = ax.get_xlim()[1] - ax.get_xlim()[0]
s = pd.Series([],dtype=float)
for i in range(100):
N = start + width * (i+1)/100
s[N] = eval(f)
s.sort_index().plot(ax=ax, color=color, linewidth=3, label=label)
plt.text(s.index[-1], s.iloc[-1], f, verticalalignment='center')
def upper_bound(ax, order, C=1, minN=None):
f = order
if C != 1:
f = "C * (%s)" % order
plot_func(ax, f, C=C, color="r", label="upper bound")
if minN != None:
ax.axvspan(minN, ax.get_xlim()[1], color='0.85')
ax.legend(frameon=False)
```
## Exercise 1: show `N+100` is in `O(N)`.
You need to show that some multiple of `N` is an upper bound on
`N+100` for large values of `N`. Paste+run this code:
```python
ax = scale_ax()
ax.set_xlim(0, 10) # TODO: change upper bound
plot_func(ax, "N + 100")
upper_bound(ax, order="N") # TODO: pass C and minN
```
It should look like this:
<img src="1.png">
Clearly `g(N)=N` is not an upper bound on `f(N)=N+100`, but remember
that you are allowed to do the following:
1. multiple `g(N)` by a constant `C`
2. require that `N` be large
To use your first capability, pass in a `C` value of of 25, changing the call to `upper_bound(...)` to be like this:
```python
upper_bound(ax, order="N", C=25)
```
It should look like this:
<img src="2.png">
Better, but now the red line is only an upper bound for large `N`
values. Let's set a lower bound on `N`, like this:
```python3
upper_bound(ax, order="N", C=25, minN=6)
```
It should look like this:
<img src="3.png">
Great!
<b>In general for all of these exercises, your job is to choose `C` and
`minN` values so that the red line is above the black line in the
shaded portion.</b>
Finally, you should make sure the upper bound keeps holding for large
N values. If this were a math course, you would prove this is true
for all large N values. But since this is a programming course, let's
just make the `xlim` really big and make sure there are no obvious
problems, like this:
```python
ax.set_xlim(0, 1e6) # 1 million
```
Looks good!
<img src="4.png">
## Exercise 2: show `100*N` is in `O(N)`.
Start with this:
```python
ax = scale_ax()
ax.set_xlim(0, 10)
plot_func(ax, "100*N")
upper_bound(ax, order="N")
```
Do you need to set `minN` for this one, or is choosing the right `C` good enough?
## Exercise 3: show `N**2 + 5*N + 25` is in `O(N**2)`
Start with this:
```python
ax = scale_ax()
ax.set_xlim(0, 10)
plot_func(ax, "N**2 + 5*N + 25")
upper_bound(ax, order="N**2")
```
## Exercise 4: *try* to show `2*N + (N/15)**4` is in `O(N)`
Start with this:
```python
ax = scale_ax()
ax.set_xlim(0, 10)
plot_func(ax, "2*N + (N/15)**4")
upper_bound(ax, order="N")
```
Use `C=3` and `minN=1`.
What happens when you increase the x limit to 75?
```python
ax.set_xlim(0, 75)
```
It turns out `2*N + (N/15)**4` is NOT in `O(N)` after all. What
Big O order of growth does `2*N + (N/15)**4` have?
## Exercise 5: show `log2(N)` is in `O(log10(N))`
Start with this:
```python
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "log2(N)")
upper_bound(ax, order="log10(N)")
```
Any `C` value that is at least `log2(10)` will work here. In general,
any log curve with base M is just a constant multiple of another log
curve with base N. Thus, it is common to just say an algorithm is
`O(log N)`, without bothering to specify the base.
## Exercise 6: show `ceil(log2(N))` is in `O(log N)`
Start with this:
```python
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "ceil(log2(N))")
upper_bound(ax, order="log2(N)")
```
## Exercise 7: show `N * ceil(log2(N))` is in `O(N log N)`
Start with this:
```python
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "N * ceil(log2(N))")
upper_bound(ax, order="N * log2(N)")
```
## Exercise 8: show `F(N) = 0+1+2+...+(N-1)` is in `O(N**2)`
Replace `????` in the following:
```python
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "sum(range(int(N)))")
upper_bound(ax, order=????)
```
Labs/Lab3/files-json/1.png

32.6 KiB

Labs/Lab3/files-json/2.png

46.8 KiB

# Files and JSON
## Writing
Let's try writing a file. Paste the following and run it (it's buggy!):
```python
f = open("file.txt")
f.write("line1")
f.write("line2")
f.close() # you need the parentheses, even without arguments!
```
The code fails because the file wasn't opened in write mode. Add "w"
as a second positional arguments to `open`, then try again.
Does the above code actually produce two lines? Open `file.txt`
through Jupyter and check. Try modifying the "line1" and "line2"
strings to get two different lines, so that `file.txt` looks like this:
```
line_1
line_2
```
## Reading
Create a file named `dog.json` in Jupyter. You can right click in the file browser to create a new file:
<img src="1.png" width=400>
Copy the following:
```json
{
"name": "Fido",
"age": 1
}
```
Paste it into `dog.json` (which you should open with the "Editor"), and be sure to SAVE it:
<img src="2.png" width=600>
Now, back in your notebook, run this:
```python
f = open("dog.json")
input1 = f.read()
input2 = f.read()
f.close()
```
What is `type(input1)`?
Print out `input1`, then print `input2`. Notice that `input2` is the
empty string? That's because the first read call consumes all the
input, and there's nothing left for the second read call. To get the
data again, you could re-open the file, then re-read it again.
Note that `f` is a file object. Calling `.read` is only one way to
get the contents. File objects are iterators, meaning that you could also loop over `f` -- try it:
```python
f = open("dog.json")
for line in f:
print("LINE: " + line, end="")
f.close()
```
You can also create lists from iterators. Try that:
```python
f = open("dog.json")
lines = list(f)
f.close()
print("GOT", len(lines), "lines")
```
## `with`
It's easy to forget to close a file. Python has a special `with`
statement that can do it automatically for you. This:
```python
f = open("dog.json")
lines = list(f)
f.close()
```
Is equivalent to this (try it):
```python
with open("dog.json") as f:
lines = list(f)
# f is automatically closed after the with block
```
## JSON
Your data may be in the following forms:
1. a file object
2. a string
3. a dict (or other Python data type)
`json.load` converts from (1) to (3). `json.loads` converts from (2)
to (3). Fix the following code so it uses the appropriate load
function:
```python
import json
with open("dog.json") as f:
dog = json.loads(f) # fixme
```
Check that `type(dog)` is a `dict`.
Now fix this one too:
```python
data = '{"name": "Fido", "age": 1}'
dog = json.load(data) # fixme
```
And one more:
```python
with open("dog.json") as f:
data = f.read()
dog = json.load(data) # fixme
```