add 8, 11, 12

This commit is contained in:
woojin
2023-08-18 22:10:35 +09:00
parent e500317044
commit b0ee36a9ff
31 changed files with 1797 additions and 73 deletions

267
Cargo.lock generated
View File

@@ -6,7 +6,16 @@ version = 3
name = "anyhow"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "autocfg"
@@ -47,11 +56,21 @@ version = "4.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
dependencies = [
"bitflags",
"clap_builder",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
"termcolor",
]
@@ -145,14 +164,19 @@ name = "cs220"
version = "0.1.0"
dependencies = [
"anyhow",
"approx",
"clap",
"etrace",
"itertools",
"lazy_static",
"ndarray",
"ndarray-rand",
"ntest",
"num-traits",
"pest",
"pest_derive",
"rayon",
"thiserror",
]
[[package]]
@@ -209,10 +233,27 @@ dependencies = [
]
[[package]]
name = "heck"
version = "0.4.0"
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
@@ -225,11 +266,18 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.2.6"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"libc",
"autocfg",
"hashbrown",
]
[[package]]
@@ -273,13 +321,35 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
name = "libc"
version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]]
name = "linux-raw-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "matrixmultiply"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
@@ -290,6 +360,30 @@ dependencies = [
"autocfg",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "ndarray-rand"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65608f937acc725f5b164dcf40f4f0bc5d67dc268ab8a649d3002606718c4588"
dependencies = [
"ndarray",
"rand",
"rand_distr",
]
[[package]]
name = "ntest"
version = "0.9.0"
@@ -320,7 +414,36 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
"libm",
]
[[package]]
@@ -335,15 +458,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "pest"
@@ -389,6 +506,12 @@ dependencies = [
"sha1",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-crate"
version = "1.2.1"
@@ -442,6 +565,52 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_distr"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
dependencies = [
"num-traits",
"rand",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "rayon"
version = "1.6.0"
@@ -486,16 +655,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.149"
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
[[package]]
name = "sha1"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -582,41 +745,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "winapi"
version = "0.3.9"
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.42.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",

View File

@@ -14,4 +14,9 @@ lazy_static = "1.4.0"
pest = "2.5.1"
pest_derive = "2.5.1"
rayon = "1.6.0"
thiserror = "1.0"
ntest = "0.9.0"
approx = "0.5.1"
num-traits = "0.2"
ndarray = "0.15.0"
ndarray-rand = "0.14.0"

View File

@@ -24,7 +24,7 @@ run_linters || exit 1
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
TESTS=("--lib assignment11_grade")
TESTS=("--lib assignment11")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi

View File

@@ -24,7 +24,7 @@ run_linters || exit 1
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
TESTS=("--lib assignment12_grade")
TESTS=("--lib assignment11")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi

View File

@@ -5,22 +5,45 @@
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-08.sh` works fine.
//! See `assignment08_grade.rs` and `/scripts/grade-08.sh` for the test script.
/// Returns an anonymous function that applies the given function `f` for `n` times.
/// Repeat
///
/// Returns an anonymous function that applies the given function `f` for `n` times.
/// For instance, `repeat(3, f)(x)` roughly translates to `f(f(f(x)))`.
///
/// Refer `test_repeat` in `assignment08_grade.rs` for detailed examples.
pub fn repeat<T, F: FnMut(T) -> T>(n: usize, mut f: F) -> impl FnMut(T) -> T {
todo!();
f // This line has been added to prevent compile error. You can erase this line.
}
/// Funny Map
///
/// Applies the given function `f` for `i` times for the `i`-th element of the given vector.
///
/// For instance, `funny_map(f, [v0, v1, v2, v3])` roughly translates to `[v0, f(v1), f(f(v2)), f(f(f(v3)))]`.
///
/// Refer `test_funny_map` in `assignment08_grade.rs` for detailed examples.
pub fn funny_map<T, F: Fn(T) -> T>(f: F, vs: Vec<T>) -> Vec<T> {
todo!()
}
/// Count Repeat
///
/// Returns the number of the elements of the set
/// {x, f(x), f(f(x)), f(f(f(x))), ...}.
/// You may assume that the answer is finite and small enough.
///
/// Refer `test_count_repeat` in `assignment08_grade.rs` for detailed examples.
pub fn count_repeat<T, F: Fn(T) -> T>(f: F, x: T) -> usize
where
T: PartialEq + Copy,
{
todo!()
}
/// Either `T1`, or `T2`.
///
/// Fill out `map` method for this type.
#[derive(Debug, PartialEq, Eq)]
pub enum Either2<T1, T2> {
/// Case 1.
@@ -39,6 +62,8 @@ impl<T1, T2> Either2<T1, T2> {
/// Maps the inner value.
///
/// If the inner value is case 1, apply `f1`, and if it is case 2, apply `f2`.
///
/// Refer `test_either2_map` in `assignment08_grade.rs` for detailed examples.
pub fn map<U1, U2, F1, F2>(self, f1: F1, f2: F2) -> Either2<U1, U2>
where
F1: FnOnce(T1) -> U1,

View File

@@ -27,4 +27,21 @@ mod test {
let u2 = Either2::<u32, f32>::Case2 { inner: 43.0 };
assert_eq!(u2, v2.map(|i| i + 1, |f| f + 1.0));
}
#[test]
fn test_count_repeat() {
let inc_mod_100 = |x| (x + 1) % 100;
assert_eq!(count_repeat(inc_mod_100, 10), 100);
assert_eq!(count_repeat(inc_mod_100, 12345), 101);
let p_lookup = |n| vec![1, 0, 2, 4, 5, 6, 7, 3][n];
assert_eq!(count_repeat(p_lookup, 0), 2);
assert_eq!(count_repeat(p_lookup, 1), 2);
assert_eq!(count_repeat(p_lookup, 2), 1);
assert_eq!(count_repeat(p_lookup, 3), 5);
assert_eq!(count_repeat(p_lookup, 4), 5);
assert_eq!(count_repeat(p_lookup, 5), 5);
assert_eq!(count_repeat(p_lookup, 6), 5);
assert_eq!(count_repeat(p_lookup, 7), 5);
}
}

View File

@@ -0,0 +1,202 @@
//! Binary Search Tree
//!
//! Refer `bst_grade.rs` for test cases.
use std::cell::RefCell;
use std::cmp::Ordering;
use std::fmt::Debug;
use std::ops::Deref;
use std::rc::{Rc, Weak};
/// Node struct of tree
#[derive(Debug, Clone)]
struct Node<T>
where
T: Ord,
{
value: T,
parent: Option<Weak<RefCell<Node<T>>>>,
left: Option<Rc<RefCell<Node<T>>>>,
right: Option<Rc<RefCell<Node<T>>>>,
}
impl<T> Node<T>
where
T: Ord,
{
fn new(value: T) -> Rc<RefCell<Node<T>>> {
Rc::new(RefCell::new(Node {
value,
parent: None,
left: None,
right: None,
}))
}
fn with_parent(value: T, parent: Weak<RefCell<Node<T>>>) -> Rc<RefCell<Node<T>>> {
Rc::new(RefCell::new(Node {
value,
parent: Some(parent),
left: None,
right: None,
}))
}
/// Minimum node starting from cursor
fn min_node(mut cursor: Rc<RefCell<Node<T>>>) -> Rc<RefCell<Node<T>>> {
todo!();
}
/// Upgraded parent node.
/// `None` if the node has no parent.
fn parent(&self) -> Option<Rc<RefCell<Node<T>>>> {
self.parent.as_ref().and_then(|p| p.upgrade())
}
}
/// Binary Search Tree
#[derive(Debug)]
pub struct Tree<T>
where
T: Ord,
{
root: Option<Rc<RefCell<Node<T>>>>,
len: usize,
}
impl<T: Ord> Default for Tree<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Tree<T>
where
T: Ord,
{
/// New tree
pub fn new() -> Tree<T> {
Tree { root: None, len: 0 }
}
/// Length of the tree
pub fn len(&self) -> usize {
self.len
}
/// Check if the tree is empty.
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Check if the tree contains the value.
pub fn contains(&self, value: &T) -> bool {
if let Some(mut cursor) = self.root.clone() {
todo!();
} else {
false
}
}
/// Insert the value into the tree.
/// If there was no equal value in the tree, it returns `true`.
/// Otherwise, it returns `false`.
pub fn insert(&mut self, value: T) -> bool {
self.len += 1;
if let Some(mut cursor) = self.root.clone() {
todo!();
} else {
self.root = Some(Node::new(value));
true
}
}
/// Remove the node from the tree.
/// Returns the value of the removed node.
fn remove_node(&mut self, mut node: Rc<RefCell<Node<T>>>) -> T {
todo!();
}
/// Remove the value from the tree.
/// If there is an equal value in the tree, it returns `true`.
/// Otherwise, it returns `false`.
pub fn remove(&mut self, value: &T) -> Option<T> {
let res = if let Some(root) = self.root.clone() {
let mut cursor = root;
loop {
let mut cursor_ref = cursor.deref().borrow_mut();
let child = match value.cmp(&cursor_ref.value) {
Ordering::Less => cursor_ref.left.clone(),
Ordering::Greater => cursor_ref.right.clone(),
Ordering::Equal => {
drop(cursor_ref);
break Some(self.remove_node(cursor));
}
};
if let Some(child) = child {
drop(cursor_ref);
cursor = child;
} else {
break None;
}
}
} else {
None
};
self.len -= res.is_some() as usize;
res
}
}
impl<T> Clone for Tree<T>
where
T: Ord + Clone,
{
fn clone(&self) -> Self {
Tree {
root: self.root.clone(),
len: self.len,
}
}
}
/// IntoIterator for Tree
#[derive(Debug)]
pub struct IntoIter<T>
where
T: Ord,
{
tree: Tree<T>,
len: usize,
}
impl<T> IntoIterator for Tree<T>
where
T: Ord,
{
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
let len = self.len;
IntoIter { tree: self, len }
}
}
impl<T> Iterator for IntoIter<T>
where
T: Ord,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
todo!();
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}

View File

@@ -0,0 +1,53 @@
//! Test cases for assignment11/bst.rs
#[cfg(test)]
mod test_bst {
use super::super::bst::*;
#[test]
fn bst_insert_test() {
let mut tree = Tree::new();
let _ = tree.insert(1);
let _ = tree.insert(5);
let _ = tree.insert(3);
let _ = tree.insert(7);
assert_eq!(tree.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7]);
}
#[test]
fn bst_remove_test() {
let mut tree = Tree::new();
let _ = tree.insert(1);
let _ = tree.insert(5);
let _ = tree.insert(3);
let _ = tree.insert(7);
let _ = tree.remove(&7);
assert_eq!(tree.into_iter().collect::<Vec<_>>(), vec![1, 3, 5]);
}
#[test]
fn bst_complex_test() {
let mut tree = Tree::new();
let _ = tree.insert(1);
let _ = tree.insert(5);
let _ = tree.insert(3);
let _ = tree.insert(7);
let _ = tree.remove(&7);
let _ = tree.insert(7);
let _ = tree.insert(6);
let _ = tree.insert(8);
let _ = tree.remove(&5);
let _ = tree.remove(&1);
let _ = tree.remove(&3);
let _ = tree.remove(&7);
let _ = tree.remove(&6);
let _ = tree.remove(&8);
assert_eq!(tree.into_iter().collect::<Vec<_>>(), vec![]);
}
}

View File

@@ -0,0 +1,101 @@
//! Doubly Linked List.
//!
//! Refer `doubly_linked_list_grade.rs` for test cases.
use std::{cell::RefCell, fmt::Debug, rc::Rc};
type Link<T> = Option<Rc<RefCell<Node<T>>>>;
/// Node of a doubly-linked list.
#[derive(Debug)]
pub struct Node<T: Debug + Clone> {
/// Value of current node.
value: RefCell<T>,
/// Pointer to the next node. If it is `None`, there is no next node.
next: Link<T>,
/// Pointer to the previous node. If it is `None`, there is no previous node.
prev: Link<T>,
}
impl<T: Debug + Clone> Node<T> {
/// Creates a new node.
fn new(value: T) -> Self {
Self {
value: RefCell::new(value),
next: None,
prev: None,
}
}
/// Fetch the value contained in node
pub fn get(&self) -> T {
self.value.borrow().clone()
}
/// Replace the data contained in the node
pub fn replace(&self, new_value: T) -> T {
self.value.replace(new_value)
}
/// Fetch previous node
pub fn prev(&self) -> Link<T> {
self.prev.clone()
}
/// Fetch next node
pub fn next(&self) -> Link<T> {
self.next.clone()
}
}
/// A doubly-linked list.
#[derive(Default, Debug)]
pub struct DoublyLinkedList<T: Debug + Clone> {
/// Head node of the list. If it is `None`, the list is empty.
head: Link<T>,
/// Tail node of the list. If it is `None`, the list is empty.
tail: Link<T>,
}
impl<T: Debug + Clone> DoublyLinkedList<T> {
/// Creates a new list.
pub fn new() -> Self {
Self {
head: None,
tail: None,
}
}
/// Adds the given node to the front of the list.
pub fn push_front(&mut self, value: T) {
todo!()
}
/// Adds the given node to the back of the list.
pub fn push_back(&mut self, value: T) {
todo!()
}
/// Removes and returns the node at the front of the list.
pub fn pop_front(&mut self) -> Option<T> {
todo!()
}
/// Removes and returns the node at the back of the list.
pub fn pop_back(&mut self) -> Option<T> {
todo!()
}
}
impl<T: Debug + Clone> Drop for DoublyLinkedList<T> {
fn drop(&mut self) {
while let Some(node) = self.head.take() {
let _ = node.borrow_mut().prev.take();
self.head = node.borrow_mut().next.take();
}
let _unused = self.tail.take();
}
}

View File

@@ -0,0 +1,43 @@
//! Test cases for assignment11/doubly_linked_list.rs
#[cfg(test)]
mod test_doubly_linked_list {
use super::super::doubly_linked_list::*;
#[test]
fn test_works() {
let mut list = DoublyLinkedList::new();
list.push_back(3);
list.push_back(4);
assert_eq!(list.pop_front(), Some(3));
list.push_front(5);
assert_eq!(list.pop_back(), Some(4));
assert_eq!(list.pop_back(), Some(5));
assert_eq!(list.pop_back(), None);
assert_eq!(list.pop_front(), None);
}
#[test]
fn test_can_push_back() {
let mut list = DoublyLinkedList::new();
assert_eq!(list.pop_back(), None);
list.push_back(3);
list.push_back(4);
list.push_back(5);
assert_eq!(list.pop_back(), Some(5));
list.push_back(6);
list.push_back(7);
assert_eq!(list.pop_back(), Some(7));
assert_eq!(list.pop_back(), Some(6));
assert_eq!(list.pop_back(), Some(4));
assert_eq!(list.pop_back(), Some(3));
list.push_back(2);
assert_eq!(list.pop_back(), Some(2));
assert_eq!(list.pop_back(), None);
}
}

View File

@@ -0,0 +1,93 @@
//! A small graph library.
//!
//! A node has a i32 value and (directed) edges to other nodes. A node does not have multiple edges
//! to the same node. Nodes are not associated with a particular domain, and users can freely
//! create nodes however they like. However, after a node is created, it can be added to a
//! `SubGraph`, which form a subgraph of the graph of all nodes. A node can be added to multiple
//! subgraphs. `SubGraph` has a method to check if the it has a cycle.
//!
//! The goal of this assignment is to learn how to deal with inherently shared mutable data in
//! Rust. Design the types and fill in the `todo!()`s in methods. There are several possible
//! approaches to this problem and you may import anything from the std library accordingly.
//!
//! Refer `graph_grade.rs` for test cases.
#[derive(PartialEq, Eq, Debug)]
enum VisitStatus {
Unvisited,
Visiting,
Visited,
}
/// Handle to a graph node.
/// `NodeHandle` should implement `Clone`, which clones the handle without cloning the underlying
/// node. That is, there can be multiple handles to the same node.
/// The user can access the node through a handle if it does not violate Rust's aliasing rules.
#[derive(Debug, Clone)]
pub struct NodeHandle;
/// Error type for graph operations.
#[derive(Debug)]
pub struct GraphError;
/// Subgraph
#[derive(Debug)]
pub struct SubGraph;
impl NodeHandle {
/// Creates a node and returns the handle to it.
pub fn new(value: i32) -> Self {
todo!()
}
/// Adds an edge to `to`.
/// If the modification cannot be done, e.g. because of aliasing issues, returns `Err(GraphError)`.
/// Returns `Ok(true)` if the edge is successfully added.
/// Returns `Ok(false)` if an edge to `to` already exits.
pub fn add_edge(&self, to: NodeHandle) -> Result<bool, GraphError> {
todo!()
}
/// Removes the edge to `to`.
/// If the modification cannot be done, e.g. because of aliasing issues, returns `Err(GraphError)`.
/// Returns `Ok(true)` if the edge is successfully removed.
/// Returns `Ok(false)` if an edge to `to` does not exist.
pub fn remove_edge(&self, to: &NodeHandle) -> Result<bool, GraphError> {
todo!()
}
/// Removes all edges.
/// If the modification cannot be done, e.g. because of aliasing issues, returns `Err(GraphError)`.
pub fn clear_edges(&self) -> Result<(), GraphError> {
todo!()
}
}
impl Default for SubGraph {
fn default() -> Self {
Self::new()
}
}
impl SubGraph {
/// Creates a new subgraph.
pub fn new() -> Self {
todo!()
}
/// Adds a node to the subgraph. Returns true iff the node is newly added.
pub fn add_node(&mut self, node: NodeHandle) -> bool {
todo!()
}
/// Adds a node to the subgraph. Returns true iff the node is successfully removed.
pub fn remove_node(&mut self, node: &NodeHandle) -> bool {
todo!()
}
/// Returns true iff the subgraph contains a cycle. Nodes that do not belong to this subgraph
/// are ignored. See https://en.wikipedia.org/wiki/Cycle_(graph_theory) for an algorithm.
pub fn detect_cycle(&self) -> bool {
todo!()
}
}

View File

@@ -0,0 +1,63 @@
//! Test cases for assignment11/graph.rs
#[cfg(test)]
mod test_graph {
use super::super::graph::*;
#[test]
fn test_graph() {
let mut nodes = (0..6).map(NodeHandle::new).collect::<Vec<_>>();
let edges = [
(0, 1),
(0, 3),
(1, 4),
(2, 4),
(2, 5),
(3, 1),
(4, 3),
(5, 5),
];
for (from, to) in edges {
assert!(nodes[from].add_edge(nodes[to].clone()).unwrap());
}
let mut graph1 = SubGraph::new();
for n in 0..6 {
assert!(graph1.add_node(nodes[n].clone()));
}
assert!(graph1.detect_cycle());
assert!(!graph1.add_node(nodes[0].clone()));
let mut graph2 = SubGraph::new();
for n in [0, 1, 3] {
assert!(graph2.add_node(nodes[n].clone()));
}
assert!(!graph2.detect_cycle());
assert!(graph2.add_node(nodes[4].clone()));
assert!(graph2.detect_cycle());
assert!(nodes[4].remove_edge(&nodes[3]).unwrap());
assert!(!graph2.detect_cycle());
let mut graph3 = SubGraph::new();
for n in [0, 1, 2, 3] {
assert!(graph3.add_node(nodes[n].clone()));
}
assert!(!graph3.detect_cycle());
let more_edges = [(1, 2), (2, 3)];
for (from, to) in more_edges {
assert!(nodes[from].add_edge(nodes[to].clone()).unwrap());
}
assert!(graph3.detect_cycle());
assert!(graph3.remove_node(&nodes[2]));
assert!(!graph3.detect_cycle());
for n in nodes {
n.clear_edges().unwrap();
}
}
}

View File

@@ -1,6 +1,8 @@
//! Singly linked list.
//!
//! Consult <https://doc.rust-lang.org/book/ch15-01-box.html>.
//! Hint: Consult <https://doc.rust-lang.org/book/ch15-01-box.html>.
//!
//! Refer `linked_list_grade.rs` for test cases.
use std::fmt::Debug;

View File

@@ -0,0 +1,27 @@
//! Test cases for assignment11/linked_list.rs
#[cfg(test)]
mod test_linked_list {
use super::super::linked_list::*;
#[derive(Debug, PartialEq, Eq)]
struct V(usize);
#[test]
fn test_linked_list() {
let mut list = SinglyLinkedList::new();
list.push_back(V(3));
list.push_front(V(2));
list.push_back(V(4));
list.push_front(V(1));
list.push_back(V(5));
assert_eq!(list.pop_front(), Some(V(1)));
assert_eq!(list.pop_back(), Some(V(5)));
assert_eq!(list.pop_front(), Some(V(2)));
assert_eq!(list.pop_back(), Some(V(4)));
assert_eq!(list.pop_front(), Some(V(3)));
assert_eq!(list.pop_back(), None);
assert_eq!(list.pop_front(), None);
}
}

View File

@@ -1,6 +1,8 @@
//! Mock storage.
//!
//! Consult <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#a-use-case-for-interior-mutability-mock-objects>.
//! Hint: Consult <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#a-use-case-for-interior-mutability-mock-objects>.
//!
//! Refer `mock_storage_grade.rs` for test cases.
use std::cell::RefCell;
use std::collections::HashMap;

View File

@@ -0,0 +1,31 @@
//! Test cases for assignment11/mock_storage.rs
#[cfg(test)]
mod test_mock_storage {
use super::super::mock_storage::*;
#[test]
fn test_mock_storage() {
let mock_storage = MockStorage::new(100);
let uploader1 = FileUploader::new(&mock_storage);
let uploader2 = FileUploader::new(&mock_storage);
let usage_analyzer = UsageAnalyzer::new(&mock_storage, 0.75);
assert!(uploader1.upload("file1.txt", 20).is_ok());
assert!(usage_analyzer.is_usage_under_bound());
assert!(uploader2.upload("file2.txt", 30).is_ok());
assert!(usage_analyzer.is_usage_under_bound());
assert!(uploader1.upload("file3.txt", 40).is_ok());
assert!(!usage_analyzer.is_usage_under_bound());
assert_eq!(uploader2.upload("file4.txt", 50), Err(40));
assert!(!usage_analyzer.is_usage_under_bound());
assert!(uploader1.upload("file3.txt", 10).is_ok());
assert!(usage_analyzer.is_usage_under_bound());
}
}

View File

@@ -4,6 +4,16 @@
//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script.
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment11.zip` to <https://gg.kaist.ac.kr>.
pub mod bst;
pub mod bst_grade;
pub mod doubly_linked_list;
pub mod doubly_linked_list_grade;
pub mod graph;
pub mod graph_grade;
pub mod linked_list;
pub mod mock_storage;
pub mod mock_storage_grade;
pub mod turing_machine;
pub mod turing_machine_grade;
pub mod tv_room;
pub mod tv_room_grade;

View File

@@ -0,0 +1,139 @@
//! Simple Turing machien emulator
//!
//! Simple One-head, One-tape turing machine
//! See <https://en.wikipedia.org/wiki/Turing_machine> that describes what turing machine is.
//! See `test_turing_machine` module in `assignment11_grade.rs` for examples.
//!
//! Goal: To be accustomed with `RefCell`, `HashMap`
//!
//! Refer `turing_machine.rs` for test cases.
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{self, Formatter};
use thiserror::Error;
/// Error type for Turing machine
/// <https://google.github.io/comprehensive-rust/error-handling/deriving-error-enums.html>
#[derive(Debug, Error, PartialEq, Eq)]
pub enum TuringMachineError {
/// Invalid movement
/// You can't move left from the leftmost location
/// or move right from the rightmost location.
#[error("Invalid movement")]
InvalidMovement,
/// Exceeded maximum steps
#[error("Exceeded maximum steps")]
ExceedMaxSteps,
/// Invalid state or value
/// Occurs when you cannot find instruction for the current state and value
#[error("Invalid state or value")]
InvalidStateOrValue,
}
/// Turing Machine implementation
#[derive(Debug)]
pub struct TuringMachine<TMState, TMValue>
where
TMState: Default + Eq + PartialEq + std::hash::Hash + Clone,
TMValue: Eq + PartialEq + std::hash::Hash + Clone,
{
/// Number of steps taken by the Turing machine
pub steps: RefCell<usize>,
/// Table of instructions for the Turing machine
pub table: HashMap<(TMState, TMValue), (TMState, Move, TMValue)>,
/// Tape of the Turing machine. Finite length
pub tape: Vec<RefCell<TMValue>>,
}
/// Implementation of the movement instructions of the head of the tape.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Move {
/// Move Left
L,
/// Move Right
R,
/// Don't move
N,
}
/// Cursor for Turing machine
#[derive(Debug)]
pub struct Cursor<'a, TMState, TMValue>
where
TMState: Default + Eq + PartialEq + std::hash::Hash + Clone,
TMValue: Eq + PartialEq + std::hash::Hash + Clone,
{
// Turing mahcine
tm: &'a TuringMachine<TMState, TMValue>,
// Index of the tape
index: usize,
// Current state of the Turing machine
state: TMState,
}
impl<'a, TMState, TMValue> Cursor<'a, TMState, TMValue>
where
TMState: Default + Eq + PartialEq + std::hash::Hash + Clone,
TMValue: Eq + PartialEq + std::hash::Hash + Clone,
{
/// Generate new cursor
pub fn new(tm: &'a TuringMachine<TMState, TMValue>, state: TMState, index: usize) -> Self {
Cursor { tm, index, state }
}
/// Run the Turing machine until it halts (if it halts). Print every step of that.
pub fn run(&mut self, max_step: usize) -> Result<(TMValue, usize), TuringMachineError> {
let mut steps = self.tm.steps.borrow_mut();
while self.state != TMState::default() {
*steps += 1;
if *steps > max_step {
return Err(TuringMachineError::ExceedMaxSteps);
}
self.step()?;
// println!("{}", self);
}
Ok((self.get(), *steps))
}
/// Set tape value at the current index with `value`
/// You may need this function for `mov` function
fn set(&mut self, value: TMValue) -> TMValue {
todo!();
}
/// Step the Turing machine
/// Look at the `run` function to see how this function is used.
fn step(&mut self) -> Result<(), TuringMachineError> {
todo!();
}
/// Move the cursor while setting the value of the current index
fn mov(&mut self, new_value: TMValue, movement: &Move) -> Result<(), TuringMachineError> {
todo!();
}
/// Get the value of the current index
/// Look at the `run` function to see how this function is used.
fn get(&self) -> TMValue {
todo!();
}
}
impl<TMState, TMValue> TuringMachine<TMState, TMValue>
where
TMState: Default + Eq + PartialEq + std::hash::Hash + Clone,
TMValue: Eq + PartialEq + std::hash::Hash + Clone,
{
/// Generate new Turing machine
pub fn new(
table: HashMap<(TMState, TMValue), (TMState, Move, TMValue)>,
tape: Vec<RefCell<TMValue>>,
) -> Self {
todo!()
}
}

View File

@@ -0,0 +1,309 @@
//! Test cases for assignment11/turing_machine.rs
#[cfg(test)]
mod test_turing_machine {
use super::super::turing_machine::*;
use std::cell::RefCell;
use std::collections::HashMap;
#[test]
fn test_invalid_movement() {
/// Cell value of the tape
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
enum TMValue {
/// Zero
Zero,
/// One
One,
}
/// State for Turing machine
/// TODO: Modify this so that users can implement their own state
#[derive(Default, Debug, Eq, PartialEq, Hash, Clone)]
enum TMState {
/// Halt
#[default]
Halt,
/// A
A,
/// B
B,
}
let tape: Vec<RefCell<TMValue>> = vec![
TMValue::One,
TMValue::Zero,
TMValue::One,
TMValue::One,
TMValue::One,
TMValue::Zero,
TMValue::One,
]
.into_iter()
.map(|x| RefCell::new(x))
.collect();
let instr = HashMap::from([
(
(TMState::A, TMValue::Zero),
(TMState::B, Move::R, TMValue::One),
),
(
(TMState::A, TMValue::One),
(TMState::B, Move::L, TMValue::Zero),
),
(
(TMState::B, TMValue::Zero),
(TMState::A, Move::L, TMValue::One),
),
(
(TMState::B, TMValue::One),
(TMState::A, Move::R, TMValue::Zero),
),
]);
let mut tm = TuringMachine::new(instr, tape);
let mut cursor = Cursor::new(&tm, TMState::A, 0);
let result = cursor.run(1000);
assert_eq!(result, Err(TuringMachineError::InvalidMovement));
}
#[test]
fn test_write_15_fail_move() {
/// Cell value of the tape
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
enum TMValue {
/// Zero
Zero,
/// One
One,
}
/// State for Turing machine
/// TODO: Modify this so that users can implement their own state
#[derive(Default, Debug, Eq, PartialEq, Hash, Clone)]
enum TMState {
/// Halt
#[default]
Halt,
/// A
A,
/// B
B,
}
let tape: Vec<RefCell<TMValue>> = vec![TMValue::Zero; 10]
.into_iter()
.map(|x| RefCell::new(x))
.collect();
let instr = HashMap::from([
(
(TMState::A, TMValue::Zero),
(TMState::B, Move::R, TMValue::One),
),
(
(TMState::A, TMValue::One),
(TMState::B, Move::L, TMValue::One),
),
(
(TMState::B, TMValue::Zero),
(TMState::A, Move::L, TMValue::One),
),
(
(TMState::B, TMValue::One),
(TMState::Halt, Move::R, TMValue::One),
),
]);
let mut tm = TuringMachine::new(instr, tape);
let mut cursor = Cursor::new(&tm, TMState::A, 0);
let result = cursor.run(1000);
assert_eq!(result, Err(TuringMachineError::InvalidMovement));
}
#[test]
fn test_write_15_pass() {
/// Cell value of the tape
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
enum TMValue {
/// Zero
Zero,
/// One
One,
}
/// State for Turing machine
/// TODO: Modify this so that users can implement their own state
#[derive(Default, Debug, Eq, PartialEq, Hash, Clone)]
enum TMState {
/// Halt
#[default]
Halt,
/// A
A,
/// B
B,
}
let tape: Vec<RefCell<TMValue>> = vec![TMValue::Zero; 10]
.into_iter()
.map(|x| RefCell::new(x))
.collect();
let instr = HashMap::from([
(
(TMState::A, TMValue::Zero),
(TMState::B, Move::R, TMValue::One),
),
(
(TMState::A, TMValue::One),
(TMState::B, Move::L, TMValue::One),
),
(
(TMState::B, TMValue::Zero),
(TMState::A, Move::L, TMValue::One),
),
(
(TMState::B, TMValue::One),
(TMState::Halt, Move::R, TMValue::One),
),
]);
let mut tm = TuringMachine::new(instr, tape);
let mut cursor = Cursor::new(&tm, TMState::A, 2);
let result = cursor.run(1000);
for (idx, val) in tm.tape.iter().enumerate() {
if idx < 4 {
assert_eq!(*val.borrow(), TMValue::One);
} else {
assert_eq!(*val.borrow(), TMValue::Zero);
}
}
}
#[test]
fn test_write_zero_ones_fail_step() {
/// Cell value of the tape
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
enum TMValue {
/// Empty
Empty,
/// Zero
Zero,
/// One
One,
}
/// State for Turing machine
/// TODO: Modify this so that users can implement their own state
#[derive(Default, Debug, Eq, PartialEq, Hash, Clone)]
enum TMState {
/// Halt
#[default]
Halt,
/// A
A,
/// B
B,
}
let tape: Vec<RefCell<TMValue>> = vec![TMValue::Empty; 100]
.into_iter()
.map(|x| RefCell::new(x))
.collect();
let instr = HashMap::from([
(
(TMState::A, TMValue::Empty),
(TMState::B, Move::R, TMValue::Zero),
),
(
(TMState::B, TMValue::Empty),
(TMState::A, Move::R, TMValue::One),
),
]);
let mut tm = TuringMachine::new(instr, tape);
let mut cursor = Cursor::new(&tm, TMState::A, 0);
let result = cursor.run(10);
assert_eq!(result, Err(TuringMachineError::ExceedMaxSteps));
}
#[test]
fn test_write_zero_ones() {
/// Cell value of the tape
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
enum TMValue {
/// Empty
Empty,
/// Zero
Zero,
/// One
One,
}
/// State for Turing machine
/// TODO: Modify this so that users can implement their own state
#[derive(Default, Debug, Eq, PartialEq, Hash, Clone)]
enum TMState {
/// Halt
#[default]
Halt,
/// A
A,
/// B
B,
}
let tape: Vec<RefCell<TMValue>> = vec![TMValue::Empty; 100]
.into_iter()
.map(|x| RefCell::new(x))
.collect();
let instr = HashMap::from([
(
(TMState::A, TMValue::Empty),
(TMState::B, Move::R, TMValue::Zero),
),
(
(TMState::B, TMValue::Empty),
(TMState::A, Move::R, TMValue::One),
),
]);
let mut tm = TuringMachine::new(instr, tape);
let mut cursor = Cursor::new(&tm, TMState::A, 0);
let result = cursor.run(1000);
assert_eq!(result, Err(TuringMachineError::InvalidMovement));
for (idx, val) in tm.tape.iter().enumerate() {
if idx % 2 == 0 {
assert_eq!(*val.borrow(), TMValue::Zero);
} else {
assert_eq!(*val.borrow(), TMValue::One);
}
}
}
}

View File

@@ -15,6 +15,8 @@
//! Consult the following documentations:
//! - <https://doc.rust-lang.org/book/ch15-04-rc.html#rct-the-reference-counted-smart-pointer>
//! - <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#having-multiple-owners-of-mutable-data-by-combining-rct-and-refcellt>
//!
//! Refer `tv_room_grade.rs` for test cases.
use std::{cell::RefCell, rc::Rc};

View File

@@ -0,0 +1,32 @@
//! Test cases for assignment11/tv_room.rs
#[cfg(test)]
mod test_tv_room {
use super::super::tv_room::*;
#[test]
fn test_tv_room() {
let tv_room = TVRoom::new();
assert!(!tv_room.is_opened());
// Turn on and add new guests.
let manager = tv_room.open().unwrap();
assert!(tv_room.is_opened());
let guest1 = manager.new_guest();
let guest2 = manager.new_guest();
drop(manager);
drop(guest1);
assert!(tv_room.open().is_none());
drop(guest2);
assert!(!tv_room.is_opened());
// Turn on and add new guests.
let manager = tv_room.open().unwrap();
assert!(tv_room.is_opened());
let guest3 = manager.new_guest();
drop(guest3);
assert!(tv_room.is_opened());
drop(manager);
assert!(!tv_room.is_opened());
}
}

View File

@@ -0,0 +1,52 @@
//! Flipping card game.
//!
//! For this assignment, you have to see `play` function in `card_grade.rs` file.
//! Multiple threads will be created and they will run as enemy bots(`bot_threads`).
//! Strategy of the enemy bots is implemented in the closure of the `thread::spawn` function.
//! Your goal is to beat them so that there are more white cards than blue cards in the ground.
//! Write your strategy in the `flip_card_strategy` function of the `Player` struct.
//!
//! Have fun!
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
/// Color represents the color of the card.
/// The color of a card can be either Blue or White.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Color {
/// blue
Blue,
/// white
White,
}
/// Player struct represents a player in the card game.
/// Each player has a memory which is represented as a HashMap.
#[derive(Debug)]
pub struct Player {
memory: HashMap<isize, isize>,
}
impl Default for Player {
fn default() -> Self {
Self::new()
}
}
impl Player {
/// Creates a new player with an empty memory.
pub fn new() -> Self {
Self {
memory: HashMap::new(),
}
}
/// This function should return the index of the card to flip and the color to change to.
pub fn flip_card_strategy(&mut self) -> (usize, Color) {
todo!()
}
}

View File

@@ -0,0 +1,141 @@
//! Test cases for assignment12/card.rs
#[cfg(test)]
mod test_card {
use super::super::card::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Barrier, Mutex};
use std::thread;
use std::time::Duration;
const NUM_CARDS: usize = 10_000;
const DURATION: u64 = 1;
const NUM_ENEMIES: usize = 100;
#[derive(Clone, Debug)]
struct Card {
color: Arc<Mutex<Color>>,
}
impl Card {
fn new() -> Self {
Card {
color: Arc::new(Mutex::new(Color::Blue)),
}
}
fn flip(&self, new_color: Color) {
let mut color = self.color.lock().unwrap();
*color = new_color;
}
fn get_color(&self) -> Color {
let color = self.color.lock().unwrap();
*color
}
}
#[derive(Clone, Debug)]
struct Ground {
cards: Vec<Card>,
}
impl Ground {
fn new() -> Self {
let cards: Vec<_> = (0..NUM_CARDS).map(|_| Card::new()).collect();
Ground { cards }
}
fn flip_card(&self, idx: usize, color: Color) {
self.cards[idx % NUM_CARDS].flip(color);
}
fn get_card_color(&self, idx: usize) -> Color {
self.cards[idx % NUM_CARDS].get_color()
}
}
#[test]
fn play() {
let ground = Ground::new();
let barrier = Arc::new(Barrier::new(
NUM_ENEMIES + 1 /*Player*/ + 1, /*Referee*/
));
let playing = Arc::new(AtomicBool::new(true));
// Create a thread for the student's strategy
let mut player = Player::new();
let player_thread = {
let ground = ground.clone();
let barrier = barrier.clone();
let playing = playing.clone();
// The player's strategy thread
thread::spawn(move || {
// Get, Set, Ready, Go!
let _ = barrier.wait();
// As long as the game is still playing...
while playing.load(Ordering::SeqCst) {
let (idx, color) = player.flip_card_strategy();
ground.flip_card(idx, color);
}
})
};
// Create multiple threads for the computer's strategy
let dist = NUM_CARDS / NUM_ENEMIES;
let bot_threads: Vec<_> = (0..NUM_ENEMIES)
.map(|i| {
let ground = ground.clone();
let barrier = barrier.clone();
let playing = playing.clone();
let init = i * dist;
let mut cnt = 0;
thread::spawn(move || {
// Get, Set, Ready, Go!
let _ = barrier.wait();
// As long as the game is still playing...
while playing.load(Ordering::SeqCst) {
let idx = init + (cnt % dist);
match ground.get_card_color(idx) {
Color::White => ground.flip_card(idx, Color::Blue),
Color::Blue => thread::sleep(Duration::from_micros(1)),
};
cnt += 1;
}
})
})
.collect();
// Get, Set, Ready, Go!
let _ = barrier.wait();
// Wait for a while and stop the game
thread::sleep(Duration::from_secs(DURATION));
playing.store(false, Ordering::SeqCst);
// Wait for all threads to finish
player_thread.join().unwrap();
for bot_thread in bot_threads {
bot_thread.join().unwrap();
}
// Count the number of white and blue cards
let mut white_cnt = 0;
let mut blue_cnt = 0;
for card in ground.cards {
match card.get_color() {
Color::White => white_cnt += 1,
Color::Blue => blue_cnt += 1,
}
}
// Print the winner
println!("[White: {white_cnt}, Blue: {blue_cnt}]");
assert!(white_cnt > blue_cnt, "You lose...",);
}
}

View File

@@ -0,0 +1,37 @@
//! Demultiplexing sender
//!
//! Implement demultiplexing sender
//! Demultiplexer, `Demux` in short, is a device that has one input and many outputs.
//! It distributes the input to the outputs according to the control signal.
//! It is used when a circuit wishes to send a signal to one of many devices.
//! For more information, refer <https://www.electronics-tutorials.ws/combination/comb_3.html>
//!
//! In this assignment, closure `f` will be given as an argument to `demux` function.
//! This closure will be used to determine which destination to send the input data.
//!
//! Refer `demux_grade.rs` for test cases
use std::sync::mpsc::{channel, Receiver, SendError, Sender};
use std::thread;
/// Sender for demux.
#[derive(Debug)]
pub struct DemuxSender<T, F: Fn(&T) -> bool> {
tx_true: Sender<T>,
tx_false: Sender<T>,
f: F,
}
impl<T, F: Fn(&T) -> bool> DemuxSender<T, F> {
/// send
///
/// If `f(&value)` is true, send `value` to `tx_true`. Otherwise, send `value` to `tx_false`.
pub fn send(&self, value: T) -> Result<(), SendError<T>> {
todo!()
}
}
/// Demux.
pub fn demux<T, F: Fn(&T) -> bool>(f: F) -> (DemuxSender<T, F>, Receiver<T>, Receiver<T>) {
todo!()
}

View File

@@ -0,0 +1,36 @@
//! Test cases for assignment12/demux.rs
#[cfg(test)]
mod test_demux {
use super::super::demux::*;
use ntest::timeout;
use std::sync::mpsc::channel;
use std::thread;
#[test]
#[timeout(5000)]
fn test_demux() {
let (tx, rx1, rx2) = demux::<u32, _>(|x| x % 2 == 0);
let thread_tx = thread::spawn(move || {
for i in 0..100 {
tx.send(i).unwrap();
}
});
let thread_rx1 = thread::spawn(move || {
let sum: u32 = rx1.iter().sum();
assert_eq!(sum, (0..100).filter(|x| x % 2 == 0).sum());
});
let thread_rx2 = thread::spawn(move || {
let sum: u32 = rx2.iter().sum();
assert_eq!(sum, (0..100).filter(|x| x % 2 != 0).sum());
});
thread_tx.join().unwrap();
thread_rx1.join().unwrap();
thread_rx2.join().unwrap();
}
}

View File

@@ -0,0 +1,21 @@
//! Funnel
//!
//! Spawn a thread that executes a funnel.
//! Funnel will receive data from multiple receivers and send it to a single sender.
//! Also, the funnel will filter out data that does not pass the filter function.
//!
//! Refer `funnel_grade.rs` for test cases
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
/// Spawn a thread that concurrently receive datas from `rxs`, send it to `tx` if it makes `f` true. Returns its handle.
pub fn spawn_funnel<T, F>(rxs: Vec<Receiver<T>>, tx: Sender<T>, f: F) -> JoinHandle<()>
where
T: Send + 'static,
F: Send + Sync + Fn(&T) -> bool + 'static,
{
todo!()
}

View File

@@ -0,0 +1,33 @@
//! Test cases for assignment12/funnel.rs
#[cfg(test)]
mod test_funnel {
use super::super::funnel::*;
use ntest::timeout;
use std::sync::mpsc::channel;
use std::thread;
#[test]
#[timeout(5000)]
fn test_funnel_concurrent() {
let (txs, rxs): (Vec<_>, Vec<_>) = (0..10).map(|_| channel::<u32>()).unzip();
let (tx, rx) = channel::<u32>();
let filter = |x: &u32| x % 2 == 0;
let thread_txs_rx = thread::spawn(move || {
for i in 0..100 {
let idx = (i * 7) % 13 * 17 % 10;
txs[idx].send(i as u32).unwrap();
if i % 2 == 0 {
let x = rx.recv().unwrap();
assert_eq!(x, i as u32);
}
}
});
let thread_funnel = spawn_funnel(rxs, tx, filter);
thread_txs_rx.join().unwrap();
thread_funnel.join().unwrap();
}
}

View File

@@ -0,0 +1,15 @@
//! Assignment 12: Concurrency.
//!
//! The primary goal of this assignment is to get used to concurrency.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-12.sh` works fine.
//! See `assignment12_grade.rs` and `/scripts/grade-12.sh` for the test script.
pub mod card;
pub mod card_grade;
pub mod demux;
pub mod demux_grade;
pub mod funnel;
pub mod funnel_grade;
pub mod small_exercises;
pub mod small_exercises_grade;

View File

@@ -1,23 +1,25 @@
#![allow(single_use_lifetimes)]
//! Assignment 12: Concurrency.
//! Small exercises
//!
//! The primary goal of this assignment is to get used to concurrency.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-12.sh` works fine.
//! See `assignment12_grade.rs` and `/scripts/grade-12.sh` for the test script.
//! Refer `small_exercises_grade.rs` for test cases
use std::sync::mpsc::{Receiver, RecvError, Sender};
use std::thread;
use etrace::*;
/// The "pong" function (read the test script to figure out what it should do).
/// The "pong" function
///
/// Data will be sent and received through `rx` and `tx`.
/// Read the `test_ping_pong` function in `small_exercises_grade.rs` to figure out what it should do.
pub fn pong(rx1: &mut Receiver<u32>, tx2: &mut Sender<u32>) -> bool {
todo!()
}
/// Executes the given functions (f1, f2) in concurrent and returns the results.
///
/// Read the `test_scoped_thread` function in `small_exercises_grade.rs` to figure out what it should do.
pub fn use_scoped_thread<'scope, 'env, T1, T2, F1, F2>(
s: &'scope thread::Scope<'scope, 'env>,
f1: F1,

View File

@@ -0,0 +1,86 @@
//! Test cases for assignment12/small_exercises_grade.rs
#[cfg(test)]
mod test_pingpong {
use super::super::small_exercises::*;
use ntest::timeout;
use std::sync::mpsc::channel;
use std::thread;
#[test]
fn test_ping_pong() {
let (tx1, mut rx1) = channel();
let (mut tx2, rx2) = channel();
let thread_ping = thread::spawn(move || {
for i in 0..100 {
tx1.send(i).unwrap();
let x = rx2.recv().unwrap();
assert_eq!(x, i + 1);
}
});
let thread_pong = thread::spawn(move || while pong(&mut rx1, &mut tx2) {});
thread_ping.join().unwrap();
thread_pong.join().unwrap();
}
#[test]
fn test_scoped_thread() {
for i in 0..100 {
let v = (0..i).collect::<Vec<u32>>();
thread::scope(|s| {
let (r1, r2) = use_scoped_thread(
s,
|| v.iter().sum::<u32>(),
|| v.windows(2).map(|x| x[0] * x[1]).sum::<u32>(),
);
assert_eq!(r1, v.iter().sum());
assert_eq!(r2, v.windows(2).map(|x| x[0] * x[1]).sum());
});
}
}
#[test]
#[timeout(5000)]
fn test_scoped_thread_concurrent() {
use std::sync::Mutex;
let m = Mutex::new(0);
let (r1, r2) = thread::scope(|s| {
use_scoped_thread(
s,
|| {
for i in 0..100 {
loop {
let mut a = m.lock().unwrap();
if *a == 2 * i {
*a += 1;
break;
}
}
}
thread::current().id()
},
|| {
for i in 0..100 {
loop {
let mut a = m.lock().unwrap();
if *a == 2 * i + 1 {
*a += 1;
break;
}
}
}
thread::current().id()
},
)
});
assert!(r1 != r2);
}
}

View File

@@ -25,8 +25,6 @@ mod assignment09_grade;
pub mod assignment10;
mod assignment10_grade;
pub mod assignment11;
mod assignment11_grade;
pub mod assignment12;
mod assignment12_grade;
pub mod assignment13;
mod assignment13_grade;