mirror of
https://github.com/kmc7468/cs220.git
synced 2025-12-12 21:08:45 +00:00
add 8, 11, 12
This commit is contained in:
267
Cargo.lock
generated
267
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
202
src/assignments/assignment11/bst.rs
Normal file
202
src/assignments/assignment11/bst.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
53
src/assignments/assignment11/bst_grade.rs
Normal file
53
src/assignments/assignment11/bst_grade.rs
Normal 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![]);
|
||||
}
|
||||
}
|
||||
101
src/assignments/assignment11/doubly_linked_list.rs
Normal file
101
src/assignments/assignment11/doubly_linked_list.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
43
src/assignments/assignment11/doubly_linked_list_grade.rs
Normal file
43
src/assignments/assignment11/doubly_linked_list_grade.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
93
src/assignments/assignment11/graph.rs
Normal file
93
src/assignments/assignment11/graph.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
63
src/assignments/assignment11/graph_grade.rs
Normal file
63
src/assignments/assignment11/graph_grade.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
27
src/assignments/assignment11/linked_list_grade.rs
Normal file
27
src/assignments/assignment11/linked_list_grade.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
31
src/assignments/assignment11/mock_storage_grade.rs
Normal file
31
src/assignments/assignment11/mock_storage_grade.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
139
src/assignments/assignment11/turing_machine.rs
Normal file
139
src/assignments/assignment11/turing_machine.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
309
src/assignments/assignment11/turing_machine_grade.rs
Normal file
309
src/assignments/assignment11/turing_machine_grade.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
|
||||
32
src/assignments/assignment11/tv_room_grade.rs
Normal file
32
src/assignments/assignment11/tv_room_grade.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
52
src/assignments/assignment12/card.rs
Normal file
52
src/assignments/assignment12/card.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
141
src/assignments/assignment12/card_grade.rs
Normal file
141
src/assignments/assignment12/card_grade.rs
Normal 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...",);
|
||||
}
|
||||
}
|
||||
37
src/assignments/assignment12/demux.rs
Normal file
37
src/assignments/assignment12/demux.rs
Normal 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!()
|
||||
}
|
||||
36
src/assignments/assignment12/demux_grade.rs
Normal file
36
src/assignments/assignment12/demux_grade.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
21
src/assignments/assignment12/funnel.rs
Normal file
21
src/assignments/assignment12/funnel.rs
Normal 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!()
|
||||
}
|
||||
33
src/assignments/assignment12/funnel_grade.rs
Normal file
33
src/assignments/assignment12/funnel_grade.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
15
src/assignments/assignment12/mod.rs
Normal file
15
src/assignments/assignment12/mod.rs
Normal 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;
|
||||
@@ -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,
|
||||
86
src/assignments/assignment12/small_exercises_grade.rs
Normal file
86
src/assignments/assignment12/small_exercises_grade.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user