Merge from cs220-private

This commit is contained in:
AnHaechan
2023-08-23 00:30:26 +00:00
104 changed files with 5096 additions and 1249 deletions

616
Cargo.lock generated
View File

@@ -3,10 +3,68 @@
version = 3
[[package]]
name = "anyhow"
version = "1.0.66"
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[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"
@@ -16,24 +74,27 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "block-buffer"
version = "0.10.3"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cc"
version = "1.0.77"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
@@ -43,55 +104,65 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.0.29"
version = "4.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3"
dependencies = [
"bitflags",
"clap_builder",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.0.21"
version = "4.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 2.0.29",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [
"os_str_bytes",
]
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "cpufeatures"
version = "0.2.5"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
dependencies = [
"libc",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -99,9 +170,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@@ -110,9 +181,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
@@ -123,9 +194,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
@@ -145,21 +216,27 @@ name = "cs220"
version = "0.1.0"
dependencies = [
"anyhow",
"approx",
"clap",
"etrace",
"itertools",
"lazy_static",
"ndarray",
"ndarray-rand",
"ntest",
"num-traits",
"pest",
"pest_derive",
"rand",
"rayon",
"thiserror",
]
[[package]]
name = "digest"
version = "0.10.6"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
@@ -167,19 +244,25 @@ dependencies = [
[[package]]
name = "either"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.2.8"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
"windows-sys",
]
[[package]]
@@ -200,65 +283,69 @@ checksum = "f17311e68ea07046ee809b8513f6c259518bc10173681d98c21f8c3926f56f40"
[[package]]
name = "generic-array"
version = "0.14.6"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.1.19"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "hermit-abi"
version = "0.2.6"
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"libc",
]
[[package]]
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys",
"equivalent",
"hashbrown",
]
[[package]]
name = "is-terminal"
version = "0.4.1"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
@@ -271,25 +358,71 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.138"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[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"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[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"
version = "0.7.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
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"
@@ -308,7 +441,7 @@ checksum = "be7d33be719c6f4d09e64e27c1ef4e73485dc4cc1f4d22201f89860a7fe22e22"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -320,36 +453,59 @@ 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]]
name = "num_cpus"
version = "1.14.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi 0.1.19",
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.18.0"
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 = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "pest"
version = "2.5.1"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0"
checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a"
dependencies = [
"thiserror",
"ucd-trie",
@@ -357,9 +513,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.5.1"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344"
checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853"
dependencies = [
"pest",
"pest_generator",
@@ -367,97 +523,123 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.5.1"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c"
checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
"syn 2.0.29",
]
[[package]]
name = "pest_meta"
version = "2.5.1"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20"
checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48"
dependencies = [
"once_cell",
"pest",
"sha1",
"sha2",
]
[[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"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"thiserror",
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.47"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.6.0"
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
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.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.10.1"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -467,13 +649,12 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.36.4"
version = "0.38.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
@@ -481,21 +662,15 @@ dependencies = [
[[package]]
name = "scopeguard"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.149"
name = "sha2"
version = "0.10.7"
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 = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -510,9 +685,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.105"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
@@ -520,60 +695,76 @@ dependencies = [
]
[[package]]
name = "termcolor"
version = "1.1.3"
name = "syn"
version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [
"winapi-util",
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.29",
]
[[package]]
name = "toml"
version = "0.5.9"
name = "toml_datetime"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
[[package]]
name = "toml_edit"
version = "0.19.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
dependencies = [
"serde",
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "typenum"
version = "1.15.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ucd-trie"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unicode-ident"
version = "1.0.5"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
@@ -582,41 +773,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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -629,42 +804,51 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97"
dependencies = [
"memchr",
]

View File

@@ -6,12 +6,18 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.66"
clap = { version = "4.0.29", features = ["derive"] }
anyhow = "1.0.75"
clap = { version = "4.3.23", features = ["derive"] }
etrace = "1.1.1"
itertools = "0.10.5"
itertools = "0.11.0"
lazy_static = "1.4.0"
pest = "2.5.1"
pest_derive = "2.5.1"
rayon = "1.6.0"
pest = "2.7.2"
pest_derive = "2.7.2"
rayon = "1.7.0"
thiserror = "1.0.47"
ntest = "0.9.0"
approx = "0.5.1"
num-traits = "0.2.16"
ndarray = "0.15.6"
ndarray-rand = "0.14.0"
rand = "0.8.5"

View File

@@ -33,12 +33,13 @@
+ Mathematics (MAS101): proposition statement and proof
+ Programming (CS101): basic programming skills
Without a proper understanding of these topics, you will likely struggle in this course.
- Without a proper understanding of these topics, you will likely struggle in this course.
- In the assignment, for the concepts that don't come out in prerequisite courses, we tried to comment on the relevant resources in the assignment code. Please read them carefully.
### Tools
Make sure you're capable of using the following development tools:
Make sure that you're capable of using the following development tools:
- [Git](https://git-scm.com/): for downloading the homework skeleton and version-controlling your
development. If you're not familiar with Git, walk through [this
@@ -97,6 +98,8 @@ Make sure you're capable of using the following development tools:
- [Visual Studio Code](https://code.visualstudio.com/) (optional): for developing your homework. If you prefer other editors, you're good to go.
- [ChatGPT](https://chat.openai.com/) or other LLMs (optional): for your homework.
- In the era of AI, we believe that it is crucial to learn how to wisely use AI in programming.
- So we adjusted the difficulty of homework assuming that you'll use ChatGPT 3.5 (or equivalent) to solve it.
- [Development server](https://cloud.fearless.systems/)

View File

@@ -19,4 +19,5 @@ module BinarySearch
=
(* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
0 (* TODO *)
end
end

View File

@@ -0,0 +1,29 @@
(* Max
Given an array `a` of natural numbers with length `n`,
return the maximum element of the array.
You should stengthen the loop invariant.
*)
module Max
use int.Int
use ref.Ref
use array.Array
let max (a: array int) (n: int) : (max: int)
requires { n = length a }
requires { forall i. 0 <= i < n -> a[i] >= 0 }
ensures { forall i. 0 <= i < n -> a[i] <= max }
ensures { exists i. 0 <= i < n -> a[i] = max }
= let ref max = 0 in
for i = 0 to n - 1 do
(* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
invariant { true (* TODO: Replace `true` with your solution *) }
(* IMPORTANT: DON'T MODIFY THE BELOW LINES *)
if max < a[i] then max <- a[i];
done;
max
end

View File

@@ -0,0 +1,41 @@
(* Pascal
Prove that the Pascal's triangle indeed computes combinations.
HINT: https://en.wikipedia.org/wiki/Pascal%27s_triangle
*)
module Pascal
use int.Int
use ref.Ref
use array.Array
let rec function comb (n k: int) : int
requires { 0 <= k <= n }
variant { n }
ensures { result >= 1 }
= if k = 0 || k = n then 1 else comb (n-1) k + comb (n-1) (k-1)
(* Insert appropriate invariants so that Why3 can verify this function. *)
(* You SHOULD understand the Pascal's triangle first to find good invariants. *)
let chooses (n : int) : array int
requires { n > 0 }
ensures { forall i: int.
0 <= i < length result -> result[i] = comb n i }
=
let ref row = Array.make 1 1 in
for r = 1 to n do
invariant { length row = r }
invariant { forall c: int. 0 <= c < r -> row[c] = comb (r-1) c }
let new_row = Array.make (r+1) 1 in
for c = 1 to r-1 do
(* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
invariant { true (* TODO: Replace `true` with your solution *) }
(* IMPORTANT: DON'T MODIFY THE BELOW LINES *)
new_row[c] <- row[c-1] + row[c]
done;
row <- new_row
done;
row
end

View File

@@ -1,34 +0,0 @@
(* Euclidean division
1. Prove soundness, i.e. (division a b) returns an integer q such that
a = bq+r and 0 <= r < b for some r.
(You have to strengthen the precondition.)
Do you have to require b <> 0? Why?
2. Prove termination.
(You may have to strengthen the precondition even further.)
*)
module Division
use int.Int
let division (a b: int) : int
requires { a > 0 /\ b > 0 }
ensures { exists r: int. a = b * result + r /\ 0 <= r < b }
=
let ref q = 0 in
let ref r = a in
while r >= b do
invariant { a = b * q + r /\ r >= 0 }
variant { r }
q <- q + 1;
r <- r - b
done;
q
let main () =
division 1000 42
end

View File

@@ -0,0 +1,29 @@
(* Euclidean division
1. Prove correctness of euclideian divison:
`division a b` returns an integer `q` such that
`a = bq+r` and `0 <= r < b` for some `r`.
- You have to strengthen the loop invariant.
*)
module Division
use int.Int
let division (a b: int) : int
requires { a >= 0 }
requires { b > 0 }
ensures { exists r: int. a = b * result + r /\ 0 <= r < b }
=
let ref q = 0 in
let ref r = a in
while r >= b do
invariant { true (* TODO: Replace `true` with your solution *) }
variant { r }
q <- q + 1;
r <- r - b
done;
q
end

View File

@@ -0,0 +1,49 @@
(* Two programs to compute the factorial
Questions:
1. In module FactRecursive:
a. Implement the program that satisfies specification.
2. In module FactLoop:
a. Strengthen the invariant to prove correctness of the given implementation.
b. Select a correct variant to prove the termination.
*)
module FactRecursive
use int.Int
use int.Fact
let rec fact_rec (n: int) : int
requires { n >= 0 }
ensures { result = fact n }
variant { n }
= (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
0 (*TODO*)
end
module FactLoop
use int.Int
use int.Fact
let fact_loop (n: int) : int
requires { 0 < n }
ensures { result = fact n }
= let ref m = 0 in
let ref r = 1 in
while m < n do
invariant { true (* TODO: Replace `true` with your solution *) }
variant { n (* TODO: Replace `n` with your solution *) }
m <- m + 1;
r <- r * m
done;
r
end

View File

@@ -0,0 +1,51 @@
(* Two Way Sort
The following program sorts an array of Boolean values, with False<True.
E.g.
two_way_sorted [True; False; False; True; False]
= [False; False; False; True; True]
- Strengthen the invariants to prove correctness.
*)
module TwoWaySort
use int.Int
use bool.Bool
use ref.Refint
use array.Array
use array.ArraySwap
use array.ArrayPermut
predicate (<<) (x y: bool) = x = False \/ y = True
predicate sorted (a: array bool) =
forall i1 i2: int. 0 <= i1 <= i2 < a.length -> a[i1] << a[i2]
let two_way_sort (a: array bool) : unit
ensures { sorted a }
ensures { permut_all (old a) a }
=
let ref i = 0 in
let ref j = length a - 1 in
while i < j do
invariant { 0 <= i /\ j < length a }
invariant { forall i1: int. 0 <= i1 < i
-> true (* TODO: Replace `true` with your solution *) }
invariant { forall i2: int. j < i2 < length a
-> true (* TODO: Replace `true` with your solution *) }
invariant { true (* TODO: Replace `true` with your solution *) }
variant { j - i }
if not a[i] then
incr i
else if a[j] then
decr j
else begin
swap a i j;
incr i;
decr j
end
done
end

View File

@@ -0,0 +1,30 @@
(* Euclidean division
1. Prove correctness of euclideian divison:
`division a b` returns an integer `q` such that
`a = bq+r` and `0 <= r < b` for some `r`.
- You have to strengthen the precondition.
- You have to strengthen the loop invariant.
*)
module Division
use int.Int
let division (a b: int) : int
requires { a >= 0 }
requires { b > 0 }
ensures { exists r: int. a = b * result + r /\ 0 <= r < b }
=
let ref q = 0 in
let ref r = a in
while r >= b do
invariant { a = b * q + r /\ 0 <= r }
variant { r }
q <- q + 1;
r <- r - b
done;
q
end

View File

@@ -1,23 +1,5 @@
(* Two programs to compute the factorial
Note: function "fact" from module int.Fact (already imported)
can be used in specifications.
Questions:
1. In module FactRecursive:
a. Prove soundness of function fact_rec.
b. Prove its termination.
2. In module FactLoop:
a. Prove soundness of function fact_loop.
b. Prove its termination.
c. Change the code to use a for loop instead of a while loop.
*)
module FactRecursive
@@ -52,4 +34,4 @@ module FactLoop
done;
r
end
end

View File

@@ -1,22 +1,12 @@
(* Two Way Sort
The following program sorts an array of Boolean values, with False<True.
E.g.
two_way_sorted [True; False; False; True; False]
= [False; False; False; True; True]
Questions:
1. Prove safety i.e. the absence of array access out of bounds.
2. Prove termination.
3. Prove that array a is sorted after execution of function two_way_sort
(using the predicate sorted that is provided).
4. Show that after execution the array contents is a permutation of its
initial contents. Use the library predicate "permut_all" to do so
(the corresponding module ArrayPermut is already imported).
You can refer to the contents of array a at the beginning of the
function with notation "a at Init".
- Strengthen the invariant to prove correctness.
*)
module TwoWaySort
@@ -34,15 +24,16 @@ module TwoWaySort
forall i1 i2: int. 0 <= i1 <= i2 < a.length -> a[i1] << a[i2]
let two_way_sort (a: array bool) : unit
ensures { true }
ensures { sorted a }
ensures { permut_all (old a) a }
=
label Init in
let ref i = 0 in
let ref j = length a - 1 in
while i < j do
invariant { 0 <= i /\ j < length a }
invariant { forall i1: int. 0 <= i1 < i -> a[i1] = False }
invariant { forall i2: int. j < i2 < length a -> a[i2] = True }
invariant { 0 <= i /\ j < length a }
invariant { permut_all (old a) a }
variant { j - i }
if not a[i] then
incr i
@@ -55,4 +46,4 @@ module TwoWaySort
end
done
end
end

View File

@@ -1 +1 @@
1.70.0
1.71.0

View File

@@ -24,7 +24,7 @@ run_linters || exit 1
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
TESTS=("--lib assignment01_grade")
TESTS=("--lib assignment01")
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 assignment02_grade")
TESTS=("--lib assignment02")
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 assignment03_grade")
TESTS=("--lib assignment03")
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 assignment04_grade")
TESTS=("--lib assignment04")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi

View File

@@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0")
source $BASEDIR/grade-utils.sh
RUNNERS=(
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
)
# Lints.
@@ -22,12 +22,12 @@ run_linters || exit 1
# Executes test for each runner.
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
echo "Running with $RUNNER..."
TESTS=("--lib assignment06_grade")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
TESTS=("--lib assignment06")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
done
exit 0

View File

@@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0")
source $BASEDIR/grade-utils.sh
RUNNERS=(
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
)
# Lints.
@@ -22,12 +22,12 @@ run_linters || exit 1
# Executes test for each runner.
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
echo "Running with $RUNNER..."
TESTS=("--lib assignment07_grade")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
TESTS=("--lib assignment07")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
done
exit 0

View File

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

View File

@@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0")
source $BASEDIR/grade-utils.sh
RUNNERS=(
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
)
# Lints.
@@ -22,12 +22,12 @@ run_linters || exit 1
# Executes test for each runner.
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
echo "Running with $RUNNER..."
TESTS=("--lib assignment09_grade")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
TESTS=("--lib assignment09")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
done
exit 0

View File

@@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0")
source $BASEDIR/grade-utils.sh
RUNNERS=(
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
)
# Lints.
@@ -22,12 +22,12 @@ run_linters || exit 1
# Executes test for each runner.
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
echo "Running with $RUNNER..."
TESTS=("--lib assignment10_grade")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
TESTS=("--lib assignment10")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
done
exit 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 assignment12")
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 assignment13_grade")
TESTS=("--lib assignment13")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi

View File

@@ -0,0 +1,10 @@
//! Assignment 1: Preparing Rust Development Environment.
//!
//! The primary goal of this assignment is bringing up SSH, VSCode, and all the other necessary tools to develop Rust programs.
//! Please make sure you're comfortable with developing Rust programs before moving on to the next assignments.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-01.sh` works fine.
//! See `assigment01/small_exercises_grade.rs` and `/scripts/grade-01.sh` for the test script.
pub mod small_exercises;
mod small_exercises_grade;

View File

@@ -1,10 +1,8 @@
//! Assignment 1: Preparing Rust Development Environment.
//!
//! The primary goal of this assignment is bringing up SSH, VSCode, and all the other necessary tools to develop Rust programs.
//! Please make sure you're comfortable with developing Rust programs before moving on to the next assignments.
//! Welcome to the CS220 course!
//!
//! You should fill out `add()` and `sub()` function bodies in such a way that `/scripts/grade-01.sh` works fine.
//! See `assignment01_grade.rs` and `/scripts/grade-01.sh` for the test script.
//! See `small_problems_grade.rs` and `/scripts/grade-01.sh` for the test script.
//!
//! Hint: <https://doc.rust-lang.org/std/primitive.usize.html>

View File

@@ -1,6 +1,6 @@
#[cfg(test)]
mod test {
use super::super::assignment01::*;
use crate::assignments::assignment01::small_exercises::*;
#[test]
fn test_add_7_3() {

View File

@@ -1,143 +0,0 @@
//! Assignment 2: Mastering common programming concepts (1/2).
//!
//! The primary goal of this assignment is to re-learn the common programming concepts in Rust, especially those in the Rust Book chapters 3 and 5.
//! Please make sure you're comfortable with the concepts to proceed on to the next assignments.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-02.sh` works fine.
//! See `assignment02_grade.rs` and `/scripts/grade-02.sh` for the test script.
use std::ops::Mul;
const FAHRENHEIT_OFFSET: f64 = 32.0;
const FAHRENHEIT_SCALE: f64 = 5.0 / 9.0;
/// Converts Fahrenheit to Celsius temperature degree.
pub fn fahrenheit_to_celsius(degree: f64) -> f64 {
todo!()
}
/// Capitalizes English alphabets (leaving the other characters intact).
pub fn capitalize(input: String) -> String {
todo!()
}
/// Returns the sum of the given array. (We assume the absence of integer overflow.)
pub fn sum_array(input: &[u64]) -> u64 {
todo!()
}
/// Given a non-negative integer, say `n`, return the smallest integer of the form `3^m` that's greater than or equal to `n`.
///
/// For instance, up3(6) = 9, up3(9) = 9, up3(10) = 27. (We assume the absence of integer overflow.)
pub fn up3(n: u64) -> u64 {
todo!()
}
/// Returns the greatest common divisor (GCD) of two non-negative integers. (We assume the absence of integer overflow.)
pub fn gcd(lhs: u64, rhs: u64) -> u64 {
todo!()
}
/// Returns the array of nC0, nC1, nC2, ..., nCn, where nCk = n! / (k! * (n-k)!). (We assume the absence of integer overflow.)
///
/// Consult <https://en.wikipedia.org/wiki/Pascal%27s_triangle> for computation of binomial coefficients without integer overflow.
pub fn chooses(n: u64) -> Vec<u64> {
todo!()
}
/// Returns the "zip" of two vectors.
///
/// For instance, `zip(vec![1, 2, 3], vec![4, 5])` equals to `vec![(1, 4), (2, 5)]`.
/// Here, `3` is ignored because it doesn't have a partner.
pub fn zip(lhs: Vec<u64>, rhs: Vec<u64>) -> Vec<(u64, u64)> {
todo!()
}
/// 2x2 matrix of the following configuration:
///
/// a, b
/// c, d
#[derive(Debug, Clone, Copy)]
struct Mat2 {
a: u64,
b: u64,
c: u64,
d: u64,
}
/// 2x1 matrix of the following configuration:
///
/// a
/// b
#[derive(Debug, Clone, Copy)]
struct Vec2 {
a: u64,
b: u64,
}
impl Mat2 {
/// Creates an identity matrix.
fn new() -> Self {
Self {
a: 1,
b: 0,
c: 0,
d: 1,
}
}
}
impl Mul<Mat2> for Mat2 {
type Output = Mat2;
fn mul(self, rhs: Mat2) -> Self::Output {
todo!()
}
}
impl Mul<Vec2> for Mat2 {
type Output = Vec2;
fn mul(self, rhs: Vec2) -> Self::Output {
todo!()
}
}
impl Mat2 {
/// Calculates the power of matrix.
fn power(self, power: u64) -> Mat2 {
todo!()
}
}
impl Vec2 {
/// Gets the upper value of vector.
fn get_upper(self) -> u64 {
todo!()
}
}
/// The matrix used for calculating Fibonacci numbers.
const FIBONACCI_MAT: Mat2 = Mat2 {
a: 1,
b: 1,
c: 1,
d: 0,
};
/// The vector used for calculating Fibonacci numbers.
const FIBONACCI_VEC: Vec2 = Vec2 { a: 1, b: 0 };
/// Calculates the Fibonacci number. (We assume the absence of integer overflow.)
///
/// Consult <https://web.media.mit.edu/~holbrow/post/calculating-fibonacci-numbers-with-matrices-and-linear-algebra/> for matrix computation of Fibonacci numbers.
pub fn fibonacci(n: u64) -> u64 {
(FIBONACCI_MAT.power(n) * FIBONACCI_VEC).get_upper()
}
/// Writes down the lyrics of "twelve days of christmas".
///
/// Hint: Google the song title for lyrics and look at the test code for the expected result.
pub fn twelve_days_of_christmas_lyrics() -> String {
todo!()
}

View File

@@ -0,0 +1,13 @@
//! Assignment 2: Mastering common programming concepts (1/2).
//!
//! The primary goal of this assignment is to re-learn the common programming concepts in Rust, especially those in the Rust Book chapters 3 and 5.
//! Please make sure you're comfortable with the concepts to proceed on to the next assignments.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-02.sh` works fine.
//! See `assigment02/*_grade.rs` and `/scripts/grade-02.sh` for the test script.
pub mod small_exercises;
mod small_exercises_grade;
pub mod vec_and_mat;
mod vec_and_mat_grade;

View File

@@ -0,0 +1,46 @@
//! Small problems.
const FAHRENHEIT_OFFSET: f64 = 32.0;
const FAHRENHEIT_SCALE: f64 = 5.0 / 9.0;
/// Converts Fahrenheit to Celsius temperature degree.
pub fn fahrenheit_to_celsius(degree: f64) -> f64 {
todo!()
}
/// Capitalizes English alphabets (leaving the other characters intact).
pub fn capitalize(input: String) -> String {
todo!()
}
/// Returns the sum of the given array. (We assume the absence of integer overflow.)
pub fn sum_array(input: &[u64]) -> u64 {
todo!()
}
/// Given a non-negative integer, say `n`, return the smallest integer of the form `3^m` that's greater than or equal to `n`.
///
/// For instance, up3(6) = 9, up3(9) = 9, up3(10) = 27. (We assume the absence of integer overflow.)
pub fn up3(n: u64) -> u64 {
todo!()
}
/// Returns the greatest common divisor (GCD) of two non-negative integers. (We assume the absence of integer overflow.)
pub fn gcd(lhs: u64, rhs: u64) -> u64 {
todo!()
}
/// Returns the array of nC0, nC1, nC2, ..., nCn, where nCk = n! / (k! * (n-k)!). (We assume the absence of integer overflow.)
///
/// Consult <https://en.wikipedia.org/wiki/Pascal%27s_triangle> for computation of binomial coefficients without integer overflow.
pub fn chooses(n: u64) -> Vec<u64> {
todo!()
}
/// Returns the "zip" of two vectors.
///
/// For instance, `zip(vec![1, 2, 3], vec![4, 5])` equals to `vec![(1, 4), (2, 5)]`.
/// Here, `3` is ignored because it doesn't have a partner.
pub fn zip(lhs: Vec<u64>, rhs: Vec<u64>) -> Vec<(u64, u64)> {
todo!()
}

View File

@@ -1,6 +1,6 @@
#[cfg(test)]
mod test {
use super::super::assignment02::*;
use crate::assignments::assignment02::small_exercises::*;
#[test]
fn test_fahrenheit() {
@@ -134,128 +134,4 @@ mod test {
assert_eq!(zip(vec![1, 2], vec![4, 5, 6]), vec![(1, 4), (2, 5)]);
assert_eq!(zip(vec![], vec![4, 5]), vec![]);
}
#[test]
fn test_fibonacci() {
assert_eq!(fibonacci(0), 1);
assert_eq!(fibonacci(1), 1);
assert_eq!(fibonacci(2), 2);
assert_eq!(fibonacci(3), 3);
assert_eq!(fibonacci(4), 5);
assert_eq!(fibonacci(5), 8);
assert_eq!(fibonacci(6), 13);
assert_eq!(fibonacci(7), 21);
assert_eq!(fibonacci(50), 20365011074);
assert_eq!(fibonacci(92), 12200160415121876738);
}
#[test]
fn test_lyrics() {
assert_eq!(twelve_days_of_christmas_lyrics(), LYRICS)
}
const LYRICS: &str = r#"On the first day of Christmas, my true love sent to me
A partridge in a pear tree
On the second day of Christmas, my true love sent to me
Two turtledoves
And a partridge in a pear tree
On the third day of Christmas, my true love sent to me
Three French hens
Two turtledoves
And a partridge in a pear tree
On the fourth day of Christmas, my true love sent to me
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the fifth day of Christmas, my true love sent to me
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the sixth day of Christmas, my true love sent to me
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the seventh day of Christmas, my true love sent to me
Seven swans a-swimming
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the eighth day of Christmas, my true love sent to me
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the ninth day of Christmas, my true love sent to me
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the tenth day of Christmas, my true love sent to me
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the eleventh day of Christmas, my true love sent to me
I sent eleven pipers piping
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
On the twelfth day of Christmas, my true love sent to me
Twelve drummers drumming
Eleven pipers piping
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five gold rings (five golden rings)
Four calling birds
Three French hens
Two turtledoves
And a partridge in a pear tree
And a partridge in a pear tree
"#;
}

View File

@@ -0,0 +1,124 @@
//! Vector and matrices.
//!
//! You will implement simple operations on vectors and matrices.
use std::ops::Mul;
/// 2x2 matrix of the following configuration:
///
/// a, b
/// c, d
#[derive(Debug, Clone, Copy)]
struct Mat2 {
a: u64,
b: u64,
c: u64,
d: u64,
}
/// 2x1 matrix of the following configuration:
///
/// a
/// b
#[derive(Debug, Clone, Copy)]
struct Vec2 {
a: u64,
b: u64,
}
impl Mat2 {
/// Creates an identity matrix.
fn new() -> Self {
Self {
a: 1,
b: 0,
c: 0,
d: 1,
}
}
}
impl Mul<Mat2> for Mat2 {
type Output = Mat2;
/// Consult <https://www.mathsisfun.com/algebra/matrix-multiplying.html>
fn mul(self, rhs: Mat2) -> Self::Output {
todo!()
}
}
impl Mul<Vec2> for Mat2 {
type Output = Vec2;
/// Multiplies the matrix by the vector.
///
/// Consult <https://www.mathsisfun.com/algebra/matrix-multiplying.html>
fn mul(self, rhs: Vec2) -> Self::Output {
todo!()
}
}
impl Mat2 {
/// Calculates the power of matrix.
fn power(self, power: u64) -> Mat2 {
todo!()
}
}
impl Vec2 {
/// Gets the upper value of vector.
fn get_upper(self) -> u64 {
todo!()
}
}
/// The matrix used for calculating Fibonacci numbers.
const FIBONACCI_MAT: Mat2 = Mat2 {
a: 1,
b: 1,
c: 1,
d: 0,
};
/// The vector used for calculating Fibonacci numbers.
const FIBONACCI_VEC: Vec2 = Vec2 { a: 1, b: 0 };
/// Calculates the Fibonacci number. (We assume the absence of integer overflow.)
///
/// Consult <https://web.media.mit.edu/~holbrow/post/calculating-fibonacci-numbers-with-matrices-and-linear-algebra/> for matrix computation of Fibonacci numbers.
pub fn fibonacci(n: u64) -> u64 {
(FIBONACCI_MAT.power(n) * FIBONACCI_VEC).get_upper()
}
/// 2x2 floating-point matrix of the following configuration:
///
/// a, b
/// c, d
#[derive(Debug, Clone, Copy)]
pub struct FMat2 {
/// row 1, column 1
pub a: f64,
/// row 1, column 2
pub b: f64,
/// row 2, column 1
pub c: f64,
/// row 2, column 2
pub d: f64,
}
impl FMat2 {
/// Returns the inverse of the given matrix. (We assume the given matrix is always invertible.)
/// HINT: <https://www.mathcentre.ac.uk/resources/uploaded/sigma-matrices7-2009-1.pdf>
///
/// # Example
///
/// ```
/// assert_eq!(
/// Mat2 { a: 1.0, b: 1.0, c: 2.0, d: 3.0 }.inverse(),
/// Mat2 { a: 3.0, b: -1.0, c: -2.0, d: 1.0}
/// );
/// ```
pub fn inverse(self) -> Self {
todo!()
}
}

View File

@@ -0,0 +1,60 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment02::vec_and_mat::*;
#[test]
fn test_fibonacci() {
assert_eq!(fibonacci(0), 1);
assert_eq!(fibonacci(1), 1);
assert_eq!(fibonacci(2), 2);
assert_eq!(fibonacci(3), 3);
assert_eq!(fibonacci(4), 5);
assert_eq!(fibonacci(5), 8);
assert_eq!(fibonacci(6), 13);
assert_eq!(fibonacci(7), 21);
assert_eq!(fibonacci(50), 20365011074);
assert_eq!(fibonacci(92), 12200160415121876738);
}
// Equivalence between two floating-point matrices, as element-wise equivalence
use std::cmp::PartialEq;
impl PartialEq for FMat2 {
fn eq(&self, other: &FMat2) -> bool {
self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d
}
}
#[test]
fn test_inverse() {
assert_eq!(
FMat2 {
a: 1.0,
b: 1.0,
c: 2.0,
d: 3.0
}
.inverse(),
FMat2 {
a: 3.0,
b: -1.0,
c: -2.0,
d: 1.0
}
);
assert_eq!(
FMat2 {
a: 2.0,
b: 3.0,
c: 5.0,
d: 7.0
}
.inverse(),
FMat2 {
a: -7.0,
b: 3.0,
c: 5.0,
d: -2.0
}
);
}
}

View File

@@ -0,0 +1,79 @@
//! You will implement a number of custom operators.
/// Custom option type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MyOption<T> {
/// Some value of type `T`.
MySome(T),
/// No value.
MyNone,
}
/// Maps an `MyOption<T>` to `MyOption<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Converts an `MyOption<String>` into an `MyOption<usize>`, consuming the original:
///
/// ```
/// use cs220::assignments::assignment03::{my_map, MyOption};
///
/// fn len(s: String) -> usize {
/// s.len()
/// }
///
/// assert_eq!(my_map(MyOption::MySome(String::from("Hello, World!")), len), MyOption::MySome(13));
/// assert_eq!(my_map(MyOption::MyNone, len), MyOption::MyNone);
/// ```
pub fn my_map<T, U, F: FnOnce(T) -> U>(v: MyOption<T>, f: F) -> MyOption<U> {
todo!()
}
/// Returns `MyNone` if the option is `MyNone`, otherwise calls `f` with the wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// use cs220::assignments::assignment03::{MyOption, my_and_then};
///
/// fn pos_then_to_string(x: isize) -> MyOption<String> {
/// if x > 0 {
/// MyOption::MySome(x.to_string())
/// } else {
/// MyOption::MyNone
/// }
/// }
///
/// assert_eq!(my_and_then(MyOption::MySome(2), pos_then_to_string), MyOption::MySome(2.to_string()));
/// assert_eq!(my_and_then(MyOption::MySome(-3), pos_then_to_string), MyOption::MyNone);
/// assert_eq!(my_and_then(MyOption::MyNone, pos_then_to_string), MyOption::MyNone);
/// ```
pub fn my_and_then<T, U, F: FnOnce(T) -> MyOption<U>>(v: MyOption<T>, f: F) -> MyOption<U> {
todo!()
}
/// Custom operator: `option_op_or(v1, v2, f)`
/// If neither `v1` nor `v2` is `Some`, returns `None`.
/// If exactly one is `Some`, returns the same `Some` value.
/// If both are `Some`, apply the values inside `Some` to `f` and wrap the resulting value inside `Some`.
///
/// # Examples
///
/// ```
/// fn product(a: i32, b: i32) -> i32 {
/// a * b
/// }
///
/// assert_eq!(option_op_or(None, None, product), None);
/// assert_eq!(option_op_or(Some(3), None, product), Some(3));
/// assert_eq!(option_op_or(Some(3), Some(5), product), Some(15));
/// ```
pub fn my_option_op_or<T, F: FnOnce(T, T) -> T>(
v1: MyOption<T>,
v2: MyOption<T>,
f: F,
) -> MyOption<T> {
todo!()
}

View File

@@ -0,0 +1,58 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment03::custom_operators::{MyOption::*, *};
#[test]
fn test_my_map() {
fn len(s: &str) -> usize {
s.len()
}
fn plus_one(x: isize) -> isize {
x + 1
}
fn is_positive(x: f64) -> bool {
x > 0.0f64
}
assert_eq!(my_map(MySome("Hello, World!"), len), MySome(13));
assert_eq!(my_map(MyNone, len), MyNone);
assert_eq!(my_map(MySome(1), plus_one), MySome(2));
assert_eq!(my_map(MyNone, plus_one), MyNone);
assert_eq!(my_map(MySome(5.0f64), is_positive), MySome(true));
assert_eq!(my_map(MySome(-3.0f64), is_positive), MySome(false));
assert_eq!(my_map(MyNone::<f64>, is_positive), MyNone);
}
#[test]
fn test_my_and_then() {
fn plus_one(x: isize) -> MyOption<isize> {
MySome(x + 1)
}
fn none(_: isize) -> MyOption<isize> {
MyNone
}
assert_eq!(my_and_then(MySome(1), plus_one), MySome(2));
assert_eq!(my_and_then(MySome(1), none), MyNone);
assert_eq!(my_and_then(MyNone, plus_one), MyNone);
assert_eq!(my_and_then(MyNone, none), MyNone);
}
fn product(a: i32, b: i32) -> i32 {
a * b
}
#[test]
fn test_my_option_op_or() {
assert_eq!(my_option_op_or(MyNone, MyNone, product), MyNone);
assert_eq!(my_option_op_or(MySome(3), MyNone, product), MySome(3));
assert_eq!(my_option_op_or(MyNone, MySome(5), product), MySome(5));
assert_eq!(my_option_op_or(MySome(3), MySome(5), product), MySome(15));
}
}

View File

@@ -0,0 +1,13 @@
//! Assignment 3: Mastering common programming concepts (2/2)
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-03.sh` works fine.
//! See `assignment03/*_grade.rs` and `/scripts/grade-03.sh` for the test script.
pub mod small_exercises;
mod small_exercises_grade;
pub mod parse_shell;
mod parse_shell_grade;
pub mod custom_operators;
mod custom_operators_grade;

View File

@@ -0,0 +1,28 @@
//! Parsing a shell command.
//!
//! Shell commands are text-based instructions that you can enter in a command-line interface (CLI)
//! to interact with operating systems (e.g. Linux) and others.
//! For example, you can use the `ls` command to list files in a directory.
//!
//! You will parse a given string consists of a small number of shell commands.
/// Parse the string as a shell command.
///
/// Usually, a shell command is whitespace-separated array of strings.
/// ```text
/// cat file --> ["cat", "file"]
/// ```
/// But sometimes, you may want to include whitespaces in each argument.
/// In that case, you can use quotes.
/// ```text
/// ls 'VirtualBox VMs' --> ["ls", 'VirtualBox VMs']
/// ls VirtualBox' 'VMs --> ["ls", 'VirtualBox VMs']
/// ```
///
/// For simplicity, you may assume that the string only contains alphanumeric characters, spaces
/// (" "), and single quotes ("'").
///
/// See `test_shell` for more examples.
pub fn parse_shell_command(command: &str) -> Vec<String> {
todo!()
}

View File

@@ -0,0 +1,38 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment03::parse_shell::*;
#[test]
fn test_shell() {
assert_eq!(
parse_shell_command("cat file"),
vec!["cat".to_string(), "file".to_string()]
);
assert_eq!(
parse_shell_command("ls 'VirtualBox VMs'"),
vec!["ls".to_string(), "VirtualBox VMs".to_string()]
);
assert_eq!(
parse_shell_command("ls VirtualBox' 'VMs"),
vec!["ls".to_string(), "VirtualBox VMs".to_string()]
);
assert_eq!(
parse_shell_command("echo once upon a midnight dreary"),
vec![
"echo".to_string(),
"once".to_string(),
"upon".to_string(),
"a".to_string(),
"midnight".to_string(),
"dreary".to_string(),
]
);
assert_eq!(
parse_shell_command("echo 'once upon a midnight dreary'"),
vec![
"echo".to_string(),
"once upon a midnight dreary".to_string(),
]
);
}
}

View File

@@ -1,12 +1,7 @@
//! Assignment 3: Mastering common programming concepts (2/2).
//!
//! The primary goal of this assignment is to re-learn the common programming concepts in Rust, especially those in the Rust Book chapters 6, 7, 8, and 9.
//! Please make sure you're comfortable with the concepts to proceed on to the next assignments.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-03.sh` works fine.
//! See `assignment03_grade.rs` and `/scripts/grade-03.sh` for the test script.
//! Small problems.
use std::collections::{HashMap, HashSet};
use std::fmt;
/// Day of week.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -34,60 +29,6 @@ pub fn next_weekday(day: DayOfWeek) -> DayOfWeek {
todo!()
}
/// Custom option type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MyOption<T> {
/// Some value of type `T`.
MySome(T),
/// No value.
MyNone,
}
/// Maps an `MyOption<T>` to `MyOption<U>` by applying a function to a contained value.
///
/// # Examples
///
/// Converts an `MyOption<String>` into an `MyOption<usize>`, consuming the original:
///
/// ```
/// use cs220::assignments::assignment03::{my_map, MyOption};
///
/// fn len(s: String) -> usize {
/// s.len()
/// }
///
/// assert_eq!(my_map(MyOption::MySome(String::from("Hello, World!")), len), MyOption::MySome(13));
/// assert_eq!(my_map(MyOption::MyNone, len), MyOption::MyNone);
/// ```
pub fn my_map<T, U, F: FnOnce(T) -> U>(v: MyOption<T>, f: F) -> MyOption<U> {
todo!()
}
/// Returns `MyNone` if the option is `MyNone`, otherwise calls `f` with the wrapped value and returns the result.
///
/// Some languages call this operation flatmap.
///
/// # Examples
///
/// ```
/// use cs220::assignments::assignment03::{MyOption, my_and_then};
///
/// fn pos_then_to_string(x: isize) -> MyOption<String> {
/// if x > 0 {
/// MyOption::MySome(x.to_string())
/// } else {
/// MyOption::MyNone
/// }
/// }
///
/// assert_eq!(my_and_then(MyOption::MySome(2), pos_then_to_string), MyOption::MySome(2.to_string()));
/// assert_eq!(my_and_then(MyOption::MySome(-3), pos_then_to_string), MyOption::MyNone);
/// assert_eq!(my_and_then(MyOption::MyNone, pos_then_to_string), MyOption::MyNone);
/// ```
pub fn my_and_then<T, U, F: FnOnce(T) -> MyOption<U>>(v: MyOption<T>, f: F) -> MyOption<U> {
todo!()
}
/// Given a list of integers, returns its median (when sorted, the value in the middle position).
///
/// For a data set `x` of `n` elements, the median can be defined as follows:
@@ -166,3 +107,24 @@ pub fn piglatin(input: String) -> String {
pub fn organize(commands: Vec<String>) -> HashMap<String, HashSet<String>> {
todo!()
}
/// Events in a text editor.
#[derive(Debug)]
pub enum TypeEvent {
/// A character is typed.
Type(char),
/// The last character is removed.
Backspace,
/// The whole string is copied to the clipboard.
Copy,
/// The string in the clipboard is appended.
Paste,
}
/// Starting from an empty string and an empty clipboard,
/// processes the given `events` in order and returns the resulting string.
///
/// See the test function `test_editor` for examples.
pub fn use_editor(events: Vec<TypeEvent>) -> String {
todo!()
}

View File

@@ -1,6 +1,6 @@
#[cfg(test)]
mod test {
use super::super::assignment03::*;
use crate::assignments::assignment03::small_exercises::*;
#[test]
fn test_next_weekday() {
@@ -13,52 +13,6 @@ mod test {
assert_eq!(next_weekday(DayOfWeek::Sat), DayOfWeek::Mon);
}
#[test]
fn test_my_map() {
use MyOption::*;
fn len(s: &str) -> usize {
s.len()
}
fn plus_one(x: isize) -> isize {
x + 1
}
fn is_positive(x: f64) -> bool {
x > 0.0f64
}
assert_eq!(my_map(MySome("Hello, World!"), len), MySome(13));
assert_eq!(my_map(MyNone, len), MyNone);
assert_eq!(my_map(MySome(1), plus_one), MySome(2));
assert_eq!(my_map(MyNone, plus_one), MyNone);
assert_eq!(my_map(MySome(5.0f64), is_positive), MySome(true));
assert_eq!(my_map(MySome(-3.0f64), is_positive), MySome(false));
assert_eq!(my_map(MyNone::<f64>, is_positive), MyNone);
}
#[test]
fn test_my_and_then() {
use MyOption::*;
fn plus_one(x: isize) -> MyOption<isize> {
MySome(x + 1)
}
fn none(_: isize) -> MyOption<isize> {
MyNone
}
assert_eq!(my_and_then(MySome(1), plus_one), MySome(2));
assert_eq!(my_and_then(MySome(1), none), MyNone);
assert_eq!(my_and_then(MyNone, plus_one), MyNone);
assert_eq!(my_and_then(MyNone, none), MyNone);
}
#[test]
fn test_median() {
assert_eq!(median(vec![]), None);
@@ -165,4 +119,61 @@ mod test {
.into()
);
}
#[test]
fn test_editor() {
assert_eq!(
use_editor(vec![
TypeEvent::Type('a'),
TypeEvent::Backspace,
TypeEvent::Backspace,
TypeEvent::Type('b'),
TypeEvent::Type('c')
]),
"bc"
);
assert_eq!(
use_editor(vec![
TypeEvent::Type('a'),
TypeEvent::Copy,
TypeEvent::Paste,
TypeEvent::Paste,
TypeEvent::Type('b'),
TypeEvent::Copy,
TypeEvent::Paste
]),
"aaabaaab"
);
assert_eq!(
use_editor(vec![
TypeEvent::Paste, // clipboard starts empty
TypeEvent::Type('a'),
TypeEvent::Type('n'),
TypeEvent::Copy,
TypeEvent::Backspace,
TypeEvent::Backspace,
TypeEvent::Type('b'),
TypeEvent::Paste,
TypeEvent::Paste,
TypeEvent::Paste,
TypeEvent::Backspace
]),
"banana"
);
assert_eq!(
use_editor(vec![
TypeEvent::Copy,
TypeEvent::Backspace,
TypeEvent::Backspace,
TypeEvent::Paste,
TypeEvent::Paste,
TypeEvent::Copy,
TypeEvent::Backspace
]),
""
);
}
}

View File

@@ -1,8 +1,7 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment04::syntax::*;
use super::super::assignment04::*;
use crate::assignments::assignment04::*;
#[test]
fn test_parse() {

View File

@@ -12,9 +12,10 @@
//! For calculator, just reading `syntax.rs` would suffice for you to understand what to do.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-04.sh` works fine.
//! See `assignment04_grade.rs` and `/scripts/grade-04.sh` for the test script.
//! See `assignment04/grade.rs` and `/scripts/grade-04.sh` for the test script.
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment04.zip` to <https://gg.kaist.ac.kr>.
pub mod context;
mod grade;
pub mod parser;
pub mod syntax;

View File

@@ -0,0 +1,12 @@
//! Assignment 6: Mastering advanced types (1/2).
//!
//! The primary goal of this assignment is to understand generics, traits, and lifetimes.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-06.sh` works fine.
//! See `assignment06/*_grade.rs` and `/scripts/grade-06.sh` for the test script.
pub mod semiring;
pub mod symbolic_differentiation;
mod semiring_grade;
mod symbolic_differentiation_grade;

View File

@@ -1,9 +1,4 @@
//! Assignment 6: Mastering advanced types (1/2).
//!
//! The primary goal of this assignment is to understand generics, traits, and lifetimes.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-06.sh` works fine.
//! See `assignment06_grade.rs` and `/scripts/grade-06.sh` for the test script.
//! Semiring
use std::{collections::HashMap, fmt::Debug};
@@ -123,12 +118,6 @@ impl<C: Semiring> Semiring for Polynomial<C> {
}
}
impl<C: Semiring> From<C> for Polynomial<C> {
fn from(value: C) -> Self {
todo!()
}
}
impl<C: Semiring> Polynomial<C> {
/// Constructs polynomial `x`.
pub fn x() -> Self {
@@ -139,4 +128,41 @@ impl<C: Semiring> Polynomial<C> {
pub fn eval(&self, value: C) -> C {
todo!()
}
/// Constructs polynomial `ax^n`.
pub fn term(a: C, n: u64) -> Self {
todo!()
}
}
impl<C: Semiring> From<C> for Polynomial<C> {
fn from(value: C) -> Self {
todo!()
}
}
/// Given a string `s`, parse it into a `Polynomial<C>`.
/// You may assume that `s` follows the criteria below.
/// Therefore, you do not have to return `Err`.
///
/// Assumptions:
/// - Each term is separated by ` + `.
/// - Each term is one of the following form:
/// `a`, `x`, `ax`, `x^n`, and `ax^n`,
/// where `a` is a `usize` number and `n` is a `u64` number.
/// This `a` should then be converted to a `C` type.
/// - In `a`, it is guaranteed that `a >= 1`.
/// - In `ax` and `ax^n`, it is guaranteed that `a >= 2`.
/// - In `x^n` and `ax^n`, it is guaranteed that `n >= 2`.
/// - All terms have unique degrees.
///
/// Consult `assignment06/grade.rs` for example valid strings.
///
/// Hint: `.split`, `.parse`, and `Polynomial::term`
impl<C: Semiring> std::str::FromStr for Polynomial<C> {
type Err = (); // Ignore this for now...
fn from_str(s: &str) -> Result<Self, Self::Err> {
todo!()
}
}

View File

@@ -1,6 +1,14 @@
#[cfg(test)]
mod test {
use super::super::assignment06::*;
use crate::assignments::assignment06::semiring::*;
use ntest::assert_about_eq;
fn test_from_str(s: &str, f: impl Fn(i64) -> i64) {
let poly = s.parse::<Polynomial<i64>>().unwrap();
for i in 0..10 {
assert_eq!(poly.eval(i), f(i));
}
}
fn test_polynomial<T: Semiring>() {
// x^2 + 5x + 6
@@ -21,6 +29,43 @@ mod test {
assert_eq!(value, from_usize(13 * 13 + 5 * 13 + 6));
}
#[test]
fn test_123() {
test_from_str("123", |x| 123);
}
#[test]
fn test_x() {
test_from_str("x", |x| x);
}
#[test]
fn test_24x() {
test_from_str("24x", |x| 24 * x);
}
#[test]
fn test_2x_3() {
test_from_str("2x + 3", |x| 2 * x + 3);
}
#[test]
fn test_x3() {
test_from_str("x^3", |x| x * x * x);
}
#[test]
fn test_2x3_3x2_5x_12() {
test_from_str("2x^3 + 3x^2 + 5x + 12", |x| {
2 * x * x * x + 3 * x * x + 5 * x + 12
});
}
#[test]
fn test_x5_1() {
test_from_str("x^5 + 1", |x| x * x * x * x * x + 1);
}
#[test]
fn test_polynomial_u64() {
test_polynomial::<u64>();

View File

@@ -0,0 +1,365 @@
//! Symbolic differentiation with rational coefficents.
use std::fmt;
use std::ops::*;
/// Rational number represented by two isize, numerator and denominator.
///
/// Each Rational number should be normalized so that `demoninator` is nonnegative and `numerator` and `demoninator` are coprime.
/// See [`normalize`] for examples. As a corner case, 0 is represented by Rational { numerator: 0, demoninator: 0 }.
///
/// For "natural use", Rational also overloads standard arithmetic operations, i.e, `+`, `-`, `*`, `/`.
///
/// See [here](https://doc.rust-lang.org/core/ops/index.html) for details.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rational {
numerator: isize,
denominator: isize,
}
// Some useful constants.
/// Zero
pub const ZERO: Rational = Rational::new(0, 0);
/// One
pub const ONE: Rational = Rational::new(1, 1);
/// Minus one
pub const MINUS_ONE: Rational = Rational::new(-1, 1);
impl Rational {
/// Creates a new rational number.
pub const fn new(numerator: isize, denominator: isize) -> Self {
Self {
numerator,
denominator,
}
}
}
impl Add for Rational {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
todo!()
}
}
impl Mul for Rational {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
todo!()
}
}
impl Sub for Rational {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
todo!()
}
}
impl Div for Rational {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
todo!()
}
}
/// Differentiable functions.
///
/// For simplicity, we only consider infinitely differentiable functions.
pub trait Differentiable: Clone {
/// Differentiate.
///
/// Since the return type is `Self`, this trait can only be implemented
/// for types that are closed under differentiation.
fn diff(&self) -> Self;
}
impl Differentiable for Rational {
/// HINT: Consult <https://en.wikipedia.org/wiki/Differentiation_rules#Constant_term_rule>
fn diff(&self) -> Self {
todo!()
}
}
/// Singleton polynomial.
///
/// Unlike regular polynomials, this type only represents a single term.
/// The `Const` variant is included to make `Polynomial` closed under differentiation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SingletonPolynomial {
/// Constant polynomial.
Const(Rational),
/// Non-const polynomial.
Polynomial {
/// coefficent of polynomial. Must be non-zero.
coeff: Rational,
/// power of polynomial. Must be non-zero.
power: Rational,
},
}
impl SingletonPolynomial {
/// Creates a new const polynomial.
pub fn new_c(r: Rational) -> Self {
todo!()
}
/// Creates a new polynomial.
pub fn new_poly(coeff: Rational, power: Rational) -> Self {
todo!()
}
}
impl Differentiable for SingletonPolynomial {
/// HINT: Consult <https://en.wikipedia.org/wiki/Power_rule>
fn diff(&self) -> Self {
todo!()
}
}
/// Expoential function.(`e^x`)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Exp;
impl Exp {
/// Creates a new exponential function.
pub fn new() -> Self {
todo!()
}
}
impl Default for Exp {
fn default() -> Self {
Self::new()
}
}
impl Differentiable for Exp {
/// HINT: Consult <https://en.wikipedia.org/wiki/Differentiation_rules#Derivatives_of_exponential_and_logarithmic_functions>
fn diff(&self) -> Self {
todo!()
}
}
/// Trigonometric functions.
///
/// The trig fucntions carry their coefficents to be closed under differntiation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Trignometric {
/// Sine function.
Sine {
/// Coefficent
coeff: Rational,
},
/// Sine function.
Cosine {
/// Coefficent
coeff: Rational,
},
}
impl Trignometric {
/// Creates a new sine function.
pub fn new_sine(coeff: Rational) -> Self {
todo!()
}
/// Creates a new cosine function.
pub fn new_cosine(coeff: Rational) -> Self {
todo!()
}
}
impl Differentiable for Trignometric {
/// HINT: Consult <https://en.wikipedia.org/wiki/Differentiation_rules#Derivatives_of_trigonometric_functions>
fn diff(&self) -> Self {
todo!()
}
}
/// Basic functions
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BaseFuncs {
/// Constant
Const(Rational),
/// Polynomial
Poly(SingletonPolynomial),
/// Exponential
Exp(Exp),
/// Trignometirc
Trig(Trignometric),
}
impl Differentiable for BaseFuncs {
fn diff(&self) -> Self {
todo!()
}
}
/// Complex functions.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ComplexFuncs<F> {
/// Basic functions
Func(F),
/// Addition
Add(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Subtraction
Sub(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Multipliciation
Mul(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Division
Div(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Composition
Comp(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
}
impl<F: Differentiable> Differentiable for Box<F> {
fn diff(&self) -> Self {
todo!()
}
}
impl<F: Differentiable> Differentiable for ComplexFuncs<F> {
/// HINT: Consult <https://en.wikipedia.org/wiki/Differentiation_rules#Elementary_rules_of_differentiation>
fn diff(&self) -> Self {
todo!()
}
}
/// Evaluate functions.
pub trait Evaluate {
/// Evaluate `self` at `x`.
fn evaluate(&self, x: f64) -> f64;
}
impl Evaluate for Rational {
fn evaluate(&self, x: f64) -> f64 {
todo!()
}
}
impl Evaluate for SingletonPolynomial {
fn evaluate(&self, x: f64) -> f64 {
todo!()
}
}
impl Evaluate for Exp {
fn evaluate(&self, x: f64) -> f64 {
todo!()
}
}
impl Evaluate for Trignometric {
fn evaluate(&self, x: f64) -> f64 {
todo!()
}
}
impl Evaluate for BaseFuncs {
fn evaluate(&self, x: f64) -> f64 {
todo!()
}
}
impl<F: Evaluate> Evaluate for ComplexFuncs<F> {
fn evaluate(&self, x: f64) -> f64 {
todo!()
}
}
impl fmt::Display for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if *self == ZERO {
return write!(f, "0");
} else if self.denominator == 1 {
return write!(f, "{}", self.numerator);
}
write!(f, "{}/{}", self.numerator, self.denominator)
}
}
impl fmt::Display for SingletonPolynomial {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Const(r) => write!(f, "{r}"),
Self::Polynomial { coeff, power } => {
// coeff or power is zero
if *coeff == ZERO {
return write!(f, "0");
} else if *power == ZERO {
return write!(f, "{coeff}");
}
// Standard form of px^q
let coeff = if *coeff == ONE {
"".to_string()
} else if *coeff == MINUS_ONE {
"-".to_string()
} else {
format!("({coeff})")
};
let var = if *power == ONE {
"x".to_string()
} else {
format!("x^({power})")
};
write!(f, "{coeff}{var}")
}
}
}
}
impl fmt::Display for Exp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "exp(x)")
}
}
impl fmt::Display for Trignometric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (func, coeff) = match self {
Trignometric::Sine { coeff } => ("sin(x)", coeff),
Trignometric::Cosine { coeff } => ("cos(x)", coeff),
};
if *coeff == ZERO {
write!(f, "0")
} else if *coeff == ONE {
write!(f, "{func}")
} else if *coeff == MINUS_ONE {
write!(f, "-{func}")
} else {
write!(f, "({coeff}){func}")
}
}
}
impl fmt::Display for BaseFuncs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Const(r) => write!(f, "{r}"),
Self::Poly(p) => write!(f, "{p}"),
Self::Exp(e) => write!(f, "{e}"),
Self::Trig(t) => write!(f, "{t}"),
}
}
}
impl<F: Differentiable + fmt::Display> fmt::Display for ComplexFuncs<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ComplexFuncs::Func(func) => write!(f, "{func}"),
ComplexFuncs::Add(l, r) => write!(f, "({l} + {r})"),
ComplexFuncs::Sub(l, r) => write!(f, "({l} - {r})"),
ComplexFuncs::Mul(l, r) => write!(f, "({l} * {r})"),
ComplexFuncs::Div(l, r) => write!(f, "({l} / {r})"),
ComplexFuncs::Comp(l, r) => write!(f, "({l} ∘ {r})"),
}
}
}

View File

@@ -0,0 +1,216 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment06::symbolic_differentiation::*;
use ntest::assert_about_eq;
// Constant rationals to use
const TWO: Rational = Rational::new(2, 1);
const FOUR: Rational = Rational::new(4, 1);
const THIRD: Rational = Rational::new(1, 3);
const FIVE_THIRD: Rational = Rational::new(5, 3);
const TWO_SEVENTH: Rational = Rational::new(2, 7);
#[test]
fn test_rational_simpl() {
assert_eq!(format!("{}", Rational::new(1, 2)), "1/2".to_string());
assert_eq!(format!("{}", Rational::new(0, 0)), "0".to_string());
assert_eq!(format!("{}", Rational::new(1, 1)), "1".to_string());
assert_eq!(format!("{}", Rational::new(-3, 7)), "-3/7".to_string());
}
#[test]
fn test_rational_arithmetic() {
assert_eq!(
format!("{}", Rational::new(1, 4) + Rational::new(3, 4)),
"1".to_string()
);
assert_eq!(
format!("{}", Rational::new(1, 3) + Rational::new(1, 6)),
"1/2".to_string()
);
assert_eq!(
format!("{}", Rational::new(1, 5) - Rational::new(1, 2)),
"-3/10".to_string()
);
assert_eq!(
format!("{}", Rational::new(-5, 12) * Rational::new(6, 125)),
"-1/50".to_string()
);
assert_eq!(
format!("{}", Rational::new(-3, 4) / Rational::new(-7, 13)),
"39/28".to_string()
);
}
#[test]
fn test_rational_arithmetic_long() {
assert_eq!(
format!(
"{}",
Rational::new(1, 2) + Rational::new(1, 2) + Rational::new(1, 2)
),
"3/2".to_string()
);
assert_eq!(
format!(
"{}",
Rational::new(1, 2) - Rational::new(1, 4) + Rational::new(1, 5)
),
"9/20".to_string()
);
assert_eq!(
format!(
"{}",
Rational::new(1, 2)
* Rational::new(1, 4)
* Rational::new(1, 8)
* Rational::new(1, 16)
/ Rational::new(1, 1024)
),
"1".to_string()
);
assert_eq!(
format!(
"{}",
Rational::new(123, 798)
+ Rational::new(684, 32) / (Rational::new(13, 44) - Rational::new(123, 4472))
* Rational::new(1237, 2)
),
"12356494070/250439".to_string()
);
}
#[test]
fn test_differentiate_simple() {
// Constant
assert_eq!(format!("{}", Rational::new(3, 2).diff()), "0".to_string());
// Polynomials
assert_eq!(
format!("{}", SingletonPolynomial::new_c(Rational::new(3, 1)).diff()),
"0".to_string()
);
assert_eq!(
format!("{}", SingletonPolynomial::new_poly(TWO, FOUR).diff()),
"(8)x^(3)".to_string()
);
assert_eq!(
format!(
"{}",
SingletonPolynomial::new_poly(FIVE_THIRD, THIRD).diff()
),
"(5/9)x^(-2/3)".to_string()
);
// Exponential
assert_eq!(format!("{}", Exp::new().diff()), "exp(x)".to_string());
// Trigonometric
assert_eq!(
format!("{}", Trignometric::new_sine(ONE).diff()),
"cos(x)".to_string()
);
assert_eq!(
format!("{}", Trignometric::new_cosine(ONE).diff()),
"-sin(x)".to_string()
);
assert_eq!(
format!("{}", Trignometric::new_sine(FIVE_THIRD).diff()),
"(5/3)cos(x)".to_string()
);
assert_eq!(
format!("{}", Trignometric::new_cosine(TWO_SEVENTH).diff()),
"(-2/7)sin(x)".to_string()
);
}
#[test]
fn test_differentiate_complex() {
type BF = BaseFuncs;
type CF = ComplexFuncs<BF>;
// Unlike the above simple test, it is hard to state a canonical
// form for derivative of more complex functions. Thus, we only test that the
// derivative is correct at certain points.
// Add
//
// d/dx (2x^4 + exp(x)) = 8x^3 + exp(x)
let f1 = SingletonPolynomial::new_poly(TWO, FOUR);
let f2 = Exp::new();
let deriv = CF::Add(
Box::new(CF::Func(BF::Poly(f1))),
Box::new(CF::Func(BF::Exp(f2))),
)
.diff();
assert_about_eq!(deriv.evaluate(2.2), 94.2090134994f64);
assert_about_eq!(deriv.evaluate(4.5), 819.017131301);
// Sub
//
// d/dx ((5/3)cos(x) - sin(x)) = (-5/3)sin(x) - cos(x)
let f1 = Trignometric::new_cosine(FIVE_THIRD);
let f2 = Trignometric::new_sine(ONE);
let deriv = CF::Sub(
Box::new(CF::Func(BF::Trig(f1))),
Box::new(CF::Func(BF::Trig(f2))),
)
.diff();
assert_about_eq!(deriv.evaluate(2.7), 0.191772341627);
assert_about_eq!(deriv.evaluate(0.01), -1.01661638931);
// Mult
//
// d/dx (2x^4 * cos(x) * exp(x)) =
// 8x^2 * cos(x) * exp(x) - 2x^4 * sin(x) * exp(x) + 2x^4 * cos(x) * exp(x)
let f1 = SingletonPolynomial::new_poly(TWO, FOUR);
let f2 = Trignometric::new_cosine(ONE);
let f3 = Exp::new();
let deriv = CF::Mul(
Box::new(CF::Func(BF::Poly(f1))),
Box::new(CF::Mul(
Box::new(CF::Func(BF::Trig(f2))),
Box::new(CF::Func(BF::Exp(f3))),
)),
)
.diff();
assert_about_eq!(deriv.evaluate(3.4), -14804.9016757);
assert_about_eq!(deriv.evaluate(0.07), 0.00298352866);
// Div
//
// (d/dx) (sin(x)/cos(x)) = (cos(x)*cos(x) + sin(x)*sin(x)) / cos(x)*cos(x)
let f1 = Trignometric::new_sine(ONE);
let f2 = Trignometric::new_cosine(ONE);
let deriv = CF::Div(
Box::new(CF::Func(BF::Trig(f1))),
Box::new(CF::Func(BF::Trig(f2))),
)
.diff();
assert_about_eq!(deriv.evaluate(core::f64::consts::PI), 1f64);
assert_about_eq!(deriv.evaluate(0f64), 1f64);
// Comp
//
// d/dx (cos(x^2)) = -2x * sin(x^2)
let f1 = Trignometric::new_cosine(ONE);
let f2 = SingletonPolynomial::new_poly(ONE, TWO);
let deriv = CF::Comp(
Box::new(CF::Func(BF::Trig(f1))),
Box::new(CF::Func(BF::Poly(f2))),
)
.diff();
assert_about_eq!(deriv.evaluate(2.714), -4.79392977);
assert_about_eq!(deriv.evaluate(3.9), -3.72556973);
}
}

View File

@@ -1,29 +0,0 @@
//! Assignment 7: Mastering advanced types (2/2).
//!
//! The primary goal of this assignment is to understand generics, traits, and lifetimes.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-07.sh` works fine.
//! See `assignment07_grade.rs` and `/scripts/grade-07.sh` for the test script.
struct FindIter<'s, T: Eq> {
query: &'s [T],
base: &'s [T],
curr: usize,
}
impl<T: Eq> Iterator for FindIter<'_, T> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns an iterator over substring query indexes in the base.
pub fn find<'s, T: Eq>(query: &'s [T], base: &'s [T]) -> impl 's + Iterator<Item = usize> {
FindIter {
query,
base,
curr: 0,
}
}

View File

@@ -0,0 +1,43 @@
//! Generators
//!
//! HINT: Look at the `generator_grade.rs` file to see how the generator is used.
/// Yielded value. It can be either a value or a stop signal.
enum Yielded<T> {
Value(T),
Stop,
}
/// Generator
/// - You can call `next()` method to get the next value.
/// - The generator should stop when it yields `Yielded::Stop`.
///
/// Reference:
/// - [Python generator](https://python-reference.readthedocs.io/en/latest/docs/generator/)
#[allow(missing_debug_implementations)]
pub struct Generator<T, S> {
state: S,
f: fn(&mut S) -> Yielded<T>,
}
impl<T, S> Iterator for Generator<T, S> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns a generator that yields fibonacci numbers.
///
/// HINT: Consult <https://en.wikipedia.org/wiki/Fibonacci_sequence>
pub fn fib_generator(first: usize, second: usize) -> Generator<usize, (usize, usize)> {
todo!()
}
/// Returns a generator that yields collatz numbers.
///
/// HINT: Consult <https://en.wikipedia.org/wiki/Collatz_conjecture>
pub fn collatz_conjecture(start: usize) -> Generator<usize, usize> {
todo!()
}

View File

@@ -0,0 +1,38 @@
#[cfg(test)]
mod test {
use itertools::Itertools;
use ntest::assert_about_eq;
use crate::assignments::assignment07::generator::*;
#[test]
fn test_generator() {
assert_eq!(
fib_generator(0, 1).take(10).collect::<Vec<_>>(),
vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
);
assert_eq!(
collatz_conjecture(12).collect::<Vec<_>>(),
vec![12, 6, 3, 10, 5, 16, 8, 4, 2, 1]
);
assert_eq!(
collatz_conjecture(19).collect::<Vec<_>>(),
vec![19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
);
assert_eq!(
collatz_conjecture(27).collect::<Vec<_>>(),
vec![
27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364,
182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790,
395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566,
283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858,
2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308,
1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23,
70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1
]
);
}
}

View File

@@ -0,0 +1,16 @@
//! Assignment 7: Mastering advanced types (2/2).
//!
//! The primary goal of this assignment is to understand generics, traits, and lifetimes.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-07.sh` works fine.
//! See `assignment07/*_grade.rs` and `/scripts/grade-07.sh` for the test script.
pub mod generator;
pub mod my_itertools;
pub mod small_exercises;
pub mod transform;
mod generator_grade;
mod my_itertools_grade;
mod small_exercises_grade;
mod transform_grade;

View File

@@ -0,0 +1,117 @@
//! Implement your own minimal `itertools` crate.
use std::hash::Hash;
/// Iterator that iterates over the given iterator and returns only unique elements.
#[allow(missing_debug_implementations)]
pub struct Unique<I: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<I>,
}
impl<I: Iterator> Iterator for Unique<I>
where
I::Item: Eq + Hash + Clone,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Iterator that chains two iterators together.
#[allow(missing_debug_implementations)]
pub struct Chain<I1: Iterator, I2: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<(I1, I2)>,
}
impl<T: Eq + Hash + Clone, I1: Iterator<Item = T>, I2: Iterator<Item = T>> Iterator
for Chain<I1, I2>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Iterator that iterates over given iterator and enumerates each element.
#[allow(missing_debug_implementations)]
pub struct Enumerate<I: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<I>,
}
impl<I: Iterator> Iterator for Enumerate<I> {
type Item = (usize, I::Item);
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Iterator that zips two iterators together.
///
/// If one iterator is longer than the other one, the remaining elements for the longer element
/// should be ignored.
#[allow(missing_debug_implementations)]
pub struct Zip<I1: Iterator, I2: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<(I1, I2)>,
}
impl<I1: Iterator, I2: Iterator> Iterator for Zip<I1, I2> {
type Item = (I1::Item, I2::Item);
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// My Itertools trait.
pub trait MyIterTools: Iterator {
/// Returns an iterator that iterates over the `self` and returns only unique elements.
fn my_unique(self) -> Unique<Self>
where
Self: Sized,
{
todo!()
}
/// Returns an iterator that chains `self` and `other` together.
fn my_chain<I: Iterator>(self, other: I) -> Chain<Self, I>
where
Self: Sized,
{
todo!()
}
/// Returns an iterator that iterates over `self` and enumerates each element.
fn my_enumerate(self) -> Enumerate<Self>
where
Self: Sized,
{
todo!()
}
/// Returns an iterator that zips `self` and `other` together.
fn my_zip<I: Iterator>(self, other: I) -> Zip<Self, I>
where
Self: Sized,
{
todo!()
}
/// Foldleft for `MyIterTools`
fn my_fold<T, F>(mut self, init: T, mut f: F) -> T
where
Self: Sized,
F: FnMut(Self::Item, T) -> T,
{
todo!()
}
}
impl<T: ?Sized> MyIterTools for T where T: Iterator {}

View File

@@ -0,0 +1,65 @@
#[cfg(test)]
mod test {
use itertools::Itertools;
use ntest::assert_about_eq;
use crate::assignments::assignment07::my_itertools::*;
#[test]
fn test_itertools() {
assert_eq!(
[10, 1, 1, 1, 2, 3, 4, 1, 3, 2]
.into_iter()
.my_chain(std::iter::repeat(100))
.my_unique()
.take(4)
.collect::<Vec<_>>(),
vec![10, 1, 2, 3]
);
assert_eq!(
std::iter::repeat(5)
.my_enumerate()
.map(|(i, e)| { i * e })
.take(5)
.collect::<Vec<_>>(),
vec![0, 5, 10, 15, 20]
);
assert_eq!(
vec![0, 1, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,],
[0, 1, 10].into_iter().my_chain(0..10).collect::<Vec<_>>(),
);
let it = || (1..=5).cycle().my_zip((1..=3).cycle()).map(|(x, y)| x * y);
let take15 = vec![
2, // 1 * 1,
4, // 2 * 2,
9, // 3 * 3,
4, // 4 * 1,
10, // 5 * 2,
3, // 1 * 3,
2, // 2 * 1,
6, // 3 * 2,
12, // 4 * 3,
5, // 5 * 1,
2, // 1 * 2,
6, // 2 * 3,
3, // 3 * 1,
8, // 4 * 2,
15, // 5 * 3,
];
assert_eq!(
// 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
// 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
it().take(15).collect::<Vec<_>>(),
take15
);
assert_eq!(
it().take(15).my_fold(0, |elt, acc| elt + acc),
take15.iter().sum()
);
}
}

View File

@@ -0,0 +1,119 @@
//! Implement functions usint `Iterator` trait
struct FindIter<'s, T: Eq> {
query: &'s [T],
base: &'s [T],
curr: usize,
}
impl<T: Eq> Iterator for FindIter<'_, T> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns an iterator over substring query indexes in the base.
pub fn find<'s, T: Eq>(query: &'s [T], base: &'s [T]) -> impl 's + Iterator<Item = usize> {
FindIter {
query,
base,
curr: 0,
}
}
/// Implement generic fibonacci iterator
struct FibIter<T> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<T>,
}
impl<T: std::ops::Add<Output = T> + Copy> FibIter<T> {
fn new(first: T, second: T) -> Self {
todo!()
}
}
impl<T> Iterator for FibIter<T>
where
T: std::ops::Add<Output = T> + Copy,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns and iterator over the generic fibonacci sequence starting from `first` and `second`.
/// This is a generic version of `fibonacci` function, which works for any types that implements `std::ops::Add` trait.
pub fn fib<T>(first: T, second: T) -> impl Iterator<Item = T>
where
T: std::ops::Add<Output = T> + Copy,
{
todo!("replace `std::iter::empty() with your owm implementation`");
std::iter::empty()
}
/// Endpoint of range, inclusive or exclusive.
#[derive(Debug)]
pub enum Endpoint {
/// Inclusive endpoint
Inclusive(isize),
/// Exclusive endpoint
Exclusive(isize),
}
struct RangeIter {
// TODO: add necessary fields as you want
}
impl RangeIter {
fn new(endpoints: (Endpoint, Endpoint), step: isize) -> Self {
todo!()
}
}
impl Iterator for RangeIter {
type Item = isize;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns an iterator over the range [left, right) with the given step.
pub fn range(left: Endpoint, right: Endpoint, step: isize) -> impl Iterator<Item = isize> {
todo!("replace `std::iter::empty() with your owm implementation`");
std::iter::empty()
}
/// Write an iterator that returns all divisors of n in increasing order.
/// Assume n > 0.
///
/// Hint: trying all candidates from 1 to n will most likely time out!
/// To optimize it, make use of the following fact:
/// if x is a divisor of n that is greater than sqrt(n),
/// then n/x is a divisor of n that is smaller than sqrt(n).
struct Divisors {
n: u64,
// TODO: you may define additional fields here
}
impl Iterator for Divisors {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns an iterator over the divisors of n.
pub fn divisors(n: u64) -> impl Iterator<Item = u64> {
Divisors {
n,
// TODO: you may define additional fields here
}
}

View File

@@ -0,0 +1,189 @@
#[cfg(test)]
mod test {
use itertools::Itertools;
use ntest::assert_about_eq;
use crate::assignments::assignment07::small_exercises::*;
#[test]
fn test_find() {
assert_eq!(
find("abc".as_bytes(), "abcdabcd".as_bytes()).collect::<Vec<usize>>(),
vec![0, 4]
);
assert_eq!(
find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::<Vec<usize>>(),
vec![0, 9, 12]
);
assert_eq!(
find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
assert_eq!(
find("ababc".as_bytes(), "abc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
}
#[test]
fn test_find_usize() {
assert_eq!(
find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::<Vec<usize>>(),
vec![0, 4]
);
assert_eq!(
find(
&[5, 5, 7, 5],
&[5, 5, 7, 5, 5, 8, 5, 5, 9, 5, 5, 7, 5, 5, 7, 5]
)
.collect::<Vec<usize>>(),
vec![0, 9, 12]
);
}
#[test]
fn test_fib_iter() {
assert_eq!(
fib(0, 1).take(10).collect::<Vec<_>>(),
vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Rgb(u8, u8, u8);
impl std::ops::Add for Rgb {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(
((self.0 as u16 + rhs.0 as u16) / 2) as u8,
((self.1 as u16 + rhs.1 as u16) / 2) as u8,
((self.2 as u16 + rhs.2 as u16) / 2) as u8,
)
}
}
assert_eq!(
fib(Rgb(255, 0, 100), Rgb(1, 128, 0))
.take(20)
.collect::<Vec<_>>(),
vec![
Rgb(255, 0, 100),
Rgb(1, 128, 0),
Rgb(128, 64, 50),
Rgb(64, 96, 25),
Rgb(96, 80, 37),
Rgb(80, 88, 31),
Rgb(88, 84, 34),
Rgb(84, 86, 32),
Rgb(86, 85, 33),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32),
Rgb(85, 85, 32)
]
);
}
#[test]
fn test_range_iter() {
let one_to_tens = vec![
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
range(Endpoint::Inclusive(1), Endpoint::Inclusive(10), 1).collect(),
range(Endpoint::Exclusive(0), Endpoint::Inclusive(10), 1).collect(),
range(Endpoint::Inclusive(1), Endpoint::Exclusive(11), 1).collect(),
range(Endpoint::Exclusive(0), Endpoint::Exclusive(11), 1).collect(),
];
assert!(one_to_tens.iter().all_equal());
let ten_to_ones = vec![
vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
range(Endpoint::Inclusive(10), Endpoint::Inclusive(1), -1).collect(),
range(Endpoint::Exclusive(11), Endpoint::Inclusive(1), -1).collect(),
range(Endpoint::Inclusive(10), Endpoint::Exclusive(0), -1).collect(),
range(Endpoint::Exclusive(11), Endpoint::Exclusive(0), -1).collect(),
];
assert!(ten_to_ones.iter().all_equal());
let five_evens = vec![
vec![2, 4, 6, 8, 10],
range(Endpoint::Inclusive(2), Endpoint::Inclusive(10), 2).collect(),
range(Endpoint::Inclusive(2), Endpoint::Inclusive(11), 2).collect(),
range(Endpoint::Exclusive(1), Endpoint::Inclusive(10), 2).collect(),
range(Endpoint::Exclusive(1), Endpoint::Inclusive(11), 2).collect(),
range(Endpoint::Inclusive(2), Endpoint::Exclusive(11), 2).collect(),
range(Endpoint::Inclusive(2), Endpoint::Exclusive(12), 2).collect(),
range(Endpoint::Exclusive(1), Endpoint::Exclusive(11), 2).collect(),
range(Endpoint::Exclusive(1), Endpoint::Exclusive(12), 2).collect(),
];
assert!(five_evens.iter().all_equal());
let emptys = vec![
vec![],
range(Endpoint::Inclusive(2), Endpoint::Inclusive(10), -1).collect(),
range(Endpoint::Inclusive(10), Endpoint::Inclusive(-100), 1).collect(),
range(Endpoint::Inclusive(1), Endpoint::Exclusive(1), 1).collect(),
];
assert!(emptys.iter().all_equal());
}
#[test]
fn test_small() {
assert_eq!(divisors(10).collect::<Vec<u64>>(), vec![1, 2, 5, 10]);
assert_eq!(divisors(17).collect::<Vec<u64>>(), vec![1, 17]);
assert_eq!(divisors(49).collect::<Vec<u64>>(), vec![1, 7, 49]);
assert_eq!(
divisors(120).collect::<Vec<u64>>(),
vec![1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120]
);
assert_eq!(divisors(1).collect::<Vec<u64>>(), vec![1]);
assert_eq!(divisors(2).collect::<Vec<u64>>(), vec![1, 2]);
assert_eq!(divisors(3).collect::<Vec<u64>>(), vec![1, 3]);
}
#[test]
fn test_large() {
assert_eq!(
divisors(1_000_000_000_000_037).collect::<Vec<u64>>(),
vec![1, 1_000_000_000_000_037]
);
assert_eq!(
divisors(99_999_820_000_081).collect::<Vec<u64>>(),
vec![1, 9_999_991, 99_999_820_000_081]
);
assert_eq!(
divisors(1_234_567_890_123).collect::<Vec<u64>>(),
vec![
1,
3,
3_541,
10_623,
116_216_501,
348_649_503,
411_522_630_041,
1_234_567_890_123
]
);
assert_eq!(divisors(97_821_761_637_600).count(), 17280);
}
}

View File

@@ -0,0 +1,95 @@
//! Tranformer
use std::marker::PhantomData;
use std::ops::Add;
/// Represents transformation of type `T`.
pub trait Transform<T> {
/// Transforms value.
fn transform(&self, value: T) -> T;
}
impl<T1, T2, Tr1: Transform<T1>, Tr2: Transform<T2>> Transform<(T1, T2)> for (Tr1, Tr2) {
fn transform(&self, value: (T1, T2)) -> (T1, T2) {
todo!()
}
}
/// Identity transformation.
#[derive(Debug, Clone, Copy)]
pub struct Identity;
impl<T> Transform<T> for Identity {
fn transform(&self, value: T) -> T {
todo!()
}
}
/// Custom transformation.
#[derive(Debug, Clone, Copy)]
pub struct Custom<T, F: Fn(T) -> T> {
f: F,
_marker: PhantomData<T>,
}
impl<T, F: Fn(T) -> T> From<F> for Custom<T, F> {
fn from(f: F) -> Self {
Self {
f,
_marker: PhantomData,
}
}
}
impl<T, F: Fn(T) -> T> Transform<T> for Custom<T, F> {
fn transform(&self, value: T) -> T {
todo!()
}
}
/// Repeats transformation for `n` times.
#[derive(Debug, Clone, Copy)]
pub struct Repeat<T, Tr: Transform<T>> {
inner: Tr,
n: u32,
_marker: PhantomData<T>,
}
impl<T, Tr: Transform<T>> Repeat<T, Tr> {
/// Creates a new repeat transformation.
pub fn new(inner: Tr, n: u32) -> Self {
Repeat {
inner,
n,
_marker: PhantomData,
}
}
}
impl<T, Tr: Transform<T>> Transform<T> for Repeat<T, Tr> {
fn transform(&self, mut value: T) -> T {
todo!()
}
}
/// Repeats transformation until converges.
#[derive(Debug, Clone, Copy)]
pub struct RepeatUntilConverge<T: Eq, Tr: Transform<T>> {
inner: Tr,
_marker: PhantomData<T>,
}
impl<T: Clone + Eq, Tr: Transform<T>> RepeatUntilConverge<T, Tr> {
/// Creates a new repeat transformation.
pub fn new(inner: Tr) -> Self {
RepeatUntilConverge {
inner,
_marker: PhantomData,
}
}
}
impl<T: Clone + Eq, Tr: Transform<T>> Transform<T> for RepeatUntilConverge<T, Tr> {
fn transform(&self, mut value: T) -> T {
todo!()
}
}

View File

@@ -0,0 +1,62 @@
#[cfg(test)]
mod test {
use itertools::Itertools;
use ntest::assert_about_eq;
use crate::assignments::assignment07::transform::*;
#[test]
fn test_transform_identity() {
let tr = Identity;
assert_eq!(tr.transform(3), 3);
assert_eq!(tr.transform(3.0), 3.0);
assert_eq!(tr.transform("abc"), "abc");
}
#[test]
fn test_transform_tuple() {
let f1 = |x: u32| x + 1;
let f2 = |x: String| x.clone() + &x;
let tr1: Custom<_, _> = f1.into();
let tr2: Custom<_, _> = f2.into();
let list1 = 0u32..10u32;
let list2 = ["a".to_string(), "bb".to_string(), "ccc".to_string()];
for v1 in list1 {
for v2 in list2.clone() {
let tr = (tr1, tr2.clone());
let input = (v1, v2.clone());
let expected = (f1(v1), f2(v2));
assert_eq!(tr.transform(input), expected);
}
}
}
#[test]
fn test_transform_repeat() {
let inc = Custom::from(|x: i32| x + 1);
let dec = Custom::from(|x: i32| x - 1);
for i in 0..10 {
for j in -10..10 {
assert_eq!(Repeat::new(inc, i as u32).transform(j), j + i);
assert_eq!(Repeat::new(dec, i as u32).transform(j), j - i);
}
}
}
#[test]
fn test_transform_repeat_until_converge() {
let inc = Custom::from(|x: i32| if x < 50 { x + 1 } else { x });
let dec = Custom::from(|x: i32| if x > 50 { x - 1 } else { x });
assert_eq!(RepeatUntilConverge::new(inc).transform(40), 50);
assert_eq!(RepeatUntilConverge::new(inc).transform(60), 60);
assert_eq!(RepeatUntilConverge::new(dec).transform(40), 40);
assert_eq!(RepeatUntilConverge::new(dec).transform(60), 50);
}
}

View File

@@ -1,44 +0,0 @@
#[cfg(test)]
mod test {
use super::super::assignment07::*;
#[test]
fn test_find() {
assert_eq!(
find("abc".as_bytes(), "abcdabcd".as_bytes()).collect::<Vec<usize>>(),
vec![0, 4]
);
assert_eq!(
find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::<Vec<usize>>(),
vec![0, 9, 12]
);
assert_eq!(
find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
assert_eq!(
find("ababc".as_bytes(), "abc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
}
#[test]
fn test_find_usize() {
assert_eq!(
find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::<Vec<usize>>(),
vec![0, 4]
);
assert_eq!(
find(
&[5, 5, 7, 5],
&[5, 5, 7, 5, 5, 8, 5, 5, 9, 5, 5, 7, 5, 5, 7, 5]
)
.collect::<Vec<usize>>(),
vec![0, 9, 12]
);
}
}

View File

@@ -0,0 +1,71 @@
//! Church Numerals
//!
//! This exercise involves the use of "Church numerals", a
//! representation of natural numbers using lambda calculus, named after
//! Alonzo Church. Each Church numeral corresponds to a natural number `n`
//! and is represented as a higher-order function that applies a given function `f` `n` times.
//!
//! For more information, see:
//! - <https://en.wikipedia.org/wiki/Church_encoding>
//! - <https://opendsa-server.cs.vt.edu/OpenDSA/Books/PL/html/ChurchNumerals.html>
use std::rc::Rc;
/// Church numerals are represented as higher-order functions that take a function `f`
pub type Church<T> = Rc<dyn Fn(Rc<dyn Fn(T) -> T>) -> Rc<dyn Fn(T) -> T>>;
/// This function returns a Church numeral equivalent of the natural number 1.
/// It takes a function `f` and applies it exactly once.
pub fn one<T: 'static>() -> Church<T> {
Rc::new(move |f| Rc::new(move |x| f(x)))
}
/// This function returns a Church numeral equivalent of the natural number 2.
/// It takes a function `f` and applies it twice.
pub fn two<T: 'static>() -> Church<T> {
Rc::new(move |f| Rc::new(move |x| f(f(x))))
}
/// This function represents the Church numeral for zero. As zero applications
/// of `f` should leave the argument unchanged, the function simply returns the input.
pub fn zero<T: 'static>() -> Church<T> {
Rc::new(|_| Rc::new(|x| x))
}
/// Implement a function to add 1 to a given Church numeral.
pub fn succ<T: 'static>(n: Church<T>) -> Church<T> {
todo!()
}
/// Implement a function to add two Church numerals.
pub fn add<T: 'static>(n: Church<T>, m: Church<T>) -> Church<T> {
todo!()
}
/// Implement a function to multiply (mult) two Church numerals.
pub fn mult<T: 'static>(n: Church<T>, m: Church<T>) -> Church<T> {
todo!()
}
/// Implement a function to raise one Church numeral to the power of another.
/// This is the Church numeral equivalent of the natural number operation of exponentiation.
/// Given two natural numbers `n` and `m`, the function should return a Church numeral
/// that represents `n` to the power of `m`. The key is to convert `n` and `m` to Church numerals,
/// and then apply the Church numeral for `m` (the exponent) to the Church numeral for `n` (the base).
/// Note: This function should be implemented *WITHOUT* using the `to_usize` or any `pow`-like method.
pub fn exp<T: 'static>(n: usize, m: usize) -> Church<T> {
// ACTION ITEM: Uncomment the following lines and replace `todo!()` with your code.
// let n = from_usize(n);
// let m = from_usize(m);
todo!()
}
/// Implement a function to convert a Church numeral to a usize type.
pub fn to_usize<T: 'static + Default>(n: Church<T>) -> usize {
todo!()
}
/// Implement a function to convert a usize type to a Church numeral.
pub fn from_usize<T: 'static>(n: usize) -> Church<T> {
todo!()
}

View File

@@ -0,0 +1,73 @@
#[cfg(test)]
mod test {
use rand::Rng;
use crate::assignments::assignment08::church::*;
#[test]
fn you_must_pass_these_examples() {
let c_zero = zero::<usize>();
assert_eq!(to_usize(c_zero.clone()), 0);
let c_one = succ(c_zero.clone());
assert_eq!(to_usize(c_one.clone()), to_usize(one::<()>()));
let c_two = add(c_one.clone(), c_one.clone());
let c_three = add(c_one.clone(), c_two.clone());
assert_eq!(to_usize(c_three.clone()), 3);
let c_product = mult(c_three.clone(), c_two.clone());
assert_eq!(to_usize(c_product.clone()), 6);
let c_exponent = exp::<()>(6, 3);
assert_eq!(to_usize(c_exponent), 216);
let c_exponent2 = exp::<()>(3, 0);
assert_eq!(to_usize(c_exponent2), 1);
}
fn id(n: usize) -> usize {
to_usize(from_usize::<()>(n))
}
fn c_id(n: Church<usize>) -> Church<usize> {
from_usize(to_usize(n))
}
#[test]
fn engineering_isnt_just_mathematics() {
const N: usize = 77777;
assert_eq!(N, id(N));
}
/// This test case is an optional challenge.
/// While it's not necessary to pass this test,
/// successfully doing so could provide a sense of satisfaction and achievement.
// #[test]
// fn i_said_engineering_isnt_just_mathematics() {
// const N: usize = 777777777777777;
// assert_eq!(N, id(N));
// }
#[test]
fn be_honest() {
let mut rng = rand::thread_rng();
for _ in 0..77 {
let x = rng.gen_range(0..=7);
let y = rng.gen_range(0..=7);
let c_x = from_usize(x);
let c_y = from_usize(y);
let c_sum = add(c_x.clone(), c_y.clone());
assert_eq!(to_usize(c_id(c_sum)), x + y);
let c_prod = mult(c_x.clone(), c_y.clone());
assert_eq!(to_usize(c_id(c_prod)), x * y);
let c_exp = exp(x, y);
assert_eq!(to_usize(c_id(c_exp)), x.pow(y as u32));
}
}
}

View File

@@ -0,0 +1,12 @@
//! Assignment 8: First-class functions.
//!
//! The primary goal of this assignment is to get used to first-class functions.
//!
//! 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.
pub mod church;
pub mod small_exercises;
mod church_grade;
mod small_exercises_grade;

View File

@@ -1,26 +1,44 @@
//! Assignment 8: First-class functions.
//!
//! The primary goal of this assignment is to get used to first-class functions.
//!
//! 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.
//! Assignment 08: First-class functions.
/// 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 +57,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

@@ -0,0 +1,47 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment08::small_exercises::*;
#[test]
fn test_repeat() {
for i in 0..10 {
assert_eq!(42 + 2 * i, repeat(i, |x| x + 2)(42));
}
}
#[test]
fn test_funny_map() {
assert_eq!(
vec![0, 3, 6, 9, 12],
funny_map(|x| x + 2, vec![0, 1, 2, 3, 4])
);
}
#[test]
fn test_either2_map() {
let v1 = Either2::<u32, f32>::Case1 { inner: 42 };
let u1 = Either2::<u32, f32>::Case1 { inner: 43 };
assert_eq!(u1, v1.map(|i| i + 1, |f| f + 1.0));
let v2 = Either2::<u32, f32>::Case2 { inner: 42.0 };
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

@@ -1,30 +0,0 @@
#[cfg(test)]
mod test {
use super::super::assignment08::*;
#[test]
fn test_repeat() {
for i in 0..10 {
assert_eq!(42 + 2 * i, repeat(i, |x| x + 2)(42));
}
}
#[test]
fn test_funny_map() {
assert_eq!(
vec![0, 3, 6, 9, 12],
funny_map(|x| x + 2, vec![0, 1, 2, 3, 4])
);
}
#[test]
fn test_either2_map() {
let v1 = Either2::<u32, f32>::Case1 { inner: 42 };
let u1 = Either2::<u32, f32>::Case1 { inner: 43 };
assert_eq!(u1, v1.map(|i| i + 1, |f| f + 1.0));
let v2 = Either2::<u32, f32>::Case2 { inner: 42.0 };
let u2 = Either2::<u32, f32>::Case2 { inner: 43.0 };
assert_eq!(u2, v2.map(|i| i + 1, |f| f + 1.0));
}
}

View File

@@ -0,0 +1,91 @@
//! Big integer with infinite precision.
use std::fmt;
use std::{iter::zip, ops::*};
/// An signed integer with infinite precision implemented with an "carrier" vector of `u32`s.
///
/// The vector is interpreted as a base 2^(32 * (len(carrier) - 1)) integer, where negative
/// integers are represented in their [2's complement form](https://en.wikipedia.org/wiki/Two%27s_complement).
///
/// For example, the vector `vec![44,345,3]` represents the integer
/// `44 * (2^32)^2 + 345 * (2^32) + 3`,
/// and the vector `vec![u32::MAX - 5, u32::MAX - 7]` represents the integer
/// `- (5 * 2^32 + 8)
///
/// You will implement the `Add` and `Sub` trait for this type.
///
/// Unlike standard fix-sized intergers in Rust where overflow will panic, the carrier is extended to save the overflowed bit.
/// On the contrary, if the precision is too much (e.g, vec![0,0] is used to represent 0, where `vec![0]` is sufficent), the carrier is truncated.
///
/// See [this section](https://en.wikipedia.org/wiki/Two%27s_complement#Arithmetic_operations) for a rouge guide on implementation,
/// while keeping in mind that the carrier should be extended to deal with overflow.
///
/// The `sign_extension()`, `two_complement()`, and `truncate()` are non-mandatory helper methods.
///
/// For testing and debugging pruposes, the `Display` trait is implemented for you, which shows the integer in hexadecimal form.
#[derive(Debug, Clone)]
pub struct BigInt {
/// The carrier for `BigInt`.
///
/// Note that the carrier should always be non-empty.
pub carrier: Vec<u32>,
}
impl BigInt {
/// Create a new `BigInt` from a `usize`.
pub fn new(n: u32) -> Self {
todo!()
}
/// Creates a new `BigInt` from a `Vec<u32>`.
pub fn new_large(carrier: Vec<u32>) -> Self {
assert!(!carrier.is_empty());
todo!()
}
}
const SIGN_MASK: u32 = 1 << 31;
impl BigInt {
/// Extend `self` to `len` bits.
fn sign_extension(&self, len: usize) -> Self {
todo!()
}
/// Compute the two's complement of `self`.
fn two_complement(&self) -> Self {
todo!()
}
/// Truncate a `BigInt` to the minimum length.
fn truncate(&self) -> Self {
todo!()
}
}
impl Add for BigInt {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
todo!()
}
}
impl Sub for BigInt {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
todo!()
}
}
impl fmt::Display for BigInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Hex formatting so that each u32 can be formatted independently.
for i in self.carrier.iter() {
write!(f, "{:08x}", i)?;
}
Ok(())
}
}

View File

@@ -0,0 +1,104 @@
#[cfg(test)]
mod test {
use ntest::{assert_false, assert_true};
use crate::assignments::assignment09::bigint::*;
#[test]
fn test_inf_prec_simple() {
// Basic
assert_eq!("00000000", format!("{}", BigInt::new(0)));
assert_eq!("ffffffff", format!("{}", BigInt::new(u32::MAX)));
assert_eq!("00bc4fdc", format!("{}", BigInt::new(12_341_212)));
assert_eq!("fffffed8", format!("{}", BigInt::new(4_294_967_000u32)));
// Add Basic
assert_eq!("00000001", format!("{}", BigInt::new(0) + BigInt::new(1)));
assert_eq!(
"0df655df",
format!("{}", BigInt::new(13_413) + BigInt::new(234_234_234))
);
assert_eq!(
"ffffff03",
format!("{}", BigInt::new(4_294_967_000u32) + BigInt::new(43))
);
// Sub Basic
assert_eq!("ffffffff", format!("{}", BigInt::new(0) - BigInt::new(1)));
assert_eq!(
"f20a12eb",
format!("{}", BigInt::new(13_413) - BigInt::new(234_234_234))
);
assert_eq!(
"fffffead",
format!("{}", BigInt::new(4_294_967_000u32) - BigInt::new(43))
);
}
#[test]
#[should_panic]
fn test_inf_prec_panic() {
let _ = BigInt::new_large(vec![]);
}
#[test]
fn test_inf_prec_complex() {
// Positive overflow
assert_eq!(
"0000000080000000",
format!("{}", BigInt::new(i32::MAX as u32) + BigInt::new(1))
);
// Negative overflow
assert_eq!(
"ffffffff7fffffff",
format!("{}", BigInt::new(i32::MIN as u32) - BigInt::new(1))
);
// Larger positive overflow
assert_eq!(
"00000000fffffffe00000000",
format!(
"{}",
BigInt::new_large(vec![i32::MAX as u32, 0])
+ BigInt::new_large(vec![i32::MAX as u32, 0])
)
);
// Smaller negative overflow
assert_eq!(
"ffffffff000000000119464a",
format!(
"{}",
BigInt::new_large(vec![i32::MIN as u32, 2_871_572])
+ BigInt::new_large(vec![i32::MIN as u32, 15_562_038])
)
);
// Truncate
assert_eq!(
"00000000",
format!(
"{}",
BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_456])
- BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_456])
)
);
assert_eq!(
"ffffffff",
format!(
"{}",
BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_456])
- BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_457])
)
);
// TODO: add a test case testing sign extension.
}
}

View File

@@ -0,0 +1,72 @@
//! Simple matrix multiplication
use itertools::*;
/// elementwise vector addition
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment09::vec_add;
///
/// let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let res = vec_add(&vec1, &vec2);
/// assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
/// ```
pub fn vec_add(lhs: &[f64], rhs: &[f64]) -> Vec<f64> {
todo!()
}
/// dot product of two arrays
///
/// You don't know how to calculate dot product?
/// See <https://mathinsight.org/dot_product_examples>
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment09::dot_product;
///
/// let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let res = dot_product(&vec1, &vec2);
///
/// assert_eq!(res, 55.0);
/// ```
pub fn dot_product(lhs: &[f64], rhs: &[f64]) -> f64 {
todo!()
}
/// Matrix multiplication
///
/// You don't know how to multiply matrix?
/// Quite simple! See <https://www.mathsisfun.com/algebra/matrix-multiplying.html>
///
/// Assume rhs is transposed
/// - lhs: (m, n)
/// - rhs: (p, n)
/// - output: (m, p)
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment09::matmul;
///
/// let mat1 = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
/// let mat2 = vec![
/// vec![7.0, 8.0, 9.0],
/// vec![10.0, 11.0, 12.0],
/// vec![13.0, 14.0, 15.0],
/// vec![16.0, 17.0, 18.0],
/// ];
/// let ans = vec![
/// vec![50.0, 68.0, 86.0, 104.0],
/// vec![122.0, 167.0, 212.0, 257.0],
/// ];
/// let res = matmul(&mat1, &mat2);
/// assert_eq!(ans, res);
/// ```
pub fn matmul(lhs: &[Vec<f64>], rhs: &[Vec<f64>]) -> Vec<Vec<f64>> {
todo!()
}

View File

@@ -0,0 +1,106 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment09::matmul::*;
use approx::*;
use itertools::Itertools;
use ndarray::prelude::*;
use ndarray_rand::{rand_distr::Uniform, RandomExt};
#[test]
fn vec_add_test() {
let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let res = vec_add(&vec1, &vec2);
assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
for _ in 0..5 {
let vec1 = Array::random(500000, Uniform::new(0., 10.));
let vec2 = Array::random(500000, Uniform::new(0., 10.));
let res = vec_add(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let ans = vec1 + vec2;
assert_eq!(Array::from_vec(res), ans);
}
}
#[test]
fn dot_product_test() {
let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let res = dot_product(&vec1, &vec2);
assert_eq!(res, 55.0);
for _ in 0..5 {
let vec1 = Array::random(1000000, Uniform::new(0., 10.));
let vec2 = Array::random(1000000, Uniform::new(0., 10.));
let res = dot_product(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let _res = relative_eq!(res, vec1.dot(&vec2), epsilon = f64::EPSILON);
}
}
/// Reference: <https://github.com/rust-ndarray/ndarray/issues/590>
/// Converts nested `Vec`s to a 2-D array by cloning the elements.
///
/// **Panics** if the length of any axis overflows `isize`, if the
/// size in bytes of all the data overflows `isize`, or if not all the
/// rows have the same length.
fn vec_to_array<T: Clone>(v: Vec<Vec<T>>) -> Array2<T> {
if v.is_empty() {
return Array2::from_shape_vec((0, 0), Vec::new()).unwrap();
}
let nrows = v.len();
let ncols = v[0].len();
let mut data = Vec::with_capacity(nrows * ncols);
for row in &v {
assert_eq!(row.len(), ncols);
data.extend_from_slice(row);
}
Array2::from_shape_vec((nrows, ncols), data).unwrap()
}
#[test]
fn matmul_test() {
let mat1 = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
let mat2 = vec![
vec![7.0, 8.0, 9.0],
vec![10.0, 11.0, 12.0],
vec![13.0, 14.0, 15.0],
vec![16.0, 17.0, 18.0],
];
let ans = vec![
vec![50.0, 68.0, 86.0, 104.0],
vec![122.0, 167.0, 212.0, 257.0],
];
let res = matmul(&mat1, &mat2);
assert_eq!(ans, res);
for _ in 0..5 {
let mat1 = Array::random((500, 500), Uniform::new(0., 10.));
let mat2 = Array::random((500, 500), Uniform::new(0., 10.));
let ans = mat1.dot(&mat2);
let mat2_transposed = mat2.t();
// Run sequential matrix multiplication
let res = matmul(
mat1.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
mat2_transposed
.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
);
// Check answer
for it in ans.iter().zip(vec_to_array(res).iter()) {
let (ans, res) = it;
let _res = relative_eq!(ans, res);
}
}
}
}

View File

@@ -0,0 +1,14 @@
//! Assignment 9: Iterators (1/2).
//!
//! The primary goal of this assignment is to get used to iterators.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-09.sh` works fine.
//! See `assignment09/*_grade.rs` and `/scripts/grade-09.sh` for the test script.
pub mod bigint;
pub mod matmul;
pub mod small_exercises;
mod bigint_grade;
mod matmul_grade;
mod small_exercises_grade;

View File

@@ -1,14 +1,7 @@
//! Assignment 9: Iterators (1/2).
//!
//! The primary goal of this assignment is to get used to iterators.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-09.sh` works fine.
//! See `assignment09_grade.rs` and `/scripts/grade-09.sh` for the test script.
//! Small exercises.
use std::collections::HashMap;
use itertools::*;
/// Returns whether the given sequence is a fibonacci sequence starts from the given sequence's first two terms.
///
/// Returns `true` if the length of sequence is less or equal than 2.
@@ -61,6 +54,27 @@ pub fn interleave3<T>(
todo!()
}
/// Alternate elements from array of n iterators until they have run out.
///
/// You can assume that the number of elements of iterators are same.
///
/// # Example
///
/// ```
/// use cs220::assignments::assignment09::interleave_n;
///
/// assert_eq!(
/// interleave_n(&mut [[1, 2].into_iter(), [3, 4].into_iter(), [5, 6].into_iter()]),
/// vec![1, 3, 5, 2, 4, 6]
/// );
/// ```
pub fn interleave_n<T, const N: usize>(
mut iters: [impl Iterator<Item = T>; N],
) -> impl Iterator<Item = T> {
todo!();
std::iter::empty()
}
/// Returns mean of k smallest value's mean.
///
/// # Example
@@ -172,3 +186,26 @@ pub fn find_count_n(inner: Vec<usize>, n: usize) -> Vec<usize> {
pub fn position_median<T: Ord>(inner: Vec<T>) -> Option<usize> {
todo!()
}
/// Returns the sum of all elements in a two-dimensional array.
///
/// # Example
/// ```
/// assert_eq!(
/// two_dimensional_sum([[1, 2, 3].into_iter(), [4, 5, 6].into_iter()].into_iter()),
/// 21
/// );
/// ```
pub fn two_dimensional_sum(inner: impl Iterator<Item = impl Iterator<Item = i64>>) -> i64 {
todo!()
}
/// Returns whether the given string is palindrome or not.
///
/// A palindrome is a word, number, phrase, or other sequence of characters which reads the same backward as forward.
/// We consider the empty string is palindrome.
///
/// Consult <https://en.wikipedia.org/wiki/Palindrome>.
pub fn is_palindrome(s: String) -> bool {
todo!()
}

View File

@@ -1,6 +1,9 @@
#[cfg(test)]
mod test {
use super::super::assignment09::*;
use ntest::{assert_false, assert_true};
use crate::assignments::assignment09::small_exercises::*;
#[test]
fn test_is_fibonacci() {
@@ -74,6 +77,35 @@ mod test {
);
}
#[test]
fn test_interleave_n() {
assert_eq!(
interleave_n([[1, 2].into_iter(), [3, 4].into_iter(), [5, 6].into_iter()])
.collect::<Vec<_>>(),
vec![1, 3, 5, 2, 4, 6]
);
assert_eq!(
interleave_n([
[1, 2, 3].into_iter(),
[4, 5, 6].into_iter(),
[7, 8, 9].into_iter()
])
.collect::<Vec<_>>(),
vec![1, 4, 7, 2, 5, 8, 3, 6, 9]
);
assert_eq!(
interleave_n([
["a", "b", "c"].into_iter(),
["d", "e", "f"].into_iter(),
["g", "h", "i"].into_iter()
])
.collect::<String>(),
"adgbehcfi"
);
}
#[test]
fn test_k_smallest_man() {
assert_eq!(
@@ -200,4 +232,37 @@ mod test {
assert_eq!(position_median(vec![1, 3, 3, 6, 7, 8, 9]), Some(3));
assert_eq!(position_median(vec![1, 2, 3, 4, 5, 6, 8, 9]), Some(4));
}
#[test]
fn test_two_dimensional_sum() {
assert_eq!(
two_dimensional_sum([[1, 2, 3].into_iter(), [4, 5, 6].into_iter()].into_iter()),
21
);
assert_eq!(
two_dimensional_sum(
[
[1, 2, 3, 4, 5].into_iter(),
[10, 20, 30, 40, 50].into_iter()
]
.into_iter()
),
165
);
}
#[test]
fn test_is_palindrome() {
assert_true!(is_palindrome("kayak".to_string()));
assert_true!(is_palindrome("dammitimmad".to_string()));
assert_true!(is_palindrome("deified".to_string()));
assert_true!(is_palindrome("rotator".to_string()));
assert_true!(is_palindrome("noon".to_string()));
assert_true!(is_palindrome("".to_string()));
assert_true!(is_palindrome("a".to_string()));
assert_false!(is_palindrome("moon".to_string()));
assert_false!(is_palindrome("hello".to_string()));
assert_false!(is_palindrome("apple".to_string()));
}
}

View File

@@ -0,0 +1,48 @@
//! Labyrinth
//!
//! Look at the `labyrinth_grade.rs` below before you start.
//! HINT: <https://en.wikipedia.org/wiki/100_prisoners_problem>
//!
//! NOTE: You will have to implement a probabilistic algorithm, which means, the algorithm can fail
//! even if you have implemented the solution. We recommend running multiple times (at least 5 times) to check your
//! solution works well.
use std::cell::RefCell;
/// Husband
#[derive(Debug)]
pub struct Husband {
brain: RefCell<[usize; 100]>,
}
impl Husband {
/// What might a husband, who is looking for his wife's ID my_wife, be thinking?
pub fn seeking(my_wife: usize) -> Self {
todo!()
}
#[allow(missing_docs)]
pub fn has_devised_a_strategy(&self) -> Strategy<'_> {
Strategy { husband: self }
}
/// Based on the information about currently visited room number and someone's wife ID trapped inside,
/// what the husband should do next?
pub fn carefully_checks_whos_inside(&self, room: usize, wife: usize) {
todo!()
}
}
/// Strategy of husband
#[derive(Debug)]
pub struct Strategy<'a> {
husband: &'a Husband,
}
impl Iterator for Strategy<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}

View File

@@ -0,0 +1,62 @@
#[cfg(test)]
mod test {
use rand::seq::SliceRandom;
use rand::thread_rng;
use crate::assignments::assignment10::labyrinth::*;
type Wife = usize;
type Rooms = Vec<Wife>;
struct Labyrinth {
rooms: Rooms,
}
impl From<Rooms> for Labyrinth {
fn from(rooms: Rooms) -> Self {
Self { rooms }
}
}
impl Labyrinth {
fn open_the_door(&self, index: usize) -> Wife {
self.rooms[index]
}
}
#[test]
fn can_every_husband_rescue_his_wife() {
// HINT: https://en.wikipedia.org/wiki/100_prisoners_problem
const WIVES: usize = 100;
// One day, wives of 100 husbands were kidnapped by the Minotaur
// and imprisoned in a labyrinth.... 🏰
let labyrinth = Labyrinth::from({
let mut rooms: Vec<_> = (0..WIVES).collect();
rooms.shuffle(&mut thread_rng());
rooms
});
assert!((0..WIVES).all(|his_wife| {
// A new husband steps into the labyrinth to rescue his wife...!
let husband = Box::new(Husband::seeking(his_wife /*👩*/));
let strategy = Box::new(husband.has_devised_a_strategy());
#[allow(clippy::all)]
/* The Minotaur🐂 will arrive in */
(0..50) /* steps... */
.zip(strategy)
.find(|(_, room)| {
// The husband contemplates his next move... 🤔
// and finally,
let someone/*👤*/ = labyrinth.open_the_door(*room); // 🚪
husband.carefully_checks_whos_inside(*room, someone);
// Has the husband found his wife...?
someone/*👤*/ == his_wife /*👩*/
})
.is_some(/* The husband has successfully rescued his wife! 👫*/)
// or is_none(/* The unfortunate husband has encountered the Minotaur and... 🪓*/)
}));
}
}

View File

@@ -0,0 +1,11 @@
//! Assignment 10: Iterators (2/2).
//! The primary goal of this assignment is to get used to iterators.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-10.sh` works fine.
//! See `assignment10/*_grade.rs` and `/scripts/grade-10.sh` for the test script.
pub mod labyrinth;
pub mod small_exercises;
mod labyrinth_grade;
mod small_exercises_grade;

View File

@@ -1,9 +1,4 @@
//! Assignment 10: Iterators (2/2).
//!
//! The primary goal of this assignment is to get used to iterators.
//!
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-10.sh` works fine.
//! See `assignment10_grade.rs` and `/scripts/grade-10.sh` for the test script.
//! Small exercises.
use itertools::*;
@@ -117,3 +112,82 @@ pub enum File {
pub fn du_sort(root: &File) -> Vec<(&str, usize)> {
todo!()
}
/// Remove all even numbers inside a vector using the given mutable reference.
/// That is, you must modify the vector using the given mutable reference instead
/// of returning a new vector.
///
/// # Example
/// ```
/// let mut vec = vec![1, 2, 3, 4, 5];
/// remove_even(&mut vec);
/// assert_eq!(*vec, vec![1, 3, 5]);
/// ```
#[allow(clippy::ptr_arg)]
pub fn remove_even(inner: &mut Vec<i64>) {
todo!()
}
/// Remove all duplicate occurences of a number inside the array.
/// That is, if an integer appears more than once, remove some occurences
/// of it so that it only appears once. Note that you must modify the vector
/// using the given mutable reference instead of returning a new vector.
/// Also, note that the order does not matter.
///
/// # Example
/// ```
/// let mut vec = vec![1, 2, 1, 1, 3, 7, 5, 7];
/// remove_duplicate(&mut vec);
/// assert_eq!(*vec, vec![1, 2, 3, 7, 5]);
/// ```
#[allow(clippy::ptr_arg)]
pub fn remove_duplicate(inner: &mut Vec<i64>) {
todo!()
}
/// Returns the natural join of two tables using the first column as the join argument.
/// That is, for each pair of a row(`Vec<String>`) from table1 and a row(`Vec<String>`) from table2,
/// if the first element of them are equal, then add all elements of the row from table2
/// except its first element to the row from table1 and add it to the results.
/// Note that the order of results does not matter.
///
/// # Example
///
/// table1 table2
/// ---------------------- ----------------------
/// 20230001 | Jack 20230001 | CS
/// 20231234 | Mike 20230001 | EE
/// 20231234 | ME
///
///
/// result
/// -----------------------------------
/// 20230001 | Jack | CS
/// 20230001 | Jack | EE
/// 20231234 | Mike | ME
///
pub fn natural_join(table1: Vec<Vec<String>>, table2: Vec<Vec<String>>) -> Vec<Vec<String>> {
todo!()
}
struct Pythagorean;
impl Pythagorean {
fn new() -> Self {
todo!()
}
}
impl Iterator for Pythagorean {
type Item = (u64, u64, u64);
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Generates sequence of unique [primitive Pythagorean triples](https://en.wikipedia.org/wiki/Pythagorean_triple),
/// i.e. (a,b,c) such that a² + b² = c², a and b are coprimes, and a < b. Generate in the increasing order of c.
pub fn pythagorean() -> impl Iterator<Item = (u64, u64, u64)> {
Pythagorean::new()
}

View File

@@ -0,0 +1,351 @@
#[cfg(test)]
mod test {
use std::collections::HashSet;
use crate::assignments::assignment10::small_exercises::*;
#[test]
fn test_inversion() {
assert_eq!(inversion(vec![3, 5, 4]), vec![(1, 2)]);
assert_eq!(inversion(vec!["c", "a", "b", "d"]), vec![(0, 1), (0, 2)]);
assert_eq!(
inversion(vec![2, 5, 4, 6, 3, 1]),
vec![
(0, 5),
(1, 2),
(1, 4),
(1, 5),
(2, 4),
(2, 5),
(3, 4),
(3, 5),
(4, 5)
]
);
}
#[test]
fn test_traverse_preorder() {
let root = Node::NonLeaf((
1,
vec![
Node::NonLeaf((2, vec![Node::Leaf(5), Node::Leaf(6)])),
Node::Leaf(3),
Node::NonLeaf((4, vec![Node::Leaf(7), Node::Leaf(8), Node::Leaf(9)])),
],
));
assert_eq!(traverse_preorder(root), vec![1, 2, 5, 6, 3, 4, 7, 8, 9]);
}
#[test]
fn test_du_sort() {
let rootfile = File::Directory(
"root".to_string(),
vec![
File::Directory(
"a".to_string(),
vec![
File::Data("a1".to_string(), 1),
File::Data("a2".to_string(), 3),
],
),
File::Directory(
"b".to_string(),
vec![
File::Data("b1".to_string(), 3),
File::Data("b2".to_string(), 15),
],
),
File::Data("c".to_string(), 8),
],
);
assert_eq!(
du_sort(&rootfile),
vec![
("a1", 1),
("a2", 3),
("b1", 3),
("a", 4),
("c", 8),
("b2", 15),
("b", 18),
("root", 1 + 3 + 3 + 15 + 8)
]
);
let rootfile = File::Directory(
"root".to_string(),
vec![
File::Directory(
"b".to_string(),
vec![
File::Data("b1".to_string(), 3),
File::Data("b2".to_string(), 15),
],
),
File::Data("c".to_string(), 8),
File::Directory(
"a".to_string(),
vec![
File::Data("a1".to_string(), 1),
File::Data("a2".to_string(), 3),
],
),
],
);
assert_eq!(
du_sort(&rootfile),
vec![
("a1", 1),
("a2", 3),
("b1", 3),
("a", 4),
("c", 8),
("b2", 15),
("b", 18),
("root", 1 + 3 + 3 + 15 + 8)
]
);
let rootfile = File::Directory(
"root".to_string(),
vec![
File::Directory(
"a".to_string(),
vec![
File::Data("a1".to_string(), 1),
File::Data("a2".to_string(), 3),
File::Directory(
"a3".to_string(),
vec![
File::Data("a31".to_string(), 1),
File::Data("a32".to_string(), 3),
File::Data("a33".to_string(), 6),
],
),
],
),
File::Directory(
"b".to_string(),
vec![
File::Data("b1".to_string(), 3),
File::Data("b2".to_string(), 15),
],
),
File::Data("c".to_string(), 16),
],
);
assert_eq!(
du_sort(&rootfile),
vec![
("a1", 1),
("a31", 1),
("a2", 3),
("a32", 3),
("b1", 3),
("a33", 6),
("a3", 10),
("a", 14),
("b2", 15),
("c", 16),
("b", 18),
("root", 48)
]
);
}
#[test]
fn test_remove_even() {
let mut vec = vec![1, 2, 3, 4, 5];
remove_even(&mut vec);
assert_eq!(*vec, vec![1, 3, 5]);
let mut vec = vec![11, 1000, 12, 101, 105, 104, 200];
remove_even(&mut vec);
assert_eq!(*vec, vec![11, 101, 105]);
}
#[test]
fn test_remove_duplicate() {
let mut vec = vec![1, 2, 1, 1, 3, 7, 5, 7];
remove_duplicate(&mut vec);
let set1: HashSet<i64> = HashSet::from_iter(vec);
let set2: HashSet<i64> = HashSet::from_iter(vec![1, 2, 3, 7, 5]);
assert_eq!(set1, set2);
}
#[test]
fn test_natural_join() {
let row1: Vec<String> = vec!["20230001", "Jack"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20231234", "Mike"]
.iter()
.map(|s| s.to_string())
.collect();
let table1 = vec![row1, row2];
let row1: Vec<String> = vec!["20230001", "CS"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "EE"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20231234", "ME"]
.iter()
.map(|s| s.to_string())
.collect();
let table2 = vec![row1, row2, row3];
let row1: Vec<String> = vec!["20230001", "Jack", "CS"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "Jack", "EE"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20231234", "Mike", "ME"]
.iter()
.map(|s| s.to_string())
.collect();
let table3 = vec![row1, row2, row3];
assert_eq!(
HashSet::<Vec<String>>::from_iter(natural_join(table1, table2)),
HashSet::<Vec<String>>::from_iter(table3)
);
let row1: Vec<String> = vec!["20230001", "Alice"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230002", "Bob"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20230003", "Charlie"]
.iter()
.map(|s| s.to_string())
.collect();
let row4: Vec<String> = vec!["20230004", "David"]
.iter()
.map(|s| s.to_string())
.collect();
let table1 = vec![row1, row2, row3, row4];
let row1: Vec<String> = vec!["20230001", "Apple"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "Avocado"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20230002", "Banana"]
.iter()
.map(|s| s.to_string())
.collect();
let row4: Vec<String> = vec!["20230002", "Berries"]
.iter()
.map(|s| s.to_string())
.collect();
let row5: Vec<String> = vec!["20230004", "Durian"]
.iter()
.map(|s| s.to_string())
.collect();
let table2 = vec![row1, row2, row3, row4, row5];
let row1: Vec<String> = vec!["20230001", "Alice", "Apple"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "Alice", "Avocado"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20230002", "Bob", "Banana"]
.iter()
.map(|s| s.to_string())
.collect();
let row4: Vec<String> = vec!["20230002", "Bob", "Berries"]
.iter()
.map(|s| s.to_string())
.collect();
let row5: Vec<String> = vec!["20230004", "David", "Durian"]
.iter()
.map(|s| s.to_string())
.collect();
let table3 = vec![row1, row2, row3, row4, row5];
assert_eq!(
HashSet::<Vec<String>>::from_iter(natural_join(table1, table2)),
HashSet::<Vec<String>>::from_iter(table3)
);
}
#[test]
fn test_pythagorean() {
let pythagoreans = [
(3, 4, 5),
(5, 12, 13),
(8, 15, 17),
(7, 24, 25),
(20, 21, 29),
(12, 35, 37),
(9, 40, 41),
(28, 45, 53),
(11, 60, 61),
(16, 63, 65),
(33, 56, 65),
(48, 55, 73),
(13, 84, 85),
(36, 77, 85),
(39, 80, 89),
(65, 72, 97),
(20, 99, 101),
(60, 91, 109),
(15, 112, 113),
(44, 117, 125),
(88, 105, 137),
(17, 144, 145),
(24, 143, 145),
(51, 140, 149),
(85, 132, 157),
(119, 120, 169),
(52, 165, 173),
(19, 180, 181),
(57, 176, 185),
(104, 153, 185),
(95, 168, 193),
(28, 195, 197),
(84, 187, 205),
(133, 156, 205),
(21, 220, 221),
(140, 171, 221),
(60, 221, 229),
(105, 208, 233),
(120, 209, 241),
(32, 255, 257),
(23, 264, 265),
(96, 247, 265),
(69, 260, 269),
(115, 252, 277),
(160, 231, 281),
(161, 240, 289),
(68, 285, 293),
];
for (i, t) in pythagorean().enumerate().take(1000) {
if i < pythagoreans.len() {
assert_eq!(pythagoreans[i], t)
}
let (a, b, c) = t;
assert_eq!(a * a + b * b, c * c);
}
}
}

View File

@@ -1,158 +0,0 @@
#[cfg(test)]
mod test {
use super::super::assignment10::*;
#[test]
fn test_inversion() {
assert_eq!(inversion(vec![3, 5, 4]), vec![(1, 2)]);
assert_eq!(inversion(vec!["c", "a", "b", "d"]), vec![(0, 1), (0, 2)]);
assert_eq!(
inversion(vec![2, 5, 4, 6, 3, 1]),
vec![
(0, 5),
(1, 2),
(1, 4),
(1, 5),
(2, 4),
(2, 5),
(3, 4),
(3, 5),
(4, 5)
]
);
}
#[test]
fn test_traverse_preorder() {
let root = Node::NonLeaf((
1,
vec![
Node::NonLeaf((2, vec![Node::Leaf(5), Node::Leaf(6)])),
Node::Leaf(3),
Node::NonLeaf((4, vec![Node::Leaf(7), Node::Leaf(8), Node::Leaf(9)])),
],
));
assert_eq!(traverse_preorder(root), vec![1, 2, 5, 6, 3, 4, 7, 8, 9]);
}
#[test]
fn test_du_sort() {
let rootfile = File::Directory(
"root".to_string(),
vec![
File::Directory(
"a".to_string(),
vec![
File::Data("a1".to_string(), 1),
File::Data("a2".to_string(), 3),
],
),
File::Directory(
"b".to_string(),
vec![
File::Data("b1".to_string(), 3),
File::Data("b2".to_string(), 15),
],
),
File::Data("c".to_string(), 8),
],
);
assert_eq!(
du_sort(&rootfile),
vec![
("a1", 1),
("a2", 3),
("b1", 3),
("a", 4),
("c", 8),
("b2", 15),
("b", 18),
("root", 1 + 3 + 3 + 15 + 8)
]
);
let rootfile = File::Directory(
"root".to_string(),
vec![
File::Directory(
"b".to_string(),
vec![
File::Data("b1".to_string(), 3),
File::Data("b2".to_string(), 15),
],
),
File::Data("c".to_string(), 8),
File::Directory(
"a".to_string(),
vec![
File::Data("a1".to_string(), 1),
File::Data("a2".to_string(), 3),
],
),
],
);
assert_eq!(
du_sort(&rootfile),
vec![
("a1", 1),
("a2", 3),
("b1", 3),
("a", 4),
("c", 8),
("b2", 15),
("b", 18),
("root", 1 + 3 + 3 + 15 + 8)
]
);
let rootfile = File::Directory(
"root".to_string(),
vec![
File::Directory(
"a".to_string(),
vec![
File::Data("a1".to_string(), 1),
File::Data("a2".to_string(), 3),
File::Directory(
"a3".to_string(),
vec![
File::Data("a31".to_string(), 1),
File::Data("a32".to_string(), 3),
File::Data("a33".to_string(), 6),
],
),
],
),
File::Directory(
"b".to_string(),
vec![
File::Data("b1".to_string(), 3),
File::Data("b2".to_string(), 15),
],
),
File::Data("c".to_string(), 16),
],
);
assert_eq!(
du_sort(&rootfile),
vec![
("a1", 1),
("a31", 1),
("a2", 3),
("a32", 3),
("b1", 3),
("a33", 6),
("a3", 10),
("a", 14),
("b2", 15),
("c", 16),
("b", 18),
("root", 48)
]
);
}
}

View File

@@ -0,0 +1,97 @@
//! 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.
///
/// TODO: You can freely add fields to this struct.
#[derive(Debug, Clone)]
pub struct NodeHandle;
/// Error type for graph operations.
#[derive(Debug)]
pub struct GraphError;
/// Subgraph
///
/// TODO: You can freely add fields to this struct.
#[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 crate::assignments::assignment11::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();
(0..6).for_each(|n| {
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

@@ -59,4 +59,82 @@ impl<T: Debug> SinglyLinkedList<T> {
pub fn pop_back(&mut self) -> Option<T> {
todo!()
}
/// Create a new list from the given vector `vec`.
pub fn from_vec(vec: Vec<T>) -> Self {
todo!()
}
/// Convert the current list into a vector.
pub fn as_vec(&self) -> Vec<T> {
todo!()
}
/// Return the length (i.e., number of nodes) of the list.
pub fn length(&self) -> usize {
todo!()
}
/// Apply function `f` on every element of the list.
///
/// # Examples
///
/// `self`: `[1, 2]`, `f`: `|x| x + 1` ==> `[2, 3]`
pub fn map<F: Fn(T) -> T>(&mut self, f: F) {
todo!()
}
/// Insert given list `another` at the specified index `idx`.
/// If `idx` is out-of-bound of `self`, append `another` at the end of `self`.
///
/// # Examples
///
/// `self`: `[1, 2]`, `another`: `[3, 4]`, `idx`: `1` ==> `[1, 3, 4, 2]`
/// `self`: `[1, 2]`, `another`: `[3, 4]`, `idx`: `5` ==> `[1, 2, 3, 4]`
pub fn insert(&mut self, another: &Self, idx: usize) {
todo!()
}
/// Reverse the list in a chunk of size `n`.
/// If `n == 0`, do nothing.
///
/// # Examples
///
/// `self`: `[1, 2, 3, 4, 5, 6, 7, 8, 9]`, `n`: `3`
/// // each chunk of size `3`: `[1, 2, 3]`, `[4, 5, 6]`, `[7, 8, 9]`
/// // reversed sequence of chunks: `[7, 8, 9]`, `[4, 5, 6]`, `[1, 2, 3]`
/// ==> `[7, 8, 9, 4, 5, 6, 1, 2, 3]`,
///
/// `self`: `[1, 2, 3, 4, 5, 6, 7, 8, 9]`, `n`: `4`
/// // each chunk of size `4`: `[1, 2, 3, 4]`, `[5, 6, 7, 8]`, `[9]`
/// // reversed sequence of chunks: `[9]`, `[5, 6, 7, 8]`, `[1, 2, 3, 4]`
/// ==> `[9, 5, 6, 7, 8, 1, 2, 3, 4]`
pub fn chunk_reverse(&mut self, n: usize) {
todo!()
}
/// Apply given function `f` for each adjacent pair of elements in the list.
/// If `self.length() < 2`, do nothing.
///
/// # Examples
///
/// `self`: `[1, 2, 3, 4]`, `f`: `|x, y| x + y`
/// // each adjacent pair of elements: `(1, 2)`, `(2, 3)`, `(3, 4)`
/// // apply `f` to each pair: `f(1, 2) == 3`, `f(2, 3) == 5`, `f(3, 4) == 7`
/// ==> `[3, 5, 7]`
pub fn pair_map<F: Fn(T, T) -> T>(&mut self, f: F) {
todo!()
}
}
// A list of lists.
impl<T: Debug> SinglyLinkedList<SinglyLinkedList<T>> {
/// Flatten the list of lists into a single list.
///
/// # Examples
/// `self`: `[[1, 2, 3], [4, 5, 6], [7, 8]]`
/// ==> `[1, 2, 3, 4, 5, 6, 7, 8]`
pub fn flatten(self) -> SinglyLinkedList<T> {
todo!()
}
}

View File

@@ -0,0 +1,122 @@
#[cfg(test)]
mod test_linked_list {
use crate::assignments::assignment11::linked_list::*;
#[derive(Debug, PartialEq, Eq)]
struct V(usize);
#[test]
fn test_push_pop() {
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);
}
#[test]
fn test_from_as_vec() {
assert_eq!(SinglyLinkedList::<i32>::new().as_vec(), vec![]);
assert_eq!(
SinglyLinkedList::from_vec(vec![1, 2, 3]).as_vec(),
vec![1, 2, 3]
);
}
#[test]
fn test_length() {
let list = SinglyLinkedList::from_vec(vec![1, 2, 3]);
assert_eq!(list.length(), 3);
}
#[test]
fn test_map() {
let mut list = SinglyLinkedList::from_vec(vec![1, 2, 3]);
let incr = |x: i32| x + 1;
list.map(incr);
assert_eq!(list.as_vec(), vec![2, 3, 4]);
}
#[test]
fn test_insert() {
let mut list1 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
let mut list2 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
let mut list3 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
let list4 = SinglyLinkedList::from_vec(vec![4, 5, 6]);
list1.insert(&list4, 0);
assert_eq!(list1.as_vec(), vec![4, 5, 6, 1, 2, 3]);
list2.insert(&list4, 1);
assert_eq!(list2.as_vec(), vec![1, 4, 5, 6, 2, 3]);
list3.insert(&list4, 4);
assert_eq!(list3.as_vec(), vec![1, 2, 3, 4, 5, 6]);
}
#[test]
fn test_chunk_reverse() {
let mut list1 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
list1.chunk_reverse(3);
assert_eq!(list1.as_vec(), vec![7, 8, 9, 3, 4, 5, 1, 2, 3]);
let mut list2 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4, 5, 6, 7, 8]);
list2.chunk_reverse(3);
assert_eq!(list2.as_vec(), vec![7, 8, 4, 5, 6, 1, 2, 3]);
let mut list3 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
list3.chunk_reverse(4);
assert_eq!(list3.as_vec(), vec![1, 2, 3]);
let mut list4 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4]);
list4.chunk_reverse(1);
assert_eq!(list4.as_vec(), vec![4, 3, 2, 1]);
let mut list5 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4]);
list4.chunk_reverse(0);
assert_eq!(list4.as_vec(), vec![1, 2, 3, 4]);
}
#[test]
fn test_pair_map() {
let mut list = SinglyLinkedList::from_vec(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
let add = |x: i32, y: i32| x + y;
list.pair_map(add);
assert_eq!(list.as_vec(), vec![3, 5, 7, 9, 11, 13, 15, 17]);
list.pair_map(add);
assert_eq!(list.as_vec(), vec![8, 12, 16, 20, 24, 28, 32]);
list.pair_map(add);
assert_eq!(list.as_vec(), vec![20, 28, 36, 44, 52, 60]);
list.pair_map(add);
assert_eq!(list.as_vec(), vec![48, 64, 80, 96, 112]);
}
#[test]
fn test_flatten() {
let list1 = SinglyLinkedList::from_vec(vec![1, 2]);
let list2 = SinglyLinkedList::from_vec(vec![3]);
let list3 = SinglyLinkedList::from_vec(vec![4, 5, 6, 7]);
let list4 = SinglyLinkedList::<i32>::new();
let list5 = SinglyLinkedList::from_vec(vec![8, 9, 10]);
let list_list = SinglyLinkedList::from_vec(vec![list1, list2, list3, list4, list5]);
assert_eq!(
list_list.flatten().as_vec(),
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
);
}
}

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 crate::assignments::assignment11::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

@@ -1,9 +1,15 @@
//! Assignment 11: Familiarizing with smart pointers.
//!
//! You should fill out `todo!()` placeholders in such a way that `/scripts/grade-11.sh` works fine.
//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script.
//! 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 graph;
pub mod linked_list;
pub mod mock_storage;
pub mod tv_room;
mod graph_grade;
mod linked_list_grade;
mod mock_storage_grade;
mod tv_room_grade;

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 crate::assignments::assignment11::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

@@ -1,81 +0,0 @@
#[cfg(test)]
mod test {
#[test]
fn test_tv_room() {
use crate::assignments::assignment11::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());
}
#[test]
fn test_mock_storage() {
use crate::assignments::assignment11::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());
}
#[derive(Debug, PartialEq, Eq)]
struct V(usize);
#[test]
fn test_linked_list() {
use crate::assignments::assignment11::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

@@ -0,0 +1,52 @@
//! Flipping card game.
//!
//! HINT: 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,142 @@
//! Test cases for assignment12/card.rs
#[cfg(test)]
mod test_card {
use crate::assignments::assignment12::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 crate::assignments::assignment12::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 crate::assignments::assignment12::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,16 @@
//! 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 demux;
pub mod funnel;
pub mod small_exercises;
mod card_grade;
mod demux_grade;
mod funnel_grade;
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

@@ -1,6 +1,8 @@
//! Test cases for assignment12/small_exercises_grade.rs
#[cfg(test)]
mod test {
use super::super::assignment12::*;
mod test_pingpong {
use crate::assignments::assignment12::small_exercises::*;
use ntest::timeout;
use std::sync::mpsc::channel;

View File

@@ -1,48 +0,0 @@
//! Assignment 13: Parallelism.
//!
//! The primary goal of this assignment is to get used to data parallelism.
//!
//! Refer to your solution for assignment 09. You will implement the parallelized version of assignment 09.
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-13.sh` works fine.
//! See `assignment13_grade.rs` and `/scripts/grade-13.sh` for the test script.
use rayon::prelude::*;
/// Returns the sum of `f(v)` for all element `v` the given array.
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment13::sigma;
/// use rayon::iter::IntoParallelIterator;
///
/// assert_eq!(sigma([1, 2].into_par_iter(), |x| x + 2), 7);
/// assert_eq!(sigma([1, 2].into_par_iter(), |x| x * 4), 12);
/// ```
pub fn sigma<T, F: Fn(T) -> i64 + Sync + Send>(
inner: impl ParallelIterator<Item = T>,
f: F,
) -> i64 {
todo!()
}
/// Alternate elements from three iterators until they have run out.
///
/// # Example
///
/// ```
/// use cs220::assignments::assignment13::interleave3;
/// use rayon::iter::IntoParallelIterator;
///
/// assert_eq!(
/// interleave3([1, 2].into_par_iter(), [3, 4].into_par_iter(), [5, 6].into_par_iter()),
/// vec![1, 3, 5, 2, 4, 6]
/// );
/// ```
pub fn interleave3<T: Send>(
list1: impl IndexedParallelIterator<Item = T>,
list2: impl IndexedParallelIterator<Item = T>,
list3: impl IndexedParallelIterator<Item = T>,
) -> Vec<T> {
todo!()
}

View File

@@ -0,0 +1,10 @@
//! Assignment 13: Parallelism.
//!
//! The primary goal of this assignment is to get used to data parallelism.
//!
//! Refer to your solution for assignment 09. You will implement the parallelized version of assignment 09.
//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-13.sh` works fine.
//! See `assignment13/small_exercises_grade.rs` and `/scripts/grade-13.sh` for the test script.
pub mod small_exercises;
mod small_exercises_grade;

Some files were not shown because too many files have changed in this diff Show More