diff --git a/Cargo.lock b/Cargo.lock index 7132052..cf2e00a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", +] diff --git a/Cargo.toml b/Cargo.toml index 280b42e..d5e48c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index 3752b2a..917cea3 100644 --- a/README.md +++ b/README.md @@ -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/) diff --git a/assets/why3/binary_search.mlw b/assets/why3/assignment05/binary_search.mlw similarity index 98% rename from assets/why3/binary_search.mlw rename to assets/why3/assignment05/binary_search.mlw index e00e19f..17cd0c9 100644 --- a/assets/why3/binary_search.mlw +++ b/assets/why3/assignment05/binary_search.mlw @@ -19,4 +19,5 @@ module BinarySearch = (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *) 0 (* TODO *) -end + +end \ No newline at end of file diff --git a/assets/why3/assignment05/max.mlw b/assets/why3/assignment05/max.mlw new file mode 100644 index 0000000..49c168e --- /dev/null +++ b/assets/why3/assignment05/max.mlw @@ -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 \ No newline at end of file diff --git a/assets/why3/assignment05/pascal.mlw b/assets/why3/assignment05/pascal.mlw new file mode 100644 index 0000000..c3cd28f --- /dev/null +++ b/assets/why3/assignment05/pascal.mlw @@ -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 \ No newline at end of file diff --git a/assets/why3/ex1_eucl_div.mlw b/assets/why3/ex1_eucl_div.mlw deleted file mode 100644 index 00e7d58..0000000 --- a/assets/why3/ex1_eucl_div.mlw +++ /dev/null @@ -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 diff --git a/assets/why3/exercises/ex1_eucl_div.mlw b/assets/why3/exercises/ex1_eucl_div.mlw new file mode 100644 index 0000000..24fa3af --- /dev/null +++ b/assets/why3/exercises/ex1_eucl_div.mlw @@ -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 \ No newline at end of file diff --git a/assets/why3/exercises/ex2_fact.mlw b/assets/why3/exercises/ex2_fact.mlw new file mode 100644 index 0000000..bef4c59 --- /dev/null +++ b/assets/why3/exercises/ex2_fact.mlw @@ -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 \ No newline at end of file diff --git a/assets/why3/exercises/ex3_two_way.mlw b/assets/why3/exercises/ex3_two_way.mlw new file mode 100644 index 0000000..836722e --- /dev/null +++ b/assets/why3/exercises/ex3_two_way.mlw @@ -0,0 +1,51 @@ +(* Two Way Sort + + The following program sorts an array of Boolean values, with False 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 \ No newline at end of file diff --git a/assets/why3/exercises/solutions/ex1_eucl_div_sol.mlw b/assets/why3/exercises/solutions/ex1_eucl_div_sol.mlw new file mode 100644 index 0000000..f8580e7 --- /dev/null +++ b/assets/why3/exercises/solutions/ex1_eucl_div_sol.mlw @@ -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 \ No newline at end of file diff --git a/assets/why3/ex2_fact.mlw b/assets/why3/exercises/solutions/ex2_fact_sol.mlw similarity index 58% rename from assets/why3/ex2_fact.mlw rename to assets/why3/exercises/solutions/ex2_fact_sol.mlw index 343ae67..cfd514b 100644 --- a/assets/why3/ex2_fact.mlw +++ b/assets/why3/exercises/solutions/ex2_fact_sol.mlw @@ -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 \ No newline at end of file diff --git a/assets/why3/ex4_two_way.mlw b/assets/why3/exercises/solutions/ex3_two_way_sol.mlw similarity index 58% rename from assets/why3/ex4_two_way.mlw rename to assets/why3/exercises/solutions/ex3_two_way_sol.mlw index bfbe00e..72a6bb7 100644 --- a/assets/why3/ex4_two_way.mlw +++ b/assets/why3/exercises/solutions/ex3_two_way_sol.mlw @@ -1,22 +1,12 @@ (* Two Way Sort The following program sorts an array of Boolean values, with False 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 \ No newline at end of file diff --git a/rust-toolchain b/rust-toolchain index 832e9af..df484cb 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.70.0 +1.71.0 diff --git a/scripts/grade-01.sh b/scripts/grade-01.sh index 5579366..4add88a 100755 --- a/scripts/grade-01.sh +++ b/scripts/grade-01.sh @@ -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 diff --git a/scripts/grade-02.sh b/scripts/grade-02.sh index 570f0e4..2d50a5c 100755 --- a/scripts/grade-02.sh +++ b/scripts/grade-02.sh @@ -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 diff --git a/scripts/grade-03.sh b/scripts/grade-03.sh index bce3607..4e9f47e 100755 --- a/scripts/grade-03.sh +++ b/scripts/grade-03.sh @@ -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 diff --git a/scripts/grade-04.sh b/scripts/grade-04.sh index a5d1bfc..2f0fdc7 100755 --- a/scripts/grade-04.sh +++ b/scripts/grade-04.sh @@ -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 diff --git a/scripts/grade-06.sh b/scripts/grade-06.sh index d139dcb..bd89d59 100755 --- a/scripts/grade-06.sh +++ b/scripts/grade-06.sh @@ -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 diff --git a/scripts/grade-07.sh b/scripts/grade-07.sh index e323e09..766286d 100755 --- a/scripts/grade-07.sh +++ b/scripts/grade-07.sh @@ -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 diff --git a/scripts/grade-08.sh b/scripts/grade-08.sh index 99b7b27..815f90d 100755 --- a/scripts/grade-08.sh +++ b/scripts/grade-08.sh @@ -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 diff --git a/scripts/grade-09.sh b/scripts/grade-09.sh index 8120c66..fc449ea 100755 --- a/scripts/grade-09.sh +++ b/scripts/grade-09.sh @@ -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 diff --git a/scripts/grade-10.sh b/scripts/grade-10.sh index a221be6..87897bb 100755 --- a/scripts/grade-10.sh +++ b/scripts/grade-10.sh @@ -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 diff --git a/scripts/grade-11.sh b/scripts/grade-11.sh index 8666182..34fb51a 100755 --- a/scripts/grade-11.sh +++ b/scripts/grade-11.sh @@ -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 diff --git a/scripts/grade-12.sh b/scripts/grade-12.sh index 6a87f98..3c1609e 100755 --- a/scripts/grade-12.sh +++ b/scripts/grade-12.sh @@ -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 diff --git a/scripts/grade-13.sh b/scripts/grade-13.sh index 6c180d4..c490775 100755 --- a/scripts/grade-13.sh +++ b/scripts/grade-13.sh @@ -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 diff --git a/src/assignments/assignment01/mod.rs b/src/assignments/assignment01/mod.rs new file mode 100644 index 0000000..dfcc07f --- /dev/null +++ b/src/assignments/assignment01/mod.rs @@ -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; diff --git a/src/assignments/assignment01.rs b/src/assignments/assignment01/small_exercises.rs similarity index 60% rename from src/assignments/assignment01.rs rename to src/assignments/assignment01/small_exercises.rs index 058e21a..a85884d 100644 --- a/src/assignments/assignment01.rs +++ b/src/assignments/assignment01/small_exercises.rs @@ -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: diff --git a/src/assignments/assignment01_grade.rs b/src/assignments/assignment01/small_exercises_grade.rs similarity index 86% rename from src/assignments/assignment01_grade.rs rename to src/assignments/assignment01/small_exercises_grade.rs index 3580df9..3039dee 100644 --- a/src/assignments/assignment01_grade.rs +++ b/src/assignments/assignment01/small_exercises_grade.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod test { - use super::super::assignment01::*; + use crate::assignments::assignment01::small_exercises::*; #[test] fn test_add_7_3() { diff --git a/src/assignments/assignment02.rs b/src/assignments/assignment02.rs deleted file mode 100644 index e0944eb..0000000 --- a/src/assignments/assignment02.rs +++ /dev/null @@ -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 for computation of binomial coefficients without integer overflow. -pub fn chooses(n: u64) -> Vec { - 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, rhs: Vec) -> 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 for Mat2 { - type Output = Mat2; - - fn mul(self, rhs: Mat2) -> Self::Output { - todo!() - } -} - -impl Mul 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 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!() -} diff --git a/src/assignments/assignment02/mod.rs b/src/assignments/assignment02/mod.rs new file mode 100644 index 0000000..282c04c --- /dev/null +++ b/src/assignments/assignment02/mod.rs @@ -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; diff --git a/src/assignments/assignment02/small_exercises.rs b/src/assignments/assignment02/small_exercises.rs new file mode 100644 index 0000000..8f962b7 --- /dev/null +++ b/src/assignments/assignment02/small_exercises.rs @@ -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 for computation of binomial coefficients without integer overflow. +pub fn chooses(n: u64) -> Vec { + 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, rhs: Vec) -> Vec<(u64, u64)> { + todo!() +} diff --git a/src/assignments/assignment02_grade.rs b/src/assignments/assignment02/small_exercises_grade.rs similarity index 56% rename from src/assignments/assignment02_grade.rs rename to src/assignments/assignment02/small_exercises_grade.rs index de75737..cca1426 100644 --- a/src/assignments/assignment02_grade.rs +++ b/src/assignments/assignment02/small_exercises_grade.rs @@ -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 -"#; } diff --git a/src/assignments/assignment02/vec_and_mat.rs b/src/assignments/assignment02/vec_and_mat.rs new file mode 100644 index 0000000..0054fff --- /dev/null +++ b/src/assignments/assignment02/vec_and_mat.rs @@ -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 for Mat2 { + type Output = Mat2; + + /// Consult + fn mul(self, rhs: Mat2) -> Self::Output { + todo!() + } +} + +impl Mul for Mat2 { + type Output = Vec2; + + /// Multiplies the matrix by the vector. + /// + /// Consult + 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 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: + /// + /// # 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!() + } +} diff --git a/src/assignments/assignment02/vec_and_mat_grade.rs b/src/assignments/assignment02/vec_and_mat_grade.rs new file mode 100644 index 0000000..bf47fa7 --- /dev/null +++ b/src/assignments/assignment02/vec_and_mat_grade.rs @@ -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 + } + ); + } +} diff --git a/src/assignments/assignment03/custom_operators.rs b/src/assignments/assignment03/custom_operators.rs new file mode 100644 index 0000000..137547b --- /dev/null +++ b/src/assignments/assignment03/custom_operators.rs @@ -0,0 +1,79 @@ +//! You will implement a number of custom operators. + +/// Custom option type. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MyOption { + /// Some value of type `T`. + MySome(T), + /// No value. + MyNone, +} + +/// Maps an `MyOption` to `MyOption` by applying a function to a contained value. +/// +/// # Examples +/// +/// Converts an `MyOption` into an `MyOption`, 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 U>(v: MyOption, f: F) -> MyOption { + 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 { +/// 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 MyOption>(v: MyOption, f: F) -> MyOption { + 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>( + v1: MyOption, + v2: MyOption, + f: F, +) -> MyOption { + todo!() +} diff --git a/src/assignments/assignment03/custom_operators_grade.rs b/src/assignments/assignment03/custom_operators_grade.rs new file mode 100644 index 0000000..c2dda4f --- /dev/null +++ b/src/assignments/assignment03/custom_operators_grade.rs @@ -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::, is_positive), MyNone); + } + + #[test] + fn test_my_and_then() { + fn plus_one(x: isize) -> MyOption { + MySome(x + 1) + } + + fn none(_: isize) -> MyOption { + 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)); + } +} diff --git a/src/assignments/assignment03/mod.rs b/src/assignments/assignment03/mod.rs new file mode 100644 index 0000000..1c950e0 --- /dev/null +++ b/src/assignments/assignment03/mod.rs @@ -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; diff --git a/src/assignments/assignment03/parse_shell.rs b/src/assignments/assignment03/parse_shell.rs new file mode 100644 index 0000000..1016149 --- /dev/null +++ b/src/assignments/assignment03/parse_shell.rs @@ -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 { + todo!() +} diff --git a/src/assignments/assignment03/parse_shell_grade.rs b/src/assignments/assignment03/parse_shell_grade.rs new file mode 100644 index 0000000..d359860 --- /dev/null +++ b/src/assignments/assignment03/parse_shell_grade.rs @@ -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(), + ] + ); + } +} diff --git a/src/assignments/assignment03.rs b/src/assignments/assignment03/small_exercises.rs similarity index 60% rename from src/assignments/assignment03.rs rename to src/assignments/assignment03/small_exercises.rs index 68d22da..ed9d703 100644 --- a/src/assignments/assignment03.rs +++ b/src/assignments/assignment03/small_exercises.rs @@ -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 { - /// Some value of type `T`. - MySome(T), - /// No value. - MyNone, -} - -/// Maps an `MyOption` to `MyOption` by applying a function to a contained value. -/// -/// # Examples -/// -/// Converts an `MyOption` into an `MyOption`, 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 U>(v: MyOption, f: F) -> MyOption { - 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 { -/// 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 MyOption>(v: MyOption, f: F) -> MyOption { - 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) -> HashMap> { 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) -> String { + todo!() +} diff --git a/src/assignments/assignment03_grade.rs b/src/assignments/assignment03/small_exercises_grade.rs similarity index 76% rename from src/assignments/assignment03_grade.rs rename to src/assignments/assignment03/small_exercises_grade.rs index f72f64b..45c4c57 100644 --- a/src/assignments/assignment03_grade.rs +++ b/src/assignments/assignment03/small_exercises_grade.rs @@ -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::, is_positive), MyNone); - } - - #[test] - fn test_my_and_then() { - use MyOption::*; - - fn plus_one(x: isize) -> MyOption { - MySome(x + 1) - } - - fn none(_: isize) -> MyOption { - 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 + ]), + "" + ); + } } diff --git a/src/assignments/assignment04_grade.rs b/src/assignments/assignment04/grade.rs similarity index 99% rename from src/assignments/assignment04_grade.rs rename to src/assignments/assignment04/grade.rs index 64ddf35..c37073e 100644 --- a/src/assignments/assignment04_grade.rs +++ b/src/assignments/assignment04/grade.rs @@ -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() { diff --git a/src/assignments/assignment04/mod.rs b/src/assignments/assignment04/mod.rs index 7c6a681..ceb0a8c 100644 --- a/src/assignments/assignment04/mod.rs +++ b/src/assignments/assignment04/mod.rs @@ -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 . pub mod context; +mod grade; pub mod parser; pub mod syntax; diff --git a/src/assignments/assignment06/mod.rs b/src/assignments/assignment06/mod.rs new file mode 100644 index 0000000..8727bc1 --- /dev/null +++ b/src/assignments/assignment06/mod.rs @@ -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; diff --git a/src/assignments/assignment06.rs b/src/assignments/assignment06/semiring.rs similarity index 69% rename from src/assignments/assignment06.rs rename to src/assignments/assignment06/semiring.rs index e9a9646..57cb5bb 100644 --- a/src/assignments/assignment06.rs +++ b/src/assignments/assignment06/semiring.rs @@ -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 Semiring for Polynomial { } } -impl From for Polynomial { - fn from(value: C) -> Self { - todo!() - } -} - impl Polynomial { /// Constructs polynomial `x`. pub fn x() -> Self { @@ -139,4 +128,41 @@ impl Polynomial { pub fn eval(&self, value: C) -> C { todo!() } + + /// Constructs polynomial `ax^n`. + pub fn term(a: C, n: u64) -> Self { + todo!() + } +} + +impl From for Polynomial { + fn from(value: C) -> Self { + todo!() + } +} + +/// Given a string `s`, parse it into a `Polynomial`. +/// 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 std::str::FromStr for Polynomial { + type Err = (); // Ignore this for now... + + fn from_str(s: &str) -> Result { + todo!() + } } diff --git a/src/assignments/assignment06_grade.rs b/src/assignments/assignment06/semiring_grade.rs similarity index 72% rename from src/assignments/assignment06_grade.rs rename to src/assignments/assignment06/semiring_grade.rs index 60339f6..3b553a6 100644 --- a/src/assignments/assignment06_grade.rs +++ b/src/assignments/assignment06/semiring_grade.rs @@ -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::>().unwrap(); + for i in 0..10 { + assert_eq!(poly.eval(i), f(i)); + } + } fn test_polynomial() { // 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::(); diff --git a/src/assignments/assignment06/symbolic_differentiation.rs b/src/assignments/assignment06/symbolic_differentiation.rs new file mode 100644 index 0000000..2269d13 --- /dev/null +++ b/src/assignments/assignment06/symbolic_differentiation.rs @@ -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 + 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 + 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 + 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 + 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 { + /// Basic functions + Func(F), + /// Addition + Add(Box>, Box>), + /// Subtraction + Sub(Box>, Box>), + /// Multipliciation + Mul(Box>, Box>), + /// Division + Div(Box>, Box>), + /// Composition + Comp(Box>, Box>), +} + +impl Differentiable for Box { + fn diff(&self) -> Self { + todo!() + } +} + +impl Differentiable for ComplexFuncs { + /// HINT: Consult + 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 Evaluate for ComplexFuncs { + 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 fmt::Display for ComplexFuncs { + 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})"), + } + } +} diff --git a/src/assignments/assignment06/symbolic_differentiation_grade.rs b/src/assignments/assignment06/symbolic_differentiation_grade.rs new file mode 100644 index 0000000..26ce172 --- /dev/null +++ b/src/assignments/assignment06/symbolic_differentiation_grade.rs @@ -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; + + // 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); + } +} diff --git a/src/assignments/assignment07.rs b/src/assignments/assignment07.rs deleted file mode 100644 index e1e4753..0000000 --- a/src/assignments/assignment07.rs +++ /dev/null @@ -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 Iterator for FindIter<'_, T> { - type Item = usize; - - fn next(&mut self) -> Option { - 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 { - FindIter { - query, - base, - curr: 0, - } -} diff --git a/src/assignments/assignment07/generator.rs b/src/assignments/assignment07/generator.rs new file mode 100644 index 0000000..57f8172 --- /dev/null +++ b/src/assignments/assignment07/generator.rs @@ -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 { + 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 { + state: S, + f: fn(&mut S) -> Yielded, +} + +impl Iterator for Generator { + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Returns a generator that yields fibonacci numbers. +/// +/// HINT: Consult +pub fn fib_generator(first: usize, second: usize) -> Generator { + todo!() +} + +/// Returns a generator that yields collatz numbers. +/// +/// HINT: Consult +pub fn collatz_conjecture(start: usize) -> Generator { + todo!() +} diff --git a/src/assignments/assignment07/generator_grade.rs b/src/assignments/assignment07/generator_grade.rs new file mode 100644 index 0000000..6ac4323 --- /dev/null +++ b/src/assignments/assignment07/generator_grade.rs @@ -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![0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + ); + + assert_eq!( + collatz_conjecture(12).collect::>(), + vec![12, 6, 3, 10, 5, 16, 8, 4, 2, 1] + ); + + assert_eq!( + collatz_conjecture(19).collect::>(), + 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![ + 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 + ] + ); + } +} diff --git a/src/assignments/assignment07/mod.rs b/src/assignments/assignment07/mod.rs new file mode 100644 index 0000000..0dbfcd7 --- /dev/null +++ b/src/assignments/assignment07/mod.rs @@ -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; diff --git a/src/assignments/assignment07/my_itertools.rs b/src/assignments/assignment07/my_itertools.rs new file mode 100644 index 0000000..9e2efcf --- /dev/null +++ b/src/assignments/assignment07/my_itertools.rs @@ -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 { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData, +} + +impl Iterator for Unique +where + I::Item: Eq + Hash + Clone, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Iterator that chains two iterators together. +#[allow(missing_debug_implementations)] +pub struct Chain { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData<(I1, I2)>, +} + +impl, I2: Iterator> Iterator + for Chain +{ + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Iterator that iterates over given iterator and enumerates each element. +#[allow(missing_debug_implementations)] +pub struct Enumerate { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData, +} + +impl Iterator for Enumerate { + type Item = (usize, I::Item); + + fn next(&mut self) -> Option { + 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 { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData<(I1, I2)>, +} + +impl Iterator for Zip { + type Item = (I1::Item, I2::Item); + + fn next(&mut self) -> Option { + 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 + where + Self: Sized, + { + todo!() + } + + /// Returns an iterator that chains `self` and `other` together. + fn my_chain(self, other: I) -> Chain + where + Self: Sized, + { + todo!() + } + + /// Returns an iterator that iterates over `self` and enumerates each element. + fn my_enumerate(self) -> Enumerate + where + Self: Sized, + { + todo!() + } + + /// Returns an iterator that zips `self` and `other` together. + fn my_zip(self, other: I) -> Zip + where + Self: Sized, + { + todo!() + } + + /// Foldleft for `MyIterTools` + fn my_fold(mut self, init: T, mut f: F) -> T + where + Self: Sized, + F: FnMut(Self::Item, T) -> T, + { + todo!() + } +} + +impl MyIterTools for T where T: Iterator {} diff --git a/src/assignments/assignment07/my_itertools_grade.rs b/src/assignments/assignment07/my_itertools_grade.rs new file mode 100644 index 0000000..d6e5111 --- /dev/null +++ b/src/assignments/assignment07/my_itertools_grade.rs @@ -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![10, 1, 2, 3] + ); + + assert_eq!( + std::iter::repeat(5) + .my_enumerate() + .map(|(i, e)| { i * e }) + .take(5) + .collect::>(), + 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::>(), + ); + + 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::>(), + take15 + ); + + assert_eq!( + it().take(15).my_fold(0, |elt, acc| elt + acc), + take15.iter().sum() + ); + } +} diff --git a/src/assignments/assignment07/small_exercises.rs b/src/assignments/assignment07/small_exercises.rs new file mode 100644 index 0000000..ece7f41 --- /dev/null +++ b/src/assignments/assignment07/small_exercises.rs @@ -0,0 +1,119 @@ +//! Implement functions usint `Iterator` trait + +struct FindIter<'s, T: Eq> { + query: &'s [T], + base: &'s [T], + curr: usize, +} + +impl Iterator for FindIter<'_, T> { + type Item = usize; + + fn next(&mut self) -> Option { + 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 { + FindIter { + query, + base, + curr: 0, + } +} + +/// Implement generic fibonacci iterator +struct FibIter { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData, +} + +impl + Copy> FibIter { + fn new(first: T, second: T) -> Self { + todo!() + } +} + +impl Iterator for FibIter +where + T: std::ops::Add + Copy, +{ + type Item = T; + + fn next(&mut self) -> Option { + 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(first: T, second: T) -> impl Iterator +where + T: std::ops::Add + 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 { + todo!() + } +} + +/// Returns an iterator over the range [left, right) with the given step. +pub fn range(left: Endpoint, right: Endpoint, step: isize) -> impl Iterator { + 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 { + todo!() + } +} + +/// Returns an iterator over the divisors of n. +pub fn divisors(n: u64) -> impl Iterator { + Divisors { + n, + // TODO: you may define additional fields here + } +} diff --git a/src/assignments/assignment07/small_exercises_grade.rs b/src/assignments/assignment07/small_exercises_grade.rs new file mode 100644 index 0000000..841d811 --- /dev/null +++ b/src/assignments/assignment07/small_exercises_grade.rs @@ -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![0, 4] + ); + + assert_eq!( + find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::>(), + vec![0, 9, 12] + ); + + assert_eq!( + find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::>(), + vec![] + ); + + assert_eq!( + find("ababc".as_bytes(), "abc".as_bytes()).collect::>(), + vec![] + ); + } + + #[test] + fn test_find_usize() { + assert_eq!( + find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::>(), + 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![0, 9, 12] + ); + } + + #[test] + fn test_fib_iter() { + assert_eq!( + fib(0, 1).take(10).collect::>(), + 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![ + 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![1, 2, 5, 10]); + + assert_eq!(divisors(17).collect::>(), vec![1, 17]); + + assert_eq!(divisors(49).collect::>(), vec![1, 7, 49]); + + assert_eq!( + divisors(120).collect::>(), + vec![1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120] + ); + + assert_eq!(divisors(1).collect::>(), vec![1]); + + assert_eq!(divisors(2).collect::>(), vec![1, 2]); + + assert_eq!(divisors(3).collect::>(), vec![1, 3]); + } + + #[test] + fn test_large() { + assert_eq!( + divisors(1_000_000_000_000_037).collect::>(), + vec![1, 1_000_000_000_000_037] + ); + + assert_eq!( + divisors(99_999_820_000_081).collect::>(), + vec![1, 9_999_991, 99_999_820_000_081] + ); + + assert_eq!( + divisors(1_234_567_890_123).collect::>(), + 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); + } +} diff --git a/src/assignments/assignment07/transform.rs b/src/assignments/assignment07/transform.rs new file mode 100644 index 0000000..e24588d --- /dev/null +++ b/src/assignments/assignment07/transform.rs @@ -0,0 +1,95 @@ +//! Tranformer +use std::marker::PhantomData; +use std::ops::Add; + +/// Represents transformation of type `T`. +pub trait Transform { + /// Transforms value. + fn transform(&self, value: T) -> T; +} + +impl, Tr2: Transform> 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 Transform for Identity { + fn transform(&self, value: T) -> T { + todo!() + } +} + +/// Custom transformation. +#[derive(Debug, Clone, Copy)] +pub struct Custom T> { + f: F, + _marker: PhantomData, +} + +impl T> From for Custom { + fn from(f: F) -> Self { + Self { + f, + _marker: PhantomData, + } + } +} + +impl T> Transform for Custom { + fn transform(&self, value: T) -> T { + todo!() + } +} + +/// Repeats transformation for `n` times. +#[derive(Debug, Clone, Copy)] +pub struct Repeat> { + inner: Tr, + n: u32, + _marker: PhantomData, +} + +impl> Repeat { + /// Creates a new repeat transformation. + pub fn new(inner: Tr, n: u32) -> Self { + Repeat { + inner, + n, + _marker: PhantomData, + } + } +} + +impl> Transform for Repeat { + fn transform(&self, mut value: T) -> T { + todo!() + } +} + +/// Repeats transformation until converges. +#[derive(Debug, Clone, Copy)] +pub struct RepeatUntilConverge> { + inner: Tr, + _marker: PhantomData, +} + +impl> RepeatUntilConverge { + /// Creates a new repeat transformation. + pub fn new(inner: Tr) -> Self { + RepeatUntilConverge { + inner, + _marker: PhantomData, + } + } +} + +impl> Transform for RepeatUntilConverge { + fn transform(&self, mut value: T) -> T { + todo!() + } +} diff --git a/src/assignments/assignment07/transform_grade.rs b/src/assignments/assignment07/transform_grade.rs new file mode 100644 index 0000000..4355964 --- /dev/null +++ b/src/assignments/assignment07/transform_grade.rs @@ -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); + } +} diff --git a/src/assignments/assignment07_grade.rs b/src/assignments/assignment07_grade.rs deleted file mode 100644 index 1ecf90c..0000000 --- a/src/assignments/assignment07_grade.rs +++ /dev/null @@ -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![0, 4] - ); - - assert_eq!( - find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::>(), - vec![0, 9, 12] - ); - - assert_eq!( - find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::>(), - vec![] - ); - - assert_eq!( - find("ababc".as_bytes(), "abc".as_bytes()).collect::>(), - vec![] - ); - } - - #[test] - fn test_find_usize() { - assert_eq!( - find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::>(), - 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![0, 9, 12] - ); - } -} diff --git a/src/assignments/assignment08/church.rs b/src/assignments/assignment08/church.rs new file mode 100644 index 0000000..251fda4 --- /dev/null +++ b/src/assignments/assignment08/church.rs @@ -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: +//! - +//! - + +use std::rc::Rc; + +/// Church numerals are represented as higher-order functions that take a function `f` +pub type Church = Rc T>) -> Rc 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() -> Church { + 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() -> Church { + 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() -> Church { + Rc::new(|_| Rc::new(|x| x)) +} + +/// Implement a function to add 1 to a given Church numeral. +pub fn succ(n: Church) -> Church { + todo!() +} + +/// Implement a function to add two Church numerals. +pub fn add(n: Church, m: Church) -> Church { + todo!() +} + +/// Implement a function to multiply (mult) two Church numerals. +pub fn mult(n: Church, m: Church) -> Church { + 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(n: usize, m: usize) -> Church { + // 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(n: Church) -> usize { + todo!() +} + +/// Implement a function to convert a usize type to a Church numeral. +pub fn from_usize(n: usize) -> Church { + todo!() +} diff --git a/src/assignments/assignment08/church_grade.rs b/src/assignments/assignment08/church_grade.rs new file mode 100644 index 0000000..1df5db4 --- /dev/null +++ b/src/assignments/assignment08/church_grade.rs @@ -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::(); + 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) -> Church { + 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)); + } + } +} diff --git a/src/assignments/assignment08/mod.rs b/src/assignments/assignment08/mod.rs new file mode 100644 index 0000000..4125a1a --- /dev/null +++ b/src/assignments/assignment08/mod.rs @@ -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; diff --git a/src/assignments/assignment08.rs b/src/assignments/assignment08/small_exercises.rs similarity index 62% rename from src/assignments/assignment08.rs rename to src/assignments/assignment08/small_exercises.rs index 972b1d3..829d5a7 100644 --- a/src/assignments/assignment08.rs +++ b/src/assignments/assignment08/small_exercises.rs @@ -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>(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: F, vs: Vec) -> Vec { 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: 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 { /// Case 1. @@ -39,6 +57,8 @@ impl Either2 { /// 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(self, f1: F1, f2: F2) -> Either2 where F1: FnOnce(T1) -> U1, diff --git a/src/assignments/assignment08/small_exercises_grade.rs b/src/assignments/assignment08/small_exercises_grade.rs new file mode 100644 index 0000000..dfcb550 --- /dev/null +++ b/src/assignments/assignment08/small_exercises_grade.rs @@ -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::::Case1 { inner: 42 }; + let u1 = Either2::::Case1 { inner: 43 }; + assert_eq!(u1, v1.map(|i| i + 1, |f| f + 1.0)); + + let v2 = Either2::::Case2 { inner: 42.0 }; + let u2 = Either2::::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); + } +} diff --git a/src/assignments/assignment08_grade.rs b/src/assignments/assignment08_grade.rs deleted file mode 100644 index e6299ed..0000000 --- a/src/assignments/assignment08_grade.rs +++ /dev/null @@ -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::::Case1 { inner: 42 }; - let u1 = Either2::::Case1 { inner: 43 }; - assert_eq!(u1, v1.map(|i| i + 1, |f| f + 1.0)); - - let v2 = Either2::::Case2 { inner: 42.0 }; - let u2 = Either2::::Case2 { inner: 43.0 }; - assert_eq!(u2, v2.map(|i| i + 1, |f| f + 1.0)); - } -} diff --git a/src/assignments/assignment09/bigint.rs b/src/assignments/assignment09/bigint.rs new file mode 100644 index 0000000..33a96e5 --- /dev/null +++ b/src/assignments/assignment09/bigint.rs @@ -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, +} + +impl BigInt { + /// Create a new `BigInt` from a `usize`. + pub fn new(n: u32) -> Self { + todo!() + } + + /// Creates a new `BigInt` from a `Vec`. + pub fn new_large(carrier: Vec) -> 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(()) + } +} diff --git a/src/assignments/assignment09/bigint_grade.rs b/src/assignments/assignment09/bigint_grade.rs new file mode 100644 index 0000000..02be492 --- /dev/null +++ b/src/assignments/assignment09/bigint_grade.rs @@ -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. + } +} diff --git a/src/assignments/assignment09/matmul.rs b/src/assignments/assignment09/matmul.rs new file mode 100644 index 0000000..dd21a25 --- /dev/null +++ b/src/assignments/assignment09/matmul.rs @@ -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 { + todo!() +} + +/// dot product of two arrays +/// +/// You don't know how to calculate dot product? +/// See +/// +/// # 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 +/// +/// 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], rhs: &[Vec]) -> Vec> { + todo!() +} diff --git a/src/assignments/assignment09/matmul_grade.rs b/src/assignments/assignment09/matmul_grade.rs new file mode 100644 index 0000000..83efd14 --- /dev/null +++ b/src/assignments/assignment09/matmul_grade.rs @@ -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: + /// 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(v: Vec>) -> Array2 { + 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::>() + .as_slice(), + mat2_transposed + .axis_iter(Axis(0)) + .map(|row| row.to_vec()) + .collect::>() + .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); + } + } + } +} diff --git a/src/assignments/assignment09/mod.rs b/src/assignments/assignment09/mod.rs new file mode 100644 index 0000000..9fa8940 --- /dev/null +++ b/src/assignments/assignment09/mod.rs @@ -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; diff --git a/src/assignments/assignment09.rs b/src/assignments/assignment09/small_exercises.rs similarity index 79% rename from src/assignments/assignment09.rs rename to src/assignments/assignment09/small_exercises.rs index 38dec0e..b2950a4 100644 --- a/src/assignments/assignment09.rs +++ b/src/assignments/assignment09/small_exercises.rs @@ -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( 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( + mut iters: [impl Iterator; N], +) -> impl Iterator { + todo!(); + std::iter::empty() +} + /// Returns mean of k smallest value's mean. /// /// # Example @@ -172,3 +186,26 @@ pub fn find_count_n(inner: Vec, n: usize) -> Vec { pub fn position_median(inner: Vec) -> Option { 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>) -> 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 . +pub fn is_palindrome(s: String) -> bool { + todo!() +} diff --git a/src/assignments/assignment09_grade.rs b/src/assignments/assignment09/small_exercises_grade.rs similarity index 77% rename from src/assignments/assignment09_grade.rs rename to src/assignments/assignment09/small_exercises_grade.rs index ed8c293..be4e085 100644 --- a/src/assignments/assignment09_grade.rs +++ b/src/assignments/assignment09/small_exercises_grade.rs @@ -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![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![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::(), + "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())); + } } diff --git a/src/assignments/assignment10/labyrinth.rs b/src/assignments/assignment10/labyrinth.rs new file mode 100644 index 0000000..0b9e8e1 --- /dev/null +++ b/src/assignments/assignment10/labyrinth.rs @@ -0,0 +1,48 @@ +//! Labyrinth +//! +//! Look at the `labyrinth_grade.rs` below before you start. +//! HINT: +//! +//! 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 { + todo!() + } +} diff --git a/src/assignments/assignment10/labyrinth_grade.rs b/src/assignments/assignment10/labyrinth_grade.rs new file mode 100644 index 0000000..934e5ae --- /dev/null +++ b/src/assignments/assignment10/labyrinth_grade.rs @@ -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; + + struct Labyrinth { + rooms: Rooms, + } + + impl From 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... 🪓*/) + })); + } +} diff --git a/src/assignments/assignment10/mod.rs b/src/assignments/assignment10/mod.rs new file mode 100644 index 0000000..7fc988f --- /dev/null +++ b/src/assignments/assignment10/mod.rs @@ -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; diff --git a/src/assignments/assignment10.rs b/src/assignments/assignment10/small_exercises.rs similarity index 54% rename from src/assignments/assignment10.rs rename to src/assignments/assignment10/small_exercises.rs index 6e3e510..4cb8f74 100644 --- a/src/assignments/assignment10.rs +++ b/src/assignments/assignment10/small_exercises.rs @@ -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) { + 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) { + 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`) from table1 and a row(`Vec`) 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>, table2: Vec>) -> Vec> { + todo!() +} + +struct Pythagorean; + +impl Pythagorean { + fn new() -> Self { + todo!() + } +} + +impl Iterator for Pythagorean { + type Item = (u64, u64, u64); + + fn next(&mut self) -> Option { + 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 { + Pythagorean::new() +} diff --git a/src/assignments/assignment10/small_exercises_grade.rs b/src/assignments/assignment10/small_exercises_grade.rs new file mode 100644 index 0000000..8cbc96f --- /dev/null +++ b/src/assignments/assignment10/small_exercises_grade.rs @@ -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 = HashSet::from_iter(vec); + let set2: HashSet = HashSet::from_iter(vec![1, 2, 3, 7, 5]); + assert_eq!(set1, set2); + } + + #[test] + fn test_natural_join() { + let row1: Vec = vec!["20230001", "Jack"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20231234", "Mike"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table1 = vec![row1, row2]; + let row1: Vec = vec!["20230001", "CS"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "EE"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20231234", "ME"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table2 = vec![row1, row2, row3]; + let row1: Vec = vec!["20230001", "Jack", "CS"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "Jack", "EE"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20231234", "Mike", "ME"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table3 = vec![row1, row2, row3]; + + assert_eq!( + HashSet::>::from_iter(natural_join(table1, table2)), + HashSet::>::from_iter(table3) + ); + + let row1: Vec = vec!["20230001", "Alice"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230002", "Bob"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20230003", "Charlie"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row4: Vec = vec!["20230004", "David"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table1 = vec![row1, row2, row3, row4]; + let row1: Vec = vec!["20230001", "Apple"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "Avocado"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20230002", "Banana"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row4: Vec = vec!["20230002", "Berries"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row5: Vec = vec!["20230004", "Durian"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table2 = vec![row1, row2, row3, row4, row5]; + let row1: Vec = vec!["20230001", "Alice", "Apple"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "Alice", "Avocado"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20230002", "Bob", "Banana"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row4: Vec = vec!["20230002", "Bob", "Berries"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row5: Vec = vec!["20230004", "David", "Durian"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table3 = vec![row1, row2, row3, row4, row5]; + + assert_eq!( + HashSet::>::from_iter(natural_join(table1, table2)), + HashSet::>::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); + } + } +} diff --git a/src/assignments/assignment10_grade.rs b/src/assignments/assignment10_grade.rs deleted file mode 100644 index e7c8b5d..0000000 --- a/src/assignments/assignment10_grade.rs +++ /dev/null @@ -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) - ] - ); - } -} diff --git a/src/assignments/assignment11/graph.rs b/src/assignments/assignment11/graph.rs new file mode 100644 index 0000000..7c3ca6f --- /dev/null +++ b/src/assignments/assignment11/graph.rs @@ -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 { + 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 { + 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!() + } +} diff --git a/src/assignments/assignment11/graph_grade.rs b/src/assignments/assignment11/graph_grade.rs new file mode 100644 index 0000000..a7ea493 --- /dev/null +++ b/src/assignments/assignment11/graph_grade.rs @@ -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::>(); + 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(); + } + } +} diff --git a/src/assignments/assignment11/linked_list.rs b/src/assignments/assignment11/linked_list.rs index 5d772e3..161a7a4 100644 --- a/src/assignments/assignment11/linked_list.rs +++ b/src/assignments/assignment11/linked_list.rs @@ -59,4 +59,82 @@ impl SinglyLinkedList { pub fn pop_back(&mut self) -> Option { todo!() } + + /// Create a new list from the given vector `vec`. + pub fn from_vec(vec: Vec) -> Self { + todo!() + } + + /// Convert the current list into a vector. + pub fn as_vec(&self) -> Vec { + 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 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 T>(&mut self, f: F) { + todo!() + } +} + +// A list of lists. +impl SinglyLinkedList> { + /// 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 { + todo!() + } } diff --git a/src/assignments/assignment11/linked_list_grade.rs b/src/assignments/assignment11/linked_list_grade.rs new file mode 100644 index 0000000..6805972 --- /dev/null +++ b/src/assignments/assignment11/linked_list_grade.rs @@ -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::::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::::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] + ); + } +} diff --git a/src/assignments/assignment11/mock_storage.rs b/src/assignments/assignment11/mock_storage.rs index a5d9604..4e0edc2 100644 --- a/src/assignments/assignment11/mock_storage.rs +++ b/src/assignments/assignment11/mock_storage.rs @@ -1,6 +1,8 @@ //! Mock storage. //! -//! Consult . +//! Hint: Consult . +//! +//! Refer `mock_storage_grade.rs` for test cases. use std::cell::RefCell; use std::collections::HashMap; diff --git a/src/assignments/assignment11/mock_storage_grade.rs b/src/assignments/assignment11/mock_storage_grade.rs new file mode 100644 index 0000000..97d05aa --- /dev/null +++ b/src/assignments/assignment11/mock_storage_grade.rs @@ -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()); + } +} diff --git a/src/assignments/assignment11/mod.rs b/src/assignments/assignment11/mod.rs index 64b4553..1802da6 100644 --- a/src/assignments/assignment11/mod.rs +++ b/src/assignments/assignment11/mod.rs @@ -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 . +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; diff --git a/src/assignments/assignment11/tv_room.rs b/src/assignments/assignment11/tv_room.rs index a3cec53..1e9dbd0 100644 --- a/src/assignments/assignment11/tv_room.rs +++ b/src/assignments/assignment11/tv_room.rs @@ -15,6 +15,8 @@ //! Consult the following documentations: //! - //! - +//! +//! Refer `tv_room_grade.rs` for test cases. use std::{cell::RefCell, rc::Rc}; diff --git a/src/assignments/assignment11/tv_room_grade.rs b/src/assignments/assignment11/tv_room_grade.rs new file mode 100644 index 0000000..9500e1c --- /dev/null +++ b/src/assignments/assignment11/tv_room_grade.rs @@ -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()); + } +} diff --git a/src/assignments/assignment11_grade.rs b/src/assignments/assignment11_grade.rs deleted file mode 100644 index 94d2902..0000000 --- a/src/assignments/assignment11_grade.rs +++ /dev/null @@ -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); - } -} diff --git a/src/assignments/assignment12/card.rs b/src/assignments/assignment12/card.rs new file mode 100644 index 0000000..c7f6e51 --- /dev/null +++ b/src/assignments/assignment12/card.rs @@ -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, +} + +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!() + } +} diff --git a/src/assignments/assignment12/card_grade.rs b/src/assignments/assignment12/card_grade.rs new file mode 100644 index 0000000..f74d435 --- /dev/null +++ b/src/assignments/assignment12/card_grade.rs @@ -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>, + } + + 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, + } + + 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...",); + } +} diff --git a/src/assignments/assignment12/demux.rs b/src/assignments/assignment12/demux.rs new file mode 100644 index 0000000..2d98008 --- /dev/null +++ b/src/assignments/assignment12/demux.rs @@ -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 +//! +//! 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 bool> { + tx_true: Sender, + tx_false: Sender, + f: F, +} + +impl bool> DemuxSender { + /// 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> { + todo!() + } +} + +/// Demux. +pub fn demux bool>(f: F) -> (DemuxSender, Receiver, Receiver) { + todo!() +} diff --git a/src/assignments/assignment12/demux_grade.rs b/src/assignments/assignment12/demux_grade.rs new file mode 100644 index 0000000..145ff0e --- /dev/null +++ b/src/assignments/assignment12/demux_grade.rs @@ -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::(|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(); + } +} diff --git a/src/assignments/assignment12/funnel.rs b/src/assignments/assignment12/funnel.rs new file mode 100644 index 0000000..8b050e8 --- /dev/null +++ b/src/assignments/assignment12/funnel.rs @@ -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(rxs: Vec>, tx: Sender, f: F) -> JoinHandle<()> +where + T: Send + 'static, + F: Send + Sync + Fn(&T) -> bool + 'static, +{ + todo!() +} diff --git a/src/assignments/assignment12/funnel_grade.rs b/src/assignments/assignment12/funnel_grade.rs new file mode 100644 index 0000000..6672419 --- /dev/null +++ b/src/assignments/assignment12/funnel_grade.rs @@ -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::()).unzip(); + let (tx, rx) = channel::(); + 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(); + } +} diff --git a/src/assignments/assignment12/mod.rs b/src/assignments/assignment12/mod.rs new file mode 100644 index 0000000..3dd2a96 --- /dev/null +++ b/src/assignments/assignment12/mod.rs @@ -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; diff --git a/src/assignments/assignment12.rs b/src/assignments/assignment12/small_exercises.rs similarity index 60% rename from src/assignments/assignment12.rs rename to src/assignments/assignment12/small_exercises.rs index 73d4d84..121b670 100644 --- a/src/assignments/assignment12.rs +++ b/src/assignments/assignment12/small_exercises.rs @@ -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, tx2: &mut Sender) -> 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, diff --git a/src/assignments/assignment12_grade.rs b/src/assignments/assignment12/small_exercises_grade.rs similarity index 94% rename from src/assignments/assignment12_grade.rs rename to src/assignments/assignment12/small_exercises_grade.rs index ced664d..b9f59e8 100644 --- a/src/assignments/assignment12_grade.rs +++ b/src/assignments/assignment12/small_exercises_grade.rs @@ -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; diff --git a/src/assignments/assignment13.rs b/src/assignments/assignment13.rs deleted file mode 100644 index dfc7394..0000000 --- a/src/assignments/assignment13.rs +++ /dev/null @@ -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 i64 + Sync + Send>( - inner: impl ParallelIterator, - 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( - list1: impl IndexedParallelIterator, - list2: impl IndexedParallelIterator, - list3: impl IndexedParallelIterator, -) -> Vec { - todo!() -} diff --git a/src/assignments/assignment13/mod.rs b/src/assignments/assignment13/mod.rs new file mode 100644 index 0000000..3ea15d4 --- /dev/null +++ b/src/assignments/assignment13/mod.rs @@ -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; diff --git a/src/assignments/assignment13/small_exercises.rs b/src/assignments/assignment13/small_exercises.rs new file mode 100644 index 0000000..893bba0 --- /dev/null +++ b/src/assignments/assignment13/small_exercises.rs @@ -0,0 +1,114 @@ +//! Assignment 13: Parallelism. +//! +//! If you did well on assignment 09, you will do well on this assignment. +//! Take it easy! + +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_par([1, 2].into_par_iter(), |x| x + 2), 7); +/// assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x * 4), 12); +/// ``` +pub fn sigma_par i64 + Sync + Send>( + inner: impl ParallelIterator, + 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_par([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_par( + list1: impl IndexedParallelIterator, + list2: impl IndexedParallelIterator, + list3: impl IndexedParallelIterator, +) -> Vec { + todo!() +} + +/// Parallel vector addition +/// +/// # Exmaple +/// +/// ``` +/// use cs220::assignments::assignment13::vec_add_par; +/// +/// 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_par(&vec1, &vec2); +/// assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]); +/// ``` +pub fn vec_add_par(lhs: &[f64], rhs: &[f64]) -> Vec { + todo!() +} + +/// Parallel dot product of two arrays +/// +/// You don't know how to calculate dot product? +/// See +/// +/// # Exmaple +/// +/// ``` +/// use cs220::assignments::assignment13::dot_product_par; +/// +/// 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_par(&vec1, &vec2); +/// +/// assert_eq!(res, 55.0); +/// ``` +pub fn dot_product_par(lhs: &[f64], rhs: &[f64]) -> f64 { + todo!() +} + +/// Parallel Matrix multiplication +/// +/// You don't know how to multiply matrix? +/// Quite simple! See +/// +/// Assume rhs is transposed +/// - lhs: (m, n) +/// - rhs: (p, n) +/// - output: (m, p) +/// +/// # Exmaple +/// +/// ``` +/// use cs220::assignments::assignment13::matmul_par; +/// +/// 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_par(&mat1, &mat2); +/// assert_eq!(ans, res); +/// ``` +pub fn matmul_par(lhs: &[Vec], rhs: &[Vec]) -> Vec> { + todo!() +} diff --git a/src/assignments/assignment13/small_exercises_grade.rs b/src/assignments/assignment13/small_exercises_grade.rs new file mode 100644 index 0000000..494acd9 --- /dev/null +++ b/src/assignments/assignment13/small_exercises_grade.rs @@ -0,0 +1,217 @@ +#[cfg(test)] +mod test { + use crate::assignments::assignment09::matmul::*; + use crate::assignments::assignment13::small_exercises::*; + use approx::*; + use itertools::Itertools; + use ndarray::prelude::*; + use ndarray_rand::{rand_distr::Uniform, RandomExt}; + use rayon::prelude::IntoParallelIterator; + use std::time::Instant; + + #[test] + fn test_sigma_par() { + assert_eq!(sigma_par([].into_par_iter(), |x: i64| x * 2), 0); + assert_eq!(sigma_par([1].into_par_iter(), |x| x * 3), 3); + assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x + 2), 7); + assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x * 4), 12); + assert_eq!(sigma_par([1, 2, 3].into_par_iter(), |x| x * 5), 30); + + assert_eq!( + sigma_par([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.floor() + as i64), + 10 + ); + assert_eq!( + sigma_par([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.ceil() + as i64), + 13 + ); + assert_eq!( + sigma_par([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.round() + as i64), + 12 + ); + + assert_eq!( + sigma_par(["Hello,", "World!"].into_par_iter(), |x| x.len() as i64), + 12 + ); + } + + #[test] + fn test_interleave3_par() { + assert_eq!( + interleave3_par( + [1, 2].into_par_iter(), + [3, 4].into_par_iter(), + [5, 6].into_par_iter() + ), + vec![1, 3, 5, 2, 4, 6] + ); + + assert_eq!( + interleave3_par( + [1, 2, 3].into_par_iter(), + [4, 5, 6].into_par_iter(), + [7, 8, 9].into_par_iter() + ), + vec![1, 4, 7, 2, 5, 8, 3, 6, 9] + ); + + assert_eq!( + interleave3_par( + ["a", "b", "c"].into_par_iter(), + ["d", "e", "f"].into_par_iter(), + ["g", "h", "i"].into_par_iter() + ) + .into_iter() + .collect::(), + "adgbehcfi" + ); + } + + #[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 now_seq = Instant::now(); + let res_seq = vec_add(vec1.as_slice().unwrap(), vec2.as_slice().unwrap()); + let elapsed_seq = now_seq.elapsed(); + + let now_par = Instant::now(); + let res_par = vec_add_par(vec1.as_slice().unwrap(), vec2.as_slice().unwrap()); + let elapsed_par = now_par.elapsed(); + + let ans = vec1 + vec2; + assert_eq!(Array::from_vec(res_seq), ans); + assert_eq!(Array::from_vec(res_par), ans); + assert!(elapsed_par < elapsed_seq); + } + } + + #[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_seq = dot_product(&vec1, &vec2); + let res_par = dot_product_par(&vec1, &vec2); + assert_eq!(res_seq, 55.0); + assert_eq!(res_par, 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 now_seq = Instant::now(); + let res_seq = dot_product(vec1.as_slice().unwrap(), vec2.as_slice().unwrap()); + let elapsed_seq = now_seq.elapsed(); + + let now_par = Instant::now(); + let res_par = dot_product_par(vec1.as_slice().unwrap(), vec2.as_slice().unwrap()); + let elapsed_par = now_par.elapsed(); + + let _res = relative_eq!(res_seq, vec1.dot(&vec2), epsilon = f64::EPSILON); + let _res = relative_eq!(res_par, vec1.dot(&vec2), epsilon = f64::EPSILON); + assert!(elapsed_par < elapsed_seq); + } + } + + /// Reference: + /// 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(v: Vec>) -> Array2 { + 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_seq = matmul(&mat1, &mat2); + let res_par = matmul_par(&mat1, &mat2); + assert_eq!(ans, res_seq); + assert_eq!(ans, res_par); + + 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 now_seq = Instant::now(); + let res_seq = matmul( + mat1.axis_iter(Axis(0)) + .map(|row| row.to_vec()) + .collect::>() + .as_slice(), + mat2_transposed + .axis_iter(Axis(0)) + .map(|row| row.to_vec()) + .collect::>() + .as_slice(), + ); + let elapsed_seq = now_seq.elapsed(); + + // Run parallel matrix multiplication + let now_par = Instant::now(); + let res_par = matmul_par( + mat1.axis_iter(Axis(0)) + .map(|row| row.to_vec()) + .collect::>() + .as_slice(), + mat2_transposed + .axis_iter(Axis(0)) + .map(|row| row.to_vec()) + .collect::>() + .as_slice(), + ); + let elapsed_par = now_par.elapsed(); + + // Check answer + for it in ans.iter().zip(vec_to_array(res_seq).iter()) { + let (ans, seq) = it; + let _res = relative_eq!(ans, seq); + } + for it in ans.iter().zip(vec_to_array(res_par).iter()) { + let (ans, par) = it; + let _res = relative_eq!(ans, par); + } + + // Check time + // println!("Sequential: {:?}", elapsed_seq); + // println!("Parallel: {:?}", elapsed_par); + assert!(elapsed_par < elapsed_seq); + } + } +} diff --git a/src/assignments/assignment13_grade.rs b/src/assignments/assignment13_grade.rs deleted file mode 100644 index 6f560c6..0000000 --- a/src/assignments/assignment13_grade.rs +++ /dev/null @@ -1,67 +0,0 @@ -#[cfg(test)] -mod test { - use super::super::assignment13::*; - use rayon::prelude::IntoParallelIterator; - - #[test] - fn test_sigma() { - assert_eq!(sigma([].into_par_iter(), |x: i64| x * 2), 0); - assert_eq!(sigma([1].into_par_iter(), |x| x * 3), 3); - assert_eq!(sigma([1, 2].into_par_iter(), |x| x + 2), 7); - assert_eq!(sigma([1, 2].into_par_iter(), |x| x * 4), 12); - assert_eq!(sigma([1, 2, 3].into_par_iter(), |x| x * 5), 30); - - assert_eq!( - sigma([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.floor() - as i64), - 10 - ); - assert_eq!( - sigma([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.ceil() - as i64), - 13 - ); - assert_eq!( - sigma([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.round() - as i64), - 12 - ); - - assert_eq!( - sigma(["Hello,", "World!"].into_par_iter(), |x| x.len() as i64), - 12 - ); - } - - #[test] - fn test_interleave3() { - 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] - ); - - assert_eq!( - interleave3( - [1, 2, 3].into_par_iter(), - [4, 5, 6].into_par_iter(), - [7, 8, 9].into_par_iter() - ), - vec![1, 4, 7, 2, 5, 8, 3, 6, 9] - ); - - assert_eq!( - interleave3( - ["a", "b", "c"].into_par_iter(), - ["d", "e", "f"].into_par_iter(), - ["g", "h", "i"].into_par_iter() - ) - .into_iter() - .collect::(), - "adgbehcfi" - ); - } -} diff --git a/src/assignments/mod.rs b/src/assignments/mod.rs index 74e67fe..93c8839 100644 --- a/src/assignments/mod.rs +++ b/src/assignments/mod.rs @@ -7,26 +7,14 @@ #![allow(unreachable_code)] pub mod assignment01; -mod assignment01_grade; pub mod assignment02; -mod assignment02_grade; pub mod assignment03; -mod assignment03_grade; pub mod assignment04; -mod assignment04_grade; pub mod assignment06; -mod assignment06_grade; pub mod assignment07; -mod assignment07_grade; pub mod assignment08; -mod assignment08_grade; pub mod assignment09; -mod assignment09_grade; pub mod assignment10; -mod assignment10_grade; pub mod assignment11; -mod assignment11_grade; pub mod assignment12; -mod assignment12_grade; pub mod assignment13; -mod assignment13_grade;