Browse Source

finish hw4, add hw5

master
L.E.R 7 months ago
parent
commit
a2057f7472

+ 2
- 0
hw4/hw4-problems/.gitignore View File

@@ -0,0 +1,2 @@
/target
**/*.rs.bk

+ 178
- 0
hw4/hw4-problems/Cargo.lock View File

@@ -0,0 +1,178 @@
[[package]]
name = "autocfg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "hw4"
version = "0.1.0"
dependencies = [
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "libc"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_jitter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[metadata]
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

+ 8
- 0
hw4/hw4-problems/Cargo.toml View File

@@ -0,0 +1,8 @@
[package]
name = "hw4"
version = "0.1.0"
authors = ["CS 538"]
edition = "2018"

[dependencies]
rand = "0.6.5"

+ 51
- 0
hw4/hw4-problems/rpn.md View File

@@ -0,0 +1,51 @@
# Reverse Polish Notation Calculator

Because it's important to write a calculator in every language, we will produce
a small Reverse Polish Notation calculator in Rust. Here is an intro on RPN:

https://en.wikipedia.org/wiki/Reverse_Polish_notation

The rough idea is that a RPN calculator is a *stack language*: we push things
onto a stack as we parse them, and when we parse operations we pop off the
required number of arguments, apply the operation, and push the result back on
to the stack.

We've supplied much of the code to help you get started. There are two main
files (and two main tasks): `rpn.rs` and `parser.rs`.

## rpn.rs

In this file, you will implement the main data structure for our calculator and
give the evaluation functions. The main data structure is a Stack; you are free
to implement the functions however you like, but a Vector might be a good
choice... (note that Vectors have `push` and `pop` operations).

You will also implement the evaluation function: given a Stack and an operation,
perform the indicated operation on the Stack. Your calculator should support (at
least) the following operations:

* Add (`+`): Add two numbers together. Sample input: `3 5 +` should lead to 8.
* Eq (`=`): Check if two numbers or two booleans are equal. Sample input: `3 5 =` should lead to false.
* Neg (`~`): Negate a boolean. Sample input: `false ~` should lead to true.
* Swap (`<->`): Swap the top two elements of the stack. Sample input: `0 1 <->` should lead to `1 0`.
* Rand (`#`): Produce a random integer from 0 to the top element of the stack. Sample input: `5 #` should lead to a uniform integer in {0, 1, 2, 3, 4}
* Cond (`?`): If-then-else. Looks at the top three elements of the stack, and
does an if-then-else. Sample input: `true 1 2 ?` should lead to 1, and `false 1 2 ?` should lead to 2.
* Quit (`quit`): Quit the calculator.

Your calculator should also accept numbers and boolean constants: `true` and `false`.

To implement the Rand operation, we will use the `rand` crate. Taking a quick
look at the basic examples should be more than enough:

https://docs.rs/rand/0.6.5/rand/

We've already added the `rand` dependency to the Cargo.toml file; take a look if
you're curious.

## parser.rs

This file contains the parser and main read-eval-print loop. We'll be relying on
standard libraries for parsing. You will write the main loop of the evaluator,
which will take in a token and update the stack/apply the indicated operation.
You shouldn't need a lot of code here---the hard work is in the other file.

+ 16
- 0
hw4/hw4-problems/src/main.rs View File

@@ -0,0 +1,16 @@
#![allow(dead_code)]
#![forbid(unsafe_code)]

mod problem1;
mod problem2;
mod problem3;
mod problem4;

mod parser;
mod rpn;

fn main() {
if let Err(err) = parser::rpn_repl() {
println!("Error: {:?}", err);
}
}

+ 94
- 0
hw4/hw4-problems/src/parser.rs View File

@@ -0,0 +1,94 @@
/*
* CS 538, Spring 2019: HW4
*
* Reverse Polish Notation: parser.rs
* See `rpn.md` for the overview.
*/

extern crate rand;

use std::io::{self, Write};

use super::rpn;

pub fn rpn_repl() -> rpn::Result<()> {
let mut stack = rpn::Stack::new();
let mut input = String::new();

// Read-eval-print loop
loop {
// Clear the input buffer
input.clear();

// Prompt the user
print!("> ");
io::stdout().flush().map_err(rpn::Error::IO)?;

// Read a line and evaluate it
io::stdin().read_line(&mut input).map_err(rpn::Error::IO)?;
evaluate_line(&mut stack, &input)?;

// A successful run should end with a stack with a exactly one item: the result
let res = stack.pop()?;
if stack.empty() {
println!("Reply> {:?}", res);
} else {
return Err(rpn::Error::Extra);
}
}
}

fn evaluate_line(stack: &mut rpn::Stack, buf: &String) -> rpn::Result<()> {
// println!("Evaluating Input: {}", buf);
// Trim whitespace and split; this gives an iterator of tokens.
let tokens = buf.trim().split_whitespace();

/*
* Write the main loop processing the tokens. The `parse` method for Strings will be useful for
* parsing integers. See here for examples:
*
* https://doc.rust-lang.org/std/primitive.str.html#method.parse
*/
for tok in tokens {
// println!("Current Tok: {}", tok);
match tok {
"quit" => {
stack.eval(rpn::Op::Quit)?;
}
"+" => {
stack.eval(rpn::Op::Add)?;
}
"=" => {
stack.eval(rpn::Op::Eq)?;
}
"~" => {
stack.eval(rpn::Op::Neg)?;
}
"<->" => {
stack.eval(rpn::Op::Swap)?;
}
"#" => {
stack.eval(rpn::Op::Rand)?;
}
"?" => {
stack.eval(rpn::Op::Cond)?;
}
"true" => {
stack.push(rpn::Item::Bool(true))?;
}
"false" => {
stack.push(rpn::Item::Bool(false))?;
}
_ => {
match tok.parse::<i32>() {
Ok(x) => {
// println!("Pushed one int {}", x);
stack.push(rpn::Item::Int(x))?;
}
Err(_e) => return Err(rpn::Error::Syntax),
}
}
}
}
Ok(())
}

+ 69
- 0
hw4/hw4-problems/src/problem1.rs View File

@@ -0,0 +1,69 @@
/*
* CS 538, Spring 2019: HW4
* Problem 1
*
* Write implementations of the following simple functions. You can use basic Vector operations
* like `push`, `pop`, and `contains`, but do not use the built-in `dedup` or `filter` functions,
* for instance.
*
* Take a look at the Vector documentation for a good intro to Rust Vectors:
*
* https://doc.rust-lang.org/std/vec/struct.Vec.html
*/

/// Compute the sum of a vector of integers
pub fn sum(vs: &Vec<i32>) -> i32 {
let mut sum = 0;
for x in vs {
sum += x;
}
sum
}

/// Return a copy of the input vector, keeping only the first copy of each element.
pub fn dedup(vs: &Vec<i32>) -> Vec<i32> {
let mut res = Vec::new();
for x in vs {
if !res.contains(x) {
res.push(*x);
}
}
res
}

/// Return a copy of the input vector keeping only elements where the predicate is true. The order
/// of elements should not be changed.
pub fn filter(vs: &Vec<i32>, pred: &Fn(i32) -> bool) -> Vec<i32> {
// unimplemented!()
let mut res = Vec::new();
for x in vs {
if pred(*x) {
res.push(*x);
}
}
res
}

/// You can put more tests here.
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_sum() {
let vs = vec![1, 2, 3, 4, 5];
assert_eq!(sum(&vs), 15);
}

#[test]
fn test_dedup() {
let vs = vec![5, 4, 3, 2, 1, 2, 3, 4, 5];
assert_eq!(dedup(&vs), [5, 4, 3, 2, 1]);
}

#[test]
fn test_filter() {
let vs = vec![5, 4, 3, 2, 1, 2, 3, 4, 5];
assert_eq!(filter(&vs, &|i: i32| i % 2 == 1), [5, 3, 1, 3, 5]);
}
}

+ 77
- 0
hw4/hw4-problems/src/problem2.rs View File

@@ -0,0 +1,77 @@
/*
* CS 538, Spring 2019: HW4
* Problem 2
*
* We've defined a type alias for vectors of vectors, which we will use to represent matrices of
* floats. Write a function to multiply two matrices; we've supplied the type signature already.
* Our reference solution assumes that Matrix is a vector of vectors, where each inner vector is a
* row of the matrix. For instance, `mat[1][2]` is the element in the second row, third column
* (remember that indices start at 0). You should follow this representation.
*
* For a brief refresher on matrix multiplication:
*
* https://en.wikipedia.org/wiki/Matrix_multiplication#Definition
*
* Note that not all pairs of matrices can be multiplied: if we are multiplying matrices A * B, the
* number of columns in A must be equal to the number of rows in B. Your program is should panic
* (whether from `panic!`, `assert!`, `assert_eq!`, out of bounds, or whatever) if this condition
* is not satisfied, though in a more polished implementation we would try to handle these errors
* more gracefully.
*
* Also note that indexing with square brackets in Rust is checked. If you access something beyond
* the end of the vector, your program will panic. Don't do this!
*/

pub type Matrix = Vec<Vec<i32>>;

/// Multiply two matrices and return the result in a new matrix
pub fn mat_mult(mat1: &Matrix, mat2: &Matrix) -> Matrix {
assert!(mat1.len() > 0);
assert!(mat2.len() > 0);
assert_eq!(mat1[0].len(), mat2.len());
let mut res: Matrix = vec![vec![0; mat2[0].len()]; mat1.len()];
for i in 0..mat1.len() {
for j in 0..mat2[0].len() {
for k in 0..mat2.len() {
res[i][j] += mat1[i][k] * mat2[k][j];
}
}
}
res
}

/// You can put more tests here.
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_mat_mult() {
let mat1 = vec![vec![2, 3], vec![1, 4]];
let mat2 = vec![vec![7, 3], vec![1, 9]];
let mat3 = vec![vec![3, 23], vec![5, 71]];
let mat4 = vec![vec![7, 18], vec![6, 19]];

let mat5 = vec![vec![1], vec![2]];

let mat6 = vec![vec![8], vec![9]];

assert_eq!(&mat_mult(&mat1, &mat1), &mat4);
assert_eq!(&mat_mult(&mat1, &mat5), &mat6);
assert_eq!(mat_mult(&mat1, &mat2), mat_mult(&mat2, &mat1));
assert_eq!(
mat_mult(&mat_mult(&mat1, &mat2), &mat3),
mat_mult(&mat1, &mat_mult(&mat2, &mat3))
);
}

#[test]
#[should_panic]
fn test_mat_mult_bad() {
let mat1 = vec![vec![2, 3], vec![1, 4]];

let mat2 = vec![vec![1], vec![2]];

mat_mult(&mat2, &mat1);
}
}

+ 52
- 0
hw4/hw4-problems/src/problem3.rs View File

@@ -0,0 +1,52 @@
/*
* CS 538, Spring 2019: HW4
* Problem 3
*
* Write a function that takes in an integer `n`, and returns a vector with all of the prime
* numbers up to `n`. There are many ways to do this; one way is the Sieve of Eratothenes:
*
* https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Overview
*
* But you can choose any way you like.
*/

/// Return the list of all prime numbers up to and including n.
pub fn sieve(n: u32) -> Vec<u32> {
let sz = n as usize;
let mut bprime = vec![true; sz + 1];
bprime[0] = false;
if n >= 1 {
bprime[1] = false;
}

for i in 2..sz + 1 {
if bprime[i] {
let mut sq = i * i;
while sq <= sz {
bprime[sq] = false;
sq += i;
}
}
}

let mut res: Vec<u32> = Vec::new();
for (i, x) in bprime.iter().enumerate() {
if *x {
res.push(i as u32)
}
}
res
}

/// You can put more tests here.
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_sieve() {
let primes_twelve = vec![2, 3, 5, 7, 11];

assert_eq!(sieve(12), primes_twelve);
}
}

+ 71
- 0
hw4/hw4-problems/src/problem4.rs View File

@@ -0,0 +1,71 @@
/*
* CS 538, Spring 2019: HW4
* Problem 4
*
* You will write a small Towers of Hanoi solver. This is a classical puzzle, involving moving a
* set of differently-sized discs from one pole to another pole, while making sure that smaller
* discs sit on top larger discs at all time. See here for some animations:
*
* https://en.wikipedia.org/wiki/Tower_of_Hanoi
*
* We first make a type of pegs: there are three possible pegs, called A, B, C
*/

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Peg {
A,
B,
C,
}

/*
* A move from peg1 to peg2 is represented by a pair (peg1, peg2).
*/
pub type Move = (Peg, Peg);

/*
* Write a program that takes the number of discs (`num_discs`) and three pegs (`src`, `aux`,
* `dst`) and outputs a vector of moves to move `num_discs` discs from `src` to `dst`, using the
* `aux` peg. You don't need to come up with the algorithm. You can implement the one here:
*
* https://en.wikipedia.org/wiki/Tower_of_Hanoi#Simpler_statement_of_iterative_solution
*
* You may assume that `src`, `aux`, and `dst` are all different.
* You may want to write some helper functions to avoid repeating lots of code.
*/

// Helper function for performing a more concise recursive solution
fn hanoi_move(n: u32, src: Peg, dst: Peg, aux: Peg, res: &mut Vec<Move>) {
if n > 0 {
hanoi_move(n - 1, src, aux, dst, res);
res.push((src, dst));
hanoi_move(n - 1, aux, dst, src, res);
}
}

pub fn hanoi(num_discs: u32, src: Peg, aux: Peg, dst: Peg) -> Vec<Move> {
let mut res: Vec<Move> = Vec::new();
hanoi_move(num_discs, src, dst, aux, &mut res);
res
}

/// You can put more tests here.
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_hanoi() {
let moves = vec![
(Peg::A, Peg::C),
(Peg::A, Peg::B),
(Peg::C, Peg::B),
(Peg::A, Peg::C),
(Peg::B, Peg::A),
(Peg::B, Peg::C),
(Peg::A, Peg::C),
];

assert_eq!(hanoi(3, Peg::A, Peg::B, Peg::C), moves);
}
}

+ 195
- 0
hw4/hw4-problems/src/rpn.rs View File

@@ -0,0 +1,195 @@
/*
* CS 538, Spring 2019: HW4
*
* Reverse Polish Notation: rpn.rs
* See `rpn.md` for the overview.
*/

use rand::prelude::*;
use std::collections::LinkedList;
use std::io;

// Stacks will work with Items, which either either integers or booleans
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
pub enum Item {
Int(i32),
Bool(bool),
}

// List of possible errors
#[derive(Debug)]
pub enum Error {
Empty, // Tried to pop empty stack
Extra, // Stack ended with extra elements
Type, // Type mismatch
Syntax, // Syntax error, didn't recognize op
IO(io::Error), // Some kind of IO error
Quit, // User quitting
}

// Base operations supported by calculator, see rpn.md
#[derive(Debug)]
pub enum Op {
Add,
Eq,
Neg,
Swap,
Rand,
Cond,
Quit,
}

// We'll define a result type for our calculator: either a valid value, or a calculator Error
pub type Result<T> = std::result::Result<T, Error>;

// Define a type for Stacks
#[derive(Debug)]
pub struct Stack {
ll: LinkedList<Item>,
}

// Implement the following functions on Stacks
impl Stack {
// Make a new Stack
pub fn new() -> Stack {
Stack {
ll: LinkedList::new(),
}
}

// Check if a Stack is empty
pub fn empty(&self) -> bool {
self.ll.is_empty()
}

// Push an item onto a stack (should never error)
pub fn push(&mut self, item: Item) -> Result<()> {
self.ll.push_back(item);
Ok(())
}

// Pop an item off the Stack; may result in Empty error
pub fn pop(&mut self) -> Result<Item> {
let res = self.ll.pop_back();
match res {
Some(x) => Ok(x),
None => Err(Error::Empty),
}
}

/*
* Main evaluation function: apply an operation to a Stack
*
* Hint: Rust has a nice bit of syntax when writing functions returning Result:
*
* ```
* let x = foo()?;
* ```
*
* If `foo()` returns `Ok(ret)`, `ret` will be assigned to `x`. Otherwise if `foo()` returns
* `Err(e)`, the assignment is not performed and `Err(e)` is returned from the enclosing
* function.
*
* This syntax makes it easier to work with multiple operations that may fail, like:
*
* ```
* let x = foo()?;
* let y = bar(x)?;
* let z = baz(x, y)?;
* ...
* ```
*
* The first one to fail will abort the whole function while returning the error.
*/
pub fn eval(&mut self, op: Op) -> Result<()> {
match op {
Op::Add => {
let x = self.pop()?;
let y = self.pop()?;
if let Item::Int(xx) = x {
if let Item::Int(yy) = y {
self.push(Item::Int(xx + yy))?;
Ok(())
} else {
Err(Error::Type)
}
} else {
Err(Error::Type)
}
}
Op::Eq => {
let x = self.pop()?;
let y = self.pop()?;
if let Item::Int(xx) = x {
if let Item::Int(yy) = y {
self.push(Item::Bool(xx == yy))?;
Ok(())
} else {
Err(Error::Type)
}
} else if let Item::Bool(xx) = x {
if let Item::Bool(yy) = y {
self.push(Item::Bool(xx == yy))?;
Ok(())
} else {
Err(Error::Type)
}
} else {
Err(Error::Type)
}
}
Op::Neg => {
let x = self.pop()?;
if let Item::Bool(xx) = x {
self.push(Item::Bool(!xx))?;
Ok(())
} else {
Err(Error::Type)
}
}
Op::Swap => {
let x = self.pop()?;
let y = self.pop()?;
self.push(x)?;
self.push(y)?;
Ok(())
}
Op::Rand => {
let x = self.pop()?;
if let Item::Int(xx) = x {
if xx >= 0 {
let mut rng = rand::thread_rng();
self.push(Item::Int(rng.gen_range(0, xx)))?;
Ok(())
} else {
Err(Error::Type)
}
} else {
Err(Error::Type)
}
}
Op::Cond => {
let y = self.pop()?;
let x = self.pop()?;
let b = self.pop()?;
if let Item::Bool(bb) = b {
if bb {
self.push(x)?;
} else {
self.push(y)?;
}
Ok(())
} else {
Err(Error::Type)
}
}
Op::Quit => {
// if !self.empty() {
// return Err(Error::Extra);
// }
// Main.rs has that check, supressing here
Ok(())
}
}
}
}

BIN
hw4/wr4-problems.pdf View File


BIN
hw4/wr4-problems/wr4.pdf View File


+ 2
- 0
hw5/hw5-problems/.gitignore View File

@@ -0,0 +1,2 @@
/target
**/*.rs.bk

+ 178
- 0
hw5/hw5-problems/Cargo.lock View File

@@ -0,0 +1,178 @@
[[package]]
name = "autocfg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "hw5"
version = "0.1.0"
dependencies = [
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "libc"
version = "0.2.51"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_jitter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

[metadata]
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

+ 8
- 0
hw5/hw5-problems/Cargo.toml View File

@@ -0,0 +1,8 @@
[package]
name = "hw5"
version = "0.1.0"
authors = ["CS 538"]
edition = "2018"

[dependencies]
rand = "0.6.5"

+ 203
- 0
hw5/hw5-problems/README View File

@@ -0,0 +1,203 @@
# Binary Search Trees

In this assignment, you will design key-value maps based on binary search trees
in Rust. If you need a refresher about how these datastructures work:

https://en.wikipedia.org/wiki/Binary_search_tree

In particular, all of your operations should maintain the central BST invariant:
from any node, all of the keys in the left subtree are less than the key of the
parent, and all of the keys in the right subtree are greater than the key of the
parent. Furthermore, all keys in a BST must be unique---no repetitions.

There are many variants on BSTs that achieve better balancing, like red-black
trees, AVL trees, B-Trees, etc. We won't worry about that too much---our goal is
just a basic BST. We'll already have our hands full with the Rust compiler.

## TreeMap functions

Implement the TreeMap methods in `bst.rs` without changing the function types.
You may use recursion in all functions (but see the extensions section).

## Iterators for TreeMap

In the second part of the assignment, you will design three kinds of iterators
for TreeMaps:

1. Consuming iterator (typically created with `.into_iter()`)
2. Borrowing iterator (typically created with `.iter()`)
3. Mutable iterator (typically created with `.iter_mut()`)

The first iterator consumes the whole TreeMap, returning owned data. The second
iterator "borrows" TreeMap, returning immutable references to data ("borrowed
values"). The third iterator returns *mutable* references to data, so that the
client can update the TreeMap in place. To make sure that the client doesn't
mess up the BST property, we will give out *immutable* references to keys and
*mutable* references to values for this last iterator. The other iterators
should give (key, value) pairs.

If you look for these functions for Rust's BTreeMap's, you'll see that the
standard library implements fancier iterators. For instance, the standard
library iterators are double-ended so you can get elements from the front or
elements from the back. We'll just implement regular iterators.

## Don't panic

Some standard functions in Rust may panic. The function `unwrap()` is one main
example. Avoid using these functions unless you really know that they will not
panic, for instance if you have already checked that the Option is not None.
There are usually better ways of getting values out from Option, like pattern
matching,
[if-let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html),
and
[while-let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html).

## Debugging

For convenience, we've added the Debug trait bound everywhere so you can print
out values. A real version of the library would not have these bounds (since
TreeMaps should work on all types, not just ones with the Debug trait). You're
free to strip the Debug traits when you're done---we won't be testing them.

Rust 1.32 (landed mid January) has a new `dbg!` macro that is quite nice. See
examples [here](https://doc.rust-lang.org/std/macro.dbg.html). Or, you can use
regular `println!` or `eprintln!` with a special formatting token:

```
println!("What is this value: {:?}", thing_with_debug);
```

## Tips

There's no way around it: you're going to meet the borrow checker. After working
at it, you'll get a better idea of what the errors mean as you improve your
mental model of how Rust "works", how the compiler reasons about your program.

You'll want to read the docs carefully (and examples), especially for the
following parts of the stdlib:

* Option: https://doc.rust-lang.org/std/option/index.html

You'll be using the `take` function a lot. The `std::mem::swap` and
`std::mem::replace` functions may also be helpful.

* Box: https://doc.rust-lang.org/std/boxed/index.html

Box is a basic example of a *smart pointer*, something that looks and feels like
a regular reference, but with some special features. The basic idea is that a
Box points to some data located on the heap. Unlike standard references, the Box
owns the data it points to. We use Boxes to define inductive datastructures like
TreeMap, where the total size of the data is not known at compile time.

For now, just treat Box<T> like a regular reference &T or &mut T, depending on
whether the Box is mutable or not. To make a new Box holding something, use the
constructor `Box::new(the_thing)`. If you have a Box `b`, you can dereference it
to get the thing that it's pointing at by doing `*b`. Just like for references,
you can call a method of the boxed thing via `b.foo()`.

These may look like magic, but as we will discuss this behavior and syntax is
enabled by Rust traits (`Deref`). Rust also has some "magic" for Box, special
syntax for this type. For instance, `*b` gives ownership of the boxed thing
whereas usually `*p` only gets a reference to the data.

* BTreeMap: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html

Our API is based off of this collection, though the stdlib implementation is
much fancier (and much faster). Incidentally, the collections module has great
documentation:

https://doc.rust-lang.org/std/collections/index.html

* Indexing traits

These are the standard Rust traits for allowing indexing, things like `map[42]`.

https://doc.rust-lang.org/std/ops/trait.Index.html
https://doc.rust-lang.org/std/ops/trait.IndexMut.html

* Iterator trait: https://doc.rust-lang.org/std/iter/trait.Iterator.html

You need two ingredients to implement this trait: a type of items to return (an
"associated type") and a `next` function, which returns the next item and
updates the iterator state.

* Associated iterator traits: FromIterator and IntoIterator

https://doc.rust-lang.org/std/iter/trait.FromIterator.html
https://doc.rust-lang.org/std/iter/trait.IntoIterator.html

These are the standard traits for converting to/from iterators, e.g., via
`.collect()`.

These are the standard traits for working with range-like things, for instance
stuff created by `(1..10)` or `('a'..'z')`. Note that there is no requirement
that the range elements are ordered (i.e., implement Ord), but we are working
with ranges of keys and keys must be ordered.

Parts of this assignment are inspired by Learning Rust with Entirely Too Many
Linked Lists (TMLL):

https://cglab.ca/~abeinges/blah/too-many-lists/book/README.html

The material in 2-3 cover similar ground to what we are doing, but for boring
linked lists instead of amazingly cool BSTs. Keep in mind that TMLL was written
a few years ago, and the Rust language has advanced quite a bit in that time.
Many of the problems in that tutorial are no longer problems in Rust. On the
downside, some of the examples in TMLL no longer work with the current version
of Rust.

## Extension

This assignment has two few optional extensions. Note: the amount of work needed
to do these extensions is almost certainly not worth it for the extra point
values. You should only do these extensions if you're actually interested.

### Range iterators (5 extra points)

The standard `BTreeMap`s support range iterators for iterating over a range of
keys. Take a look at the standard library, and implement the following:

1. Borrowing range iterator (called with `.range(a..b)`)
2. Mutable range iterator (called with `.range_mut(a..b)`)

Some tips:

* You'll want to read up on ranges and bounds

https://doc.rust-lang.org/std/ops/trait.RangeBounds.html
https://doc.rust-lang.org/std/ops/enum.Bound.html

* You will need to adapt the iterator structs.

* The standard range iterators implement a ton of other traits. Just implement
the plain `Iterator` trait.

* Your range iterators should take ownership of the range.

* Do not iterate over items unnecessarily. For instance, do not just use a
regular iterator, skip until you get into the range, and then continue from
there.

### Avoiding recursion (5 extra points)

Recursion is not encouraged in Rust. For medium-sized data structures, recursive
operations will quickly blow up the stack. Implement all operations using loops
instead of recursion.

**IMPORTANT NOTE**: Our reference solution hit problems with the borrow checker
when implementing `remove` without recursion (we filed an issue to the Rust
github). If this happens to you, give a non-recursive implementation of remove
and comment it out.

## Disclaimer

TMLL contains a very good discussion of writing your own linked list in Rust,
and the same conclusions carry over to writing BSTs. Essentially: there is no
good reason to do this in practice. You're going to be way better off just using
Rust's excellent standard libraries and collections, and if you're really doing
something weird you will almost always use unsafe Rust to write pointer-based
datastructures. The purpose of this assignment is to give you hands-on
experience in one shot with many core aspects Rust: ownership, borrowing,
references, pointers, iterators, traits, lifetimes, etc. If you ever need a map
collection in the future, you almost certainly don't want to make your own.
(This goes in essentially every language, not just Rust.)

+ 568
- 0
hw5/hw5-problems/src/lib.rs View File

@@ -0,0 +1,568 @@
/*
* CS 538, Spring 2019: HW5
*
* Binary Search Tree
* See `bst.md` for the overview.
*/

#![allow(dead_code)]
#![forbid(unsafe_code)]

use std::cmp::Ordering;
use std::fmt::Debug;
use std::iter::{FromIterator, IntoIterator};
use std::ops::{Bound, Index, IndexMut};

/// Type of BST nodes.
#[derive(Debug)]
struct Node<K, V> {
key: K,
val: V,
size: usize,
lt: TreeMap<K, V>,
rt: TreeMap<K, V>,
}

/// Type of BSTs.
///
/// See TMLL 2.1 for details about the memory layout if you are curious.
#[derive(Debug, Default)]
pub struct TreeMap<K, V> {
inner: Option<Box<Node<K, V>>>
}

/// Part 1: Basic operations and traits (50)
///
/// Your first task is to implement the following operations on BSTs.
///
/// The API here is modeled after Rust's standard BTreeMap collections type. The implementation
/// details are up to you, but you should not change the types of public functions.
impl<K, V> TreeMap<K, V>
where
K: Ord + Debug,
V: Debug,
{
/// Make a new TreeMap.
pub fn new() -> Self {
unimplemented!()
}

/// Clear a TreeMap.
pub fn clear(&mut self) {
unimplemented!()
}

/// Check if a TreeMap is empty.
pub fn is_empty(&self) -> bool {
unimplemented!()
}

/// Compute the size of a TreeMap.
pub fn len(&self) -> usize {
unimplemented!()
}

/// Check if a TreeMap has a certain key.
pub fn has_key(&self, key: &K) -> bool {
unimplemented!()
}

/// Get a reference to the value associated with a key, if present.
///
/// If the key is not in the map, return None.
pub fn get(&self, key: &K) -> Option<&V> {
unimplemented!()
}

/// Get a mutable reference to the value associated with a key, if present.
///
/// If the key is not in the map, return None.
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
unimplemented!()
}

/// Insert a (key, value) pair into a TreeMap.
///
/// If the key is already present in the map, return the previous value and replace the old
/// value with the new value. Otherwise, insert the new (key, value) pair and return None.
pub fn insert(&mut self, key: K, val: V) -> Option<V> {
unimplemented!()
}

/// Insert a nonempty TreeMap into a TreeMap.
///
/// While real BSTs do careful rotations/rebalancing to keep the depth of the merged tree as
/// small as possible, we will just do a naive insert by going down to an appropriate leaf of
/// `self` and sticking `other` there.
///
/// Since this function is not publicly visible (there is no `pub` before `fn`), it is not used
/// directly by clients. You may assume that `self` and `other` are non-overlapping: either all
/// nodes in self are strictly smaller than all nodes in other, or all nodes in self are
/// strictly larger than all nodes in other. Your implementation can panic (or just continue
/// silently) if this requirement is not met. However if the trees are not overlapping, you
/// must maintain the BST invariant.
fn insert_tree(&mut self, mut other: Self) {
unimplemented!()
}

/// Remove a key from a TreeMap.
///
/// If the map contains the key, remove the key and return the associated value.
/// If the map does not contain the key, return None and leave the map unchanged.
///
/// Hint: take the two child trees of the node you're removing, insert one into the other.
pub fn remove(&mut self, key: K) -> Option<V> {
unimplemented!()
}
}

/// We will implement a few traits for TreeMap. Starting off: implement the FromIterator trait.
///
/// Hint: feed stuff from the iterator into the TreeMap.
impl<K, V> FromIterator<(K, V)> for TreeMap<K, V>
where
K: Ord + Debug,
V: Debug,
{
fn from_iter<T>(iter: T) -> TreeMap<K, V>
where
T: IntoIterator<Item = (K, V)>,
{
unimplemented!()
}
}

/// Now, we will implement two kinds of indexing traits: Index and IndexMut.
///
/// Rust's built-in syntactic sugar hooks into these traits, for instance we can write stuff like:
///
/// let val = map[idx];
///
/// which is short for
///
/// let val = *map.index(idx);
///
/// if we have the Index trait. If we implement the IndexMut trait, we can write stuff like:
///
/// map[idx] = new_val;
///
/// which is short for
///
/// *map.index(idx) = new_val;
///
/// Index returns a reference to the value for a given key, and IndexMut returns a mutable
/// reference to the value for a given key. If the key is not in the map, panic. You will probably
/// want to take a look at the documentation for Index and IndexMut.
///
/// Note: the Rust BTreeMap actually has a more general type for these operations, something like:
///
/// fn index<Q>(&self, key: &Q) -> &V
/// where
/// K: Borrow<Q>,
/// Q: Ord + ?Sized,
/// { ... }
///
/// The idea here is that K is the key type stored in the map, while Q is the key type used for
/// lookups. In some cases, this allows us to avoid constructing a new object just to do a lookup;
/// think K = String and Q = &str. The trait bound K: Borrow<Q> says that if we have a K, we can
/// get a reference to Q. The Then, we can compare Q's since Q: Ord. (The question-mark bound Q:
/// ?Sized means that the size of Q does *not* need to be known at compile time; by default, all
/// type parameters have statically known sizes.) See the documentation for Borrow for an example
/// of how this works:
///
/// https://doc.rust-lang.org/std/borrow/trait.Borrow.html
impl<'a, K, V> Index<&'a K> for TreeMap<K, V>
where
K: Ord + Debug,
V: Debug,
{
type Output = V;

fn index(&self, key: &K) -> &V {
unimplemented!()
}
}

impl<'a, K, V> IndexMut<&'a K> for TreeMap<K, V>
where
K: Ord + Debug,
V: Debug,
{
fn index_mut(&mut self, key: &'a K) -> &mut V {
unimplemented!()
}
}

/// Part 2: Iterators (35)
///
/// Now, you will soup up your BST by implementing various kinds of iterators to perform in-order
/// traversals of the map, i.e., from smallest to largest key.
///
/// The first iterator you will design is a so-called *consuming iterator*, which takes ownership
/// of all elements in the map. You will use the following types to track the current state of the
/// iterator:

enum Next<I, T> {
Item(I),
Tree(T),
}

pub struct IntoIter<K, V> {
next_nodes: Vec<Next<(K, V), TreeMap<K, V>>>,
current_val: Option<(K, V)>,
}

/// This state has two main parts. There is the current key value pair (or nothing, if the iterator
/// is already finished), and stack of `Next` recording the next elements of the iterator. For
/// instance, if the iterator is currently at a node that is a left-child of its parent, the top of
/// the stack would be the data at the parent, the 2nd-from-top of the stack would store the whole
/// subtree from the right sibling, and so on.
///
/// Define a few functions to set up the consuming iterator.
impl<K, V> IntoIter<K, V> {
/// Make a new consuming iterator from a TreeMap
///
/// Note that this function takes ownership (i.e., tree is passed by value). This is fine,
/// because we are building a *consuming* iterator.
fn new(tree: TreeMap<K, V>) -> Self {
unimplemented!()
}

/// This is the main workhorse function for setting up the iterator. In words, this function
/// should descends to the left-furthermost non-leaf child. Along the way, it sets up the
/// iterator state: the current value, and the next-nodes-to-visit stack. Make sure you add
/// nodes in the right order: we are aiming for an in-order traversal, so the iterator should
/// visit left-child, parent, right-child as we pop things off the stack.
fn descend_left(&mut self, tree: TreeMap<K, V>) {
unimplemented!()
}
}

/// Implement IntoIterator for TreeMap.
impl<K, V> IntoIterator for TreeMap<K, V> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;

fn into_iter(self) -> IntoIter<K, V> {
unimplemented!()
}
}

/// Now, we are ready to implement the Iterator trait for IntoIter.
///
/// The main function `next` should get the current value, update the iterator to prepare the next
/// value, and then update the nodes to visit.
///
/// Hint: you'll want to use `descend_left`.
impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
unimplemented!()
}
}

/// Now, you'll define a few variants of iterators that do not consume the data. These are also
/// called *borrowing* iterators, since they borrow data instead of consuming it. The overall
/// strategy will be quite similar to what you already did above, but with a few tweaks. The main
/// idea behind a borrowing iterator is that we should produce references to data, rather than data.
///
/// For this part, you'll be designing the type structures and implementing the necessary functions.
/// You should design two kinds of iterators:
///
/// 1. Borrowing iterator. This should implement the Iterator trait with item type (&K, &V).
/// 2. Mutable iterator. This should implement the Iterator trait with item type (&K, &mut V).
///
/// Note that we need to be a bit careful with mutable iterators: clients should be allowed to
/// modify the value, but they should not be allowed to modify the keys---if they did, they might
/// screw up the BST property. Your mutable iterator should hand out a pair of references: an
/// *immutable* reference to the key, and a *mutable* reference to the value.
///
/// To construct these two iterators, implement the following two methods for TreeMap:
///
/// 1. pub fn iter(&self) -> Iter<K, V>
/// 2. pub fn iter_mut(&mut self) -> IterMut<K, V>
///
/// Iter and IterMut should implement the Iterator trait.
///
/// You should also implement versions of all the associated functions you did for IntoIter, but do
/// not implement IntoIterator for TreeMap again.
///
/// Hint: don't implement these iterators from scratch. You'll need to sprinkle in some extra
/// annotations and tweaks in a few key places, but your code for the borrowing and mutable
/// iterators should be a nearly exact copy-paste of your code for consuming iterators.

/// Part 3: Custom dropping (15)
///
/// The built-in Drop trait behaves poorly because it drops TreeMap recursively. If the TreeMap
/// too deep, this will blow up the stack when dropping (see TMLL 2.7 for details).
///
/// Implement a custom Drop trait to drop the TreeMap in a loop rather than recursing. The default
/// Drop implementation for the TreeMap/Node types drops according to a *pre-order* traversal: drop
/// root, then drop left, then drop right. Your implementation should maintain this order.
impl<K, V> Drop for TreeMap<K, V> {
fn drop(&mut self) {
unimplemented!()
}
}

#[cfg(test)]
mod test {
use super::TreeMap;
use std::fmt::Debug;

impl<K, V> TreeMap<K, V>
where
K: Ord + Debug,
V: Debug,
{
/// Test function: checks the BST invariant.
fn is_bst(&self) -> bool {
let mut lsize = 0;
let mut rsize = 0;
if let Some(boxed) = &self.inner {
let lt = &boxed.lt;
let rt = &boxed.rt;

if let Some(lnode) = &lt.inner {
if lnode.key >= boxed.key || !lt.is_bst() {
return false;
}

lsize = lnode.size;
}

if let Some(rnode) = &rt.inner {
if rnode.key <= boxed.key || !rt.is_bst() {
return false;
}

rsize = rnode.size;
}

return boxed.size == lsize + rsize + 1;
}

true
}
}

#[test]
fn test_insert() {
let mut tree = TreeMap::new();

tree.insert(1, 11);
tree.insert(3, 33);
tree.insert(2, 22);

assert_eq!(tree.get(&1), Some(&11));
assert_eq!(tree.get(&2), Some(&22));
assert_eq!(tree.get(&3), Some(&33));

assert!(tree.is_bst());

let insert_res = tree.insert(1, 111);

assert_eq!(tree.get(&1), Some(&111));
assert_eq!(insert_res, Some(11));

assert!(tree.is_bst());
}

#[test]
fn test_clear() {
let mut tree = TreeMap::new();

tree.insert(1, 11);
tree.insert(3, 33);
tree.insert(2, 22);

assert_eq!(tree.len(), 3);

tree.clear();

assert_eq!(tree.len(), 0);
}

#[test]
fn test_remove() {
let mut tree = TreeMap::new();

tree.insert(1, 11);
tree.insert(3, 33);
tree.insert(2, 22);

assert_eq!(tree.remove(1), Some(11));
assert_eq!(tree.len(), 2);
assert!(tree.is_bst());

assert_eq!(tree.remove(1), None);
assert_eq!(tree.len(), 2);
assert!(tree.is_bst());
}

#[test]
fn test_mut() {
let mut tree = TreeMap::new();

tree.insert(1, 11);
tree.insert(3, 33);
tree.insert(2, 22);

assert_eq!(tree.get(&3), Some(&33));

*tree.get_mut(&3).unwrap() = 333;

assert_eq!(tree.get(&3), Some(&333));

assert!(tree.is_bst());
}

#[test]
fn test_index() {
let mut tree = TreeMap::new();

tree.insert(1, 11);
tree.insert(3, 33);
tree.insert(2, 22);

assert_eq!(tree[&1], 11);

tree[&2] = 22;

assert_eq!(tree.get(&2), Some(&22));

assert!(tree.is_bst());
}

#[should_panic]
#[test]
fn test_bad_index() {
let mut tree = TreeMap::new();

tree.insert(1, 11);
tree.insert(3, 33);
tree.insert(2, 22);

tree[&5] = 10;
}

#[test]
fn test_from_iter() {
let vec = vec![(1, 11), (3, 33), (2, 22)];
let tree: TreeMap<i32, i32> = vec.into_iter().collect();

assert!(tree.is_bst());

assert_eq!(tree.get(&1), Some(&11));
assert_eq!(tree.get(&2), Some(&22));
assert_eq!(tree.get(&3), Some(&33));
}

#[test]
fn test_iter() {
let vec = vec![(1, 11), (3, 33), (2, 22)];
let tree: TreeMap<i32, i32> = vec.into_iter().collect();

let mut iter = tree.into_iter();

assert_eq!(iter.next(), Some((1, 11)));
assert_eq!(iter.next(), Some((2, 22)));
assert_eq!(iter.next(), Some((3, 33)));
assert_eq!(iter.next(), None);
}

/* Uncomment when borrowing iterator ready.
#[test]
fn test_borrow_iter() {
let vec = vec![(1, 11), (3, 33), (2, 22)];
let tree: TreeMap<i32, i32> = vec.into_iter().collect();

let mut iter = tree.iter();

assert_eq!(iter.next(), Some((&1, &11)));
assert_eq!(iter.next(), Some((&2, &22)));
assert_eq!(iter.next(), Some((&3, &33)));
assert_eq!(iter.next(), None);
} */

/* Uncomment when mutable iterator ready.
#[test]
fn test_mut_iter() {
let vec = vec![(1, 11), (3, 33), (2, 22)];
let mut tree: TreeMap<i32, i32> = vec.into_iter().collect();

for (k, v) in tree.iter_mut() {
*v = k + 100;
}

assert!(tree.is_bst());
assert_eq!(tree.len(), 3);

assert_eq!(tree.get(&1), Some(&101));
assert_eq!(tree.get(&2), Some(&102));
assert_eq!(tree.get(&3), Some(&103));
} */

use std::cell::RefCell;

#[derive(Debug)]
struct MyDropper<'a> {
inner: String,
log: &'a RefCell<Vec<String>>,
}

impl<'a> Drop for MyDropper<'a> {
fn drop(&mut self) {
// Record the dropped String in a shared log
let newstr = self.inner.clone();
self.log.borrow_mut().push(newstr);
}
}

#[test]
fn test_drop() {
let drops = RefCell::new(vec![]);

// Contents of tree in pre-order traversal
let contents = vec![
String::from("m"),
String::from("h"),
String::from("a"),
String::from("k"),
String::from("t"),
String::from("p"),
String::from("z"),
];

// Set up a complete binary tree (key = val)
{
let mut tree = TreeMap::new();
for s in &contents {
let log = &drops;
let key = s.clone();
let inner = s.clone();
tree.insert(key, MyDropper { inner, log });
}
} // ... and drop it

// Check the order of drops
assert_eq!(*drops.borrow(), contents);
}

/* Uncomment when drop trait ready.
#[test]
fn test_drop_big() {
// This is slow (~1-2 min on my machine) but it shouldn't overflow!
let v = (1..100000).collect::<Vec<u32>>();

// Make a really deep binary tree
{
let mut tree = TreeMap::new();
for i in v {
tree.insert(i, i);
}
} // ... and drop it

println!("All done!");
} */
}

BIN
hw5/wr5-problems.pdf View File


+ 204
- 0
hw5/wr5-problems.tex View File

@@ -0,0 +1,204 @@
\documentclass{article}

\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{stmaryrd}
\usepackage{mathtools}
\usepackage{color}
\usepackage{mathpartir}
\usepackage{fullpage}
\usepackage{minted}

\title{Written Assignment 5}
\author{CS 538, Spring 2019}

\begin{document}

\maketitle

\begin{center}
\includegraphics[width=0.6\textwidth]{images/borrowck.jpg}
\end{center}

\hspace*{\fill} ---~@avadacatavra

\bigskip

These exercises will give you practice with thinking about Rust's ownership
rules and the borrow checker. For each of the following code snippets, answer
the following questions in 1-2 sentences each:
%
\begin{enumerate}
\item Explain how the program breaks Rust's rules \emph{in your own
words}---do not just copy the compiler error.
\item \emph{Why} is the program bad? Suppose that Rust accepted this program.
What could go possibly wrong?
\end{enumerate}

\section*{Problem 0 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: String) { /* ... */ }

fn baz() {
let str_1 = String::from("Hello");
let str_2 = str_1;

foo(str_1);
}
\end{minted}

\section*{Problem 1 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: &mut String) { /* ... */ }

fn baz() {
let my_str = String::from("Hello");

foo(&my_str);
}
\end{minted}

\section*{Problem 2 (3)}

\begin{minted}[samepage]{rust}
fn baz() {
let mut my_str = String::from("Hello");
let my_ref = &my_str;

my_str.push_str(" World!");
println!("What's here? {}", my_ref);
}
\end{minted}

\section*{Problem 3 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: &mut String) { /* ... */ }

fn baz() {
let mut my_str = String::from("Hello");
let my_ref = &my_str;
let my_other_ref = &mut my_str;

foo(my_other_ref);

println!("What's here? {}", my_ref);
}
\end{minted}

\section*{Problem 4 (3)}

\begin{minted}[samepage]{rust}
struct MyStruct {
my_str: String,
my_int: i32,
}

fn foo(s: String) { /* ... */ }

fn baz() {
let my_struct = MyStruct {
my_str: String::from("Hello"),
my_int: 42,
};

foo(my_struct.my_str);
foo(my_struct.my_str);
}
\end{minted}

\section*{Problem 5 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: String) { /* ... */ }

fn bar(s: &String) {
let my_str: String = *s;

foo(my_str);
}
\end{minted}

\section*{Problem 6 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: String) { /* ... */ }

fn bar(opt: &Option<String>) {
match opt {
None => println!("Nothing!"),
Some(my_str) => foo(my_str)
}
}
\end{minted}

\section*{Problem 8 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: String) { /* ... */ }

fn bar(opt: Option<String>) {
match opt {
None => println!("Nothing!"),
Some(my_str) => foo(my_str)
}

let opt_s = opt.unwrap_or(String::new());

println!("What's here? {}", opt_s);
}
\end{minted}

\section*{Problem 8 (3)}

\begin{minted}[samepage]{rust}
fn foo<'a>(s: String) -> &'a String {
&s
}
\end{minted}

\section*{Problem 9 (3)}

\begin{minted}[samepage]{rust}
fn foo(s: &mut String) -> &mut String {
s.push_str(" World!");
s
}

fn baz() {
let mut dummy = String::new();
let mut str_ref = &mut dummy;

{
let mut my_str = String::from("Hello");
str_ref = foo(&mut my_str);
}

println!("What's here? {}", str_ref);
}
\end{minted}

\section*{Problem Optional (0)}
\begin{minted}[samepage]{rust}
fn foo<'a>(s: &'a String, t: &'a String) -> &'a String {
if s > t { s } else { t }
}

fn baz() {
let mut foo1 = String::from("Hello");

let mut str_ref: &String;

{
let mut foo2 = String::from("World");
str_ref = foo(&foo1, &foo2);
}

println!("What's here? {}", str_ref);
}
\end{minted}


\end{document}

Loading…
Cancel
Save