From d28bca2b181bc72cd187879707f68fe0399a6e90 Mon Sep 17 00:00:00 2001 From: AnHaechan Date: Mon, 21 Aug 2023 07:13:27 +0000 Subject: [PATCH] assignment 1~5: fixes - assignment05/pascal.mlw: lowered the difficulty (one more invariant given) - assignment02, 03: minor fixes & divide into sub-problems --- .../why3/{ => assignment05}/binary_search.mlw | 2 +- assets/why3/{ => assignment05}/max.mlw | 6 +- assets/why3/{ => assignment05}/pascal.mlw | 15 +- .../ex1_eucl_div.mlw} | 3 +- .../why3/{fact.mlw => exercises/ex2_fact.mlw} | 6 +- .../ex3_two_way.mlw} | 8 +- .../exercises/solutions/ex1_eucl_div_sol.mlw | 30 ++ .../why3/exercises/solutions/ex2_fact_sol.mlw | 37 +++ .../exercises/solutions/ex3_two_way_sol.mlw | 49 +++ scripts/grade-02.sh | 2 +- scripts/grade-03.sh | 2 +- src/assignments/assignment02.rs | 176 ---------- src/assignments/assignment02/mod.rs | 13 + .../assignment02/small_problems.rs | 46 +++ .../assignment02/small_problems_grade.rs | 137 ++++++++ src/assignments/assignment02/vec_and_mat.rs | 121 +++++++ .../assignment02/vec_and_mat_grade.rs | 60 ++++ src/assignments/assignment02_grade.rs | 303 ------------------ src/assignments/assignment03.rs | 288 ----------------- .../assignment03/custom_operators.rs | 79 +++++ .../assignment03/custom_operators_grade.rs | 58 ++++ src/assignments/assignment03/mod.rs | 13 + src/assignments/assignment03/parse_shell.rs | 28 ++ .../assignment03/parse_shell_grade.rs | 38 +++ .../assignment03/small_problems.rs | 130 ++++++++ .../small_problems_grade.rs} | 149 +-------- src/assignments/mod.rs | 2 - 27 files changed, 863 insertions(+), 938 deletions(-) rename assets/why3/{ => assignment05}/binary_search.mlw (99%) rename assets/why3/{ => assignment05}/max.mlw (76%) rename assets/why3/{ => assignment05}/pascal.mlw (60%) rename assets/why3/{eucl_div.mlw => exercises/ex1_eucl_div.mlw} (82%) rename assets/why3/{fact.mlw => exercises/ex2_fact.mlw} (82%) rename assets/why3/{two_way.mlw => exercises/ex3_two_way.mlw} (84%) create mode 100644 assets/why3/exercises/solutions/ex1_eucl_div_sol.mlw create mode 100644 assets/why3/exercises/solutions/ex2_fact_sol.mlw create mode 100644 assets/why3/exercises/solutions/ex3_two_way_sol.mlw delete mode 100644 src/assignments/assignment02.rs create mode 100644 src/assignments/assignment02/mod.rs create mode 100644 src/assignments/assignment02/small_problems.rs create mode 100644 src/assignments/assignment02/small_problems_grade.rs create mode 100644 src/assignments/assignment02/vec_and_mat.rs create mode 100644 src/assignments/assignment02/vec_and_mat_grade.rs delete mode 100644 src/assignments/assignment02_grade.rs delete mode 100644 src/assignments/assignment03.rs create mode 100644 src/assignments/assignment03/custom_operators.rs create mode 100644 src/assignments/assignment03/custom_operators_grade.rs create mode 100644 src/assignments/assignment03/mod.rs create mode 100644 src/assignments/assignment03/parse_shell.rs create mode 100644 src/assignments/assignment03/parse_shell_grade.rs create mode 100644 src/assignments/assignment03/small_problems.rs rename src/assignments/{assignment03_grade.rs => assignment03/small_problems_grade.rs} (58%) diff --git a/assets/why3/binary_search.mlw b/assets/why3/assignment05/binary_search.mlw similarity index 99% rename from assets/why3/binary_search.mlw rename to assets/why3/assignment05/binary_search.mlw index e00e19f..931cc48 100644 --- a/assets/why3/binary_search.mlw +++ b/assets/why3/assignment05/binary_search.mlw @@ -19,4 +19,4 @@ module BinarySearch = (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *) 0 (* TODO *) -end +end \ No newline at end of file diff --git a/assets/why3/max.mlw b/assets/why3/assignment05/max.mlw similarity index 76% rename from assets/why3/max.mlw rename to assets/why3/assignment05/max.mlw index 92ef8dd..b0b858b 100644 --- a/assets/why3/max.mlw +++ b/assets/why3/assignment05/max.mlw @@ -4,7 +4,6 @@ return the maximum element of the array. You should stengthen the loop invariant. - *) module Max @@ -20,8 +19,9 @@ module Max ensures { exists i. 0 <= i < n -> a[i] = max } = let ref max = 0 in for i = 0 to n - 1 do - (* IMPORTANT: MODIFY ONLY THIS INVARIANT, OR YOU'LL GET ZERO POINTS *) - invariant { true (*TODO*) } + (* 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 diff --git a/assets/why3/pascal.mlw b/assets/why3/assignment05/pascal.mlw similarity index 60% rename from assets/why3/pascal.mlw rename to assets/why3/assignment05/pascal.mlw index 7b838ca..2d0fa93 100644 --- a/assets/why3/pascal.mlw +++ b/assets/why3/assignment05/pascal.mlw @@ -1,10 +1,14 @@ +(* 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 - (* HINT: https://en.wikipedia.org/wiki/Pascal%27s_triangle *) - (* You should understand the Pascal's triangle first to find good invariants *) let rec function comb (n k: int) : int requires { 0 <= k <= n } variant { n } @@ -12,6 +16,7 @@ module Pascal = 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. @@ -20,10 +25,12 @@ module Pascal let ref row = Array.make 1 1 in for r = 1 to n do invariant { length row = r } - invariant { true (*TODO*) } + 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 - invariant { true (*TODO*) } + (* 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 diff --git a/assets/why3/eucl_div.mlw b/assets/why3/exercises/ex1_eucl_div.mlw similarity index 82% rename from assets/why3/eucl_div.mlw rename to assets/why3/exercises/ex1_eucl_div.mlw index 27cf0d1..c4aaf20 100644 --- a/assets/why3/eucl_div.mlw +++ b/assets/why3/exercises/ex1_eucl_div.mlw @@ -11,7 +11,6 @@ module Division use int.Int - (* IMPORTANT: DON'T MODIFY LINES EXCEPT `TODO`s OR YOU WILL GET ZERO POINTS *) let division (a b: int) : int requires { a >= 0 } requires { b > 0 } @@ -20,7 +19,7 @@ module Division let ref q = 0 in let ref r = a in while r >= b do - invariant { true (*TODO*) } + invariant { true (* TODO: Replace `true` with your solution *) } variant { r } q <- q + 1; r <- r - b diff --git a/assets/why3/fact.mlw b/assets/why3/exercises/ex2_fact.mlw similarity index 82% rename from assets/why3/fact.mlw rename to assets/why3/exercises/ex2_fact.mlw index 42d995d..adad25e 100644 --- a/assets/why3/fact.mlw +++ b/assets/why3/exercises/ex2_fact.mlw @@ -39,10 +39,8 @@ module FactLoop = let ref m = 0 in let ref r = 1 in while m < n do - (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *) - invariant { true (* TODO *) } - variant { n (* TODO *) } - (* IMPORTANT: DON'T MODIFY THE BELOW LINES *) + invariant { true (* TODO: Replace `true` with your solution *) } + variant { n (* TODO: Replace `n` with your solution *) } m <- m + 1; r <- r * m done; diff --git a/assets/why3/two_way.mlw b/assets/why3/exercises/ex3_two_way.mlw similarity index 84% rename from assets/why3/two_way.mlw rename to assets/why3/exercises/ex3_two_way.mlw index 371ce71..24705bd 100644 --- a/assets/why3/two_way.mlw +++ b/assets/why3/exercises/ex3_two_way.mlw @@ -31,13 +31,11 @@ module TwoWaySort let ref j = length a - 1 in while i < j do invariant { 0 <= i /\ j < length a } - (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *) invariant { forall i1: int. 0 <= i1 < i - -> true (* TODO *) } + -> true (* TODO: Replace `true` with your solution *) } invariant { forall i2: int. j < i2 < length a - -> true (* TODO *) } - invariant { true (* TODO *) } - (* IMPORTANT: DON'T MODIFY THE BELOW LINES *) + -> 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 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/exercises/solutions/ex2_fact_sol.mlw b/assets/why3/exercises/solutions/ex2_fact_sol.mlw new file mode 100644 index 0000000..cfd514b --- /dev/null +++ b/assets/why3/exercises/solutions/ex2_fact_sol.mlw @@ -0,0 +1,37 @@ +(* Two programs to compute the factorial + +*) + +module FactRecursive + + use int.Int + use int.Fact + + let rec fact_rec (n: int) : int + requires { n >= 0 } + ensures { result = fact n } + variant { n } + = + if n = 0 then 1 else n * fact_rec (n - 1) + +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 { 0 <= m <= n /\ r = fact m } + variant { n - m } + m <- m + 1; + r <- r * m + done; + r + +end \ No newline at end of file diff --git a/assets/why3/exercises/solutions/ex3_two_way_sol.mlw b/assets/why3/exercises/solutions/ex3_two_way_sol.mlw new file mode 100644 index 0000000..72a6bb7 --- /dev/null +++ b/assets/why3/exercises/solutions/ex3_two_way_sol.mlw @@ -0,0 +1,49 @@ +(* 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 -> a[i1] = False } + invariant { forall i2: int. j < i2 < length a -> a[i2] = True } + invariant { permut_all (old a) a } + 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/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/src/assignments/assignment02.rs b/src/assignments/assignment02.rs deleted file mode 100644 index b747baf..0000000 --- a/src/assignments/assignment02.rs +++ /dev/null @@ -1,176 +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() -} - -/// 2x2 floating-point matrix of the following configuration: -/// -/// a, b -/// c, d -#[derive(Debug, Clone, Copy)] -pub struct FMat2 { - /// row 1, column 1 - pub a: f64, - /// row 1, column 2 - pub b: f64, - /// row 2, column 1 - pub c: f64, - /// row 2, column 2 - pub d: f64, -} - -impl FMat2 { - /// Returns the inverse of the given matrix. (We assume the given matrix is always invertible.) - /// Hint: https://www.cuemath.com/algebra/inverse-of-2x2-matrix/ - /// - /// # 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!() - } -} - -/// 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..86a15c7 --- /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 `*_grade.rs` and `/scripts/grade-02.sh` for the test script. + +pub mod small_problems; +mod small_problems_grade; + +pub mod vec_and_mat; +mod vec_and_mat_grade; diff --git a/src/assignments/assignment02/small_problems.rs b/src/assignments/assignment02/small_problems.rs new file mode 100644 index 0000000..8f962b7 --- /dev/null +++ b/src/assignments/assignment02/small_problems.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/small_problems_grade.rs b/src/assignments/assignment02/small_problems_grade.rs new file mode 100644 index 0000000..ae33ff1 --- /dev/null +++ b/src/assignments/assignment02/small_problems_grade.rs @@ -0,0 +1,137 @@ +#[cfg(test)] +mod test { + use crate::assignments::assignment02::small_problems::*; + + #[test] + fn test_fahrenheit() { + assert_eq!(fahrenheit_to_celsius(32.0), 0.0); + assert_eq!(fahrenheit_to_celsius(212.0), 100.0); + } + + #[test] + fn test_capitalize() { + assert_eq!( + capitalize(String::from("aAbbBcccCddddD❤한글과✓")), + String::from("AABBBCCCCDDDDD❤한글과✓"), + ); + assert_eq!(capitalize(String::from("Tschüß")), String::from("TSCHüß")); + } + + #[test] + fn test_up3() { + assert_eq!(up3(0), 1); + assert_eq!(up3(1), 1); + assert_eq!(up3(6), 9); + assert_eq!(up3(9), 9); + assert_eq!(up3(10), 27); + assert_eq!(up3(1_000_000), 1_594_323); + assert_eq!(up3(3u64.pow(39).wrapping_add(1)), 3u64.pow(40)); + assert_eq!(up3(3u64.pow(40)), 3u64.pow(40)); + } + + #[test] + fn test_gcd() { + assert_eq!(gcd(5, 1), 1); + assert_eq!(gcd(3, 3), 3); + assert_eq!(gcd(2, 6), 2); + assert_eq!(gcd(24, 18), 6); + assert_eq!(gcd(20, 63), 1); + assert_eq!(gcd(0, 33), 33); + } + + #[test] + fn test_sum_array() { + assert_eq!(sum_array(&[]), 0); + assert_eq!(sum_array(&[1]), 1); + assert_eq!(sum_array(&[1, 2, 3, 4, 5, 100]), 115); + } + + #[test] + fn test_chooses() { + assert_eq!(chooses(0), vec![1]); + assert_eq!(chooses(1), vec![1, 1]); + assert_eq!(chooses(5), vec![1, 5, 10, 10, 5, 1]); + assert_eq!(chooses(6), vec![1, 6, 15, 20, 15, 6, 1]); + assert_eq!( + chooses(67), + vec![ + 1, + 67, + 2211, + 47905, + 766480, + 9657648, + 99795696, + 869648208, + 6522361560, + 42757703560, + 247994680648, + 1285063345176, + 5996962277488, + 25371763481680, + 97862516286480, + 345780890878896, + 1123787895356412, + 3371363686069236, + 9364899127970100, + 24151581961607100, + 57963796707857040, + 129728497393775280, + 271250494550621040, + 530707489338171600, + 972963730453314600, + 1673497616379701112, + 2703342303382594104, + 4105075349580976232, + 5864393356544251760, + 7886597962249166160, + 9989690752182277136, + 11923179284862717872, + 13413576695470557606, + 14226520737620288370, + 14226520737620288370, + 13413576695470557606, + 11923179284862717872, + 9989690752182277136, + 7886597962249166160, + 5864393356544251760, + 4105075349580976232, + 2703342303382594104, + 1673497616379701112, + 972963730453314600, + 530707489338171600, + 271250494550621040, + 129728497393775280, + 57963796707857040, + 24151581961607100, + 9364899127970100, + 3371363686069236, + 1123787895356412, + 345780890878896, + 97862516286480, + 25371763481680, + 5996962277488, + 1285063345176, + 247994680648, + 42757703560, + 6522361560, + 869648208, + 99795696, + 9657648, + 766480, + 47905, + 2211, + 67, + 1 + ] + ); + } + + #[test] + fn test_zip() { + assert_eq!(zip(vec![1, 2], vec![4, 5]), vec![(1, 4), (2, 5)]); + assert_eq!(zip(vec![1, 2, 3], vec![4, 5]), vec![(1, 4), (2, 5)]); + assert_eq!(zip(vec![1, 2], vec![4, 5, 6]), vec![(1, 4), (2, 5)]); + assert_eq!(zip(vec![], vec![4, 5]), vec![]); + } +} diff --git a/src/assignments/assignment02/vec_and_mat.rs b/src/assignments/assignment02/vec_and_mat.rs new file mode 100644 index 0000000..df969b3 --- /dev/null +++ b/src/assignments/assignment02/vec_and_mat.rs @@ -0,0 +1,121 @@ +//! 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; + + fn mul(self, rhs: Mat2) -> Self::Output { + todo!() + } +} + +impl Mul for Mat2 { + type Output = Vec2; + + /// Multiplies the matrix by the vector. + 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: https://www.mathcentre.ac.uk/resources/uploaded/sigma-matrices7-2009-1.pdf + /// + /// # Example + /// + /// ``` + /// assert_eq!( + /// Mat2 { a: 1.0, b: 1.0, c: 2.0, d: 3.0 }.inverse(), + /// Mat2 { a: 3.0, b: -1.0, c: -2.0, d: 1.0} + /// ); + /// ``` + pub fn inverse(self) -> Self { + todo!() + } +} 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/assignment02_grade.rs b/src/assignments/assignment02_grade.rs deleted file mode 100644 index 6fd72ac..0000000 --- a/src/assignments/assignment02_grade.rs +++ /dev/null @@ -1,303 +0,0 @@ -#[cfg(test)] -mod test { - use super::super::assignment02::*; - - #[test] - fn test_fahrenheit() { - assert_eq!(fahrenheit_to_celsius(32.0), 0.0); - assert_eq!(fahrenheit_to_celsius(212.0), 100.0); - } - - #[test] - fn test_capitalize() { - assert_eq!( - capitalize(String::from("aAbbBcccCddddD❤한글과✓")), - String::from("AABBBCCCCDDDDD❤한글과✓"), - ); - assert_eq!(capitalize(String::from("Tschüß")), String::from("TSCHüß")); - } - - #[test] - fn test_up3() { - assert_eq!(up3(0), 1); - assert_eq!(up3(1), 1); - assert_eq!(up3(6), 9); - assert_eq!(up3(9), 9); - assert_eq!(up3(10), 27); - assert_eq!(up3(1_000_000), 1_594_323); - assert_eq!(up3(3u64.pow(39).wrapping_add(1)), 3u64.pow(40)); - assert_eq!(up3(3u64.pow(40)), 3u64.pow(40)); - } - - #[test] - fn test_gcd() { - assert_eq!(gcd(5, 1), 1); - assert_eq!(gcd(3, 3), 3); - assert_eq!(gcd(2, 6), 2); - assert_eq!(gcd(24, 18), 6); - assert_eq!(gcd(20, 63), 1); - assert_eq!(gcd(0, 33), 33); - } - - #[test] - fn test_sum_array() { - assert_eq!(sum_array(&[]), 0); - assert_eq!(sum_array(&[1]), 1); - assert_eq!(sum_array(&[1, 2, 3, 4, 5, 100]), 115); - } - - #[test] - fn test_chooses() { - assert_eq!(chooses(0), vec![1]); - assert_eq!(chooses(1), vec![1, 1]); - assert_eq!(chooses(5), vec![1, 5, 10, 10, 5, 1]); - assert_eq!(chooses(6), vec![1, 6, 15, 20, 15, 6, 1]); - assert_eq!( - chooses(67), - vec![ - 1, - 67, - 2211, - 47905, - 766480, - 9657648, - 99795696, - 869648208, - 6522361560, - 42757703560, - 247994680648, - 1285063345176, - 5996962277488, - 25371763481680, - 97862516286480, - 345780890878896, - 1123787895356412, - 3371363686069236, - 9364899127970100, - 24151581961607100, - 57963796707857040, - 129728497393775280, - 271250494550621040, - 530707489338171600, - 972963730453314600, - 1673497616379701112, - 2703342303382594104, - 4105075349580976232, - 5864393356544251760, - 7886597962249166160, - 9989690752182277136, - 11923179284862717872, - 13413576695470557606, - 14226520737620288370, - 14226520737620288370, - 13413576695470557606, - 11923179284862717872, - 9989690752182277136, - 7886597962249166160, - 5864393356544251760, - 4105075349580976232, - 2703342303382594104, - 1673497616379701112, - 972963730453314600, - 530707489338171600, - 271250494550621040, - 129728497393775280, - 57963796707857040, - 24151581961607100, - 9364899127970100, - 3371363686069236, - 1123787895356412, - 345780890878896, - 97862516286480, - 25371763481680, - 5996962277488, - 1285063345176, - 247994680648, - 42757703560, - 6522361560, - 869648208, - 99795696, - 9657648, - 766480, - 47905, - 2211, - 67, - 1 - ] - ); - } - - #[test] - fn test_zip() { - assert_eq!(zip(vec![1, 2], vec![4, 5]), vec![(1, 4), (2, 5)]); - assert_eq!(zip(vec![1, 2, 3], vec![4, 5]), vec![(1, 4), (2, 5)]); - 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); - } - - // 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 - } - ); - } - - #[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/assignment03.rs b/src/assignments/assignment03.rs deleted file mode 100644 index ce37c1f..0000000 --- a/src/assignments/assignment03.rs +++ /dev/null @@ -1,288 +0,0 @@ -//! 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. - -use std::collections::{HashMap, HashSet}; -use std::fmt; - -/// Day of week. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DayOfWeek { - /// Sunday. - Sun, - /// Monday. - Mon, - /// Tuesday. - Tue, - /// Wednesday. - Wed, - /// Thursday. - Thu, - /// Friday. - Fri, - /// Saturday. - Sat, -} - -/// The next day of week. -/// -/// `next_weekday(Thu)` is `Fri`; and `next_weekday(Fri)` is `Mon`. -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: -/// -/// - If `n` is odd, the median is `(n+1)/2`-th smallest element of `x`. -/// - If `n` is even, the median is `(n/2)+1`-th smallest element of `x`. -/// -/// For example, the following list of seven numbers, -/// -/// ```ignore -/// vec![1, 3, 3, 6, 7, 8, 9] -/// ``` -/// -/// has the median of 6, which is the fourth value. And for this data set of eight numbers, -/// -/// ```ignore -/// vec![1, 2, 3, 4, 5, 6, 8, 9] -/// ``` -/// -/// it has the median of 5, which is the fifth value. -/// -/// Returns `None` if the list is empty. -pub fn median(values: Vec) -> Option { - todo!() -} - -/// Given a list of integers, returns its smallest mode (the value that occurs most often; a hash map will be helpful here). -/// -/// Returns `None` if the list is empty. -pub fn mode(values: Vec) -> Option { - todo!() -} - -/// Converts the given string to Pig Latin. Use the rules below to translate normal English into Pig Latin. -/// -/// 1. If a word starts with a consonant and a vowel, move the first letter of the word at the end of the word and add "ay". -/// -/// Example: "happy" -> "appyh" + "ay" -> "appyhay" -/// -/// 2. If a word starts with multiple consonants, move them to the end of the word and add "ay". -/// -/// Example: "string" -> "ingstr" + "ay" -> "ingstray" -/// -/// 3. If a word starts with a vowel, add the word "hay" at the end of the word. -/// -/// Example: "explain" -> "explain" + "hay" -> "explainhay" -/// -/// Keep in mind the details about UTF-8 encoding! -/// -/// You may assume the string only contains lowercase alphabets, and it contains at least one vowel. -pub fn piglatin(input: String) -> String { - todo!() -} - -/// Converts HR commands to the organization table. -/// -/// If the commands are as follows: -/// -/// ```ignore -/// vec!["Add Amir to Engineering", "Add Sally to Sales", "Remove Jeehoon from Sales", "Move Amir from Engineering to Sales"] -/// ``` -/// -/// The return value should be: -/// -/// ```ignore -/// ["Sales" -> ["Amir", "Sally"]] -/// ``` -/// -/// - The result is a map from department to the list of its employees. -/// - An empty department should not appear in the result. -/// - There are three commands: "Add {person} to {department}", "Remove {person} from {department}", and "Move {person} from {department} to {department}". -/// - If a command is not executable, then it's ignored. -/// - There is no space in the name of the person and department. -/// -/// See the test function for more details. -pub fn organize(commands: Vec) -> HashMap> { - 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 option_op_or T>(v1: Option, v2: Option, f: F) -> Option { - 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!() -} - -/// 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!() -} - -/// Represents a JSON value. See https://en.wikipedia.org/wiki/JSON. -/// -/// For simplicity, you may assume that numbers are of type `i64`, and strings do not contain -/// special characters that need to be escaped (e.g. '"', '\n', ...). -#[derive(Debug, PartialEq, Eq)] -pub enum JsonValue { - /// null - Null, - /// true, false - Boolean(bool), - /// integers - Number(i64), - /// strings - String(String), - /// array of JSON values - Array(Vec), - /// objects - Object(HashMap), -} - -/// Parse a string into a JSON value. Returns `Err(())` if it contains syntax errors. -/// -/// See `test_json` for examples. -pub fn parse_json(json_string: &str) -> Result { - todo!() -} - -impl fmt::Display for JsonValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - JsonValue::Null => write!(f, "null"), - JsonValue::Boolean(b) => write!(f, "{}", b), - JsonValue::Number(n) => write!(f, "{}", n), - JsonValue::String(s) => write!(f, "\"{}\"", s), - JsonValue::Array(arr) => { - write!(f, "[")?; - let mut iter = arr.iter(); - if let Some(item) = iter.next() { - write!(f, "{}", item)?; - for item in iter { - write!(f, ", {}", item)?; - } - } - write!(f, "]") - } - JsonValue::Object(obj) => { - write!(f, "{{")?; - let mut iter = obj.iter(); - if let Some((key, value)) = iter.next() { - write!(f, "\"{}\": {}", key, value)?; - for (key, value) in iter { - write!(f, ", \"{}\": {}", key, value)?; - } - } - write!(f, "}}") - } - } - } -} 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..be8a24c --- /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 `*_grade.rs` and `/scripts/grade-03.sh` for the test script. + +pub mod small_problems; +mod small_problems_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/small_problems.rs b/src/assignments/assignment03/small_problems.rs new file mode 100644 index 0000000..ed9d703 --- /dev/null +++ b/src/assignments/assignment03/small_problems.rs @@ -0,0 +1,130 @@ +//! Small problems. + +use std::collections::{HashMap, HashSet}; +use std::fmt; + +/// Day of week. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DayOfWeek { + /// Sunday. + Sun, + /// Monday. + Mon, + /// Tuesday. + Tue, + /// Wednesday. + Wed, + /// Thursday. + Thu, + /// Friday. + Fri, + /// Saturday. + Sat, +} + +/// The next day of week. +/// +/// `next_weekday(Thu)` is `Fri`; and `next_weekday(Fri)` is `Mon`. +pub fn next_weekday(day: DayOfWeek) -> DayOfWeek { + 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: +/// +/// - If `n` is odd, the median is `(n+1)/2`-th smallest element of `x`. +/// - If `n` is even, the median is `(n/2)+1`-th smallest element of `x`. +/// +/// For example, the following list of seven numbers, +/// +/// ```ignore +/// vec![1, 3, 3, 6, 7, 8, 9] +/// ``` +/// +/// has the median of 6, which is the fourth value. And for this data set of eight numbers, +/// +/// ```ignore +/// vec![1, 2, 3, 4, 5, 6, 8, 9] +/// ``` +/// +/// it has the median of 5, which is the fifth value. +/// +/// Returns `None` if the list is empty. +pub fn median(values: Vec) -> Option { + todo!() +} + +/// Given a list of integers, returns its smallest mode (the value that occurs most often; a hash map will be helpful here). +/// +/// Returns `None` if the list is empty. +pub fn mode(values: Vec) -> Option { + todo!() +} + +/// Converts the given string to Pig Latin. Use the rules below to translate normal English into Pig Latin. +/// +/// 1. If a word starts with a consonant and a vowel, move the first letter of the word at the end of the word and add "ay". +/// +/// Example: "happy" -> "appyh" + "ay" -> "appyhay" +/// +/// 2. If a word starts with multiple consonants, move them to the end of the word and add "ay". +/// +/// Example: "string" -> "ingstr" + "ay" -> "ingstray" +/// +/// 3. If a word starts with a vowel, add the word "hay" at the end of the word. +/// +/// Example: "explain" -> "explain" + "hay" -> "explainhay" +/// +/// Keep in mind the details about UTF-8 encoding! +/// +/// You may assume the string only contains lowercase alphabets, and it contains at least one vowel. +pub fn piglatin(input: String) -> String { + todo!() +} + +/// Converts HR commands to the organization table. +/// +/// If the commands are as follows: +/// +/// ```ignore +/// vec!["Add Amir to Engineering", "Add Sally to Sales", "Remove Jeehoon from Sales", "Move Amir from Engineering to Sales"] +/// ``` +/// +/// The return value should be: +/// +/// ```ignore +/// ["Sales" -> ["Amir", "Sally"]] +/// ``` +/// +/// - The result is a map from department to the list of its employees. +/// - An empty department should not appear in the result. +/// - There are three commands: "Add {person} to {department}", "Remove {person} from {department}", and "Move {person} from {department} to {department}". +/// - If a command is not executable, then it's ignored. +/// - There is no space in the name of the person and department. +/// +/// See the test function for more details. +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_problems_grade.rs similarity index 58% rename from src/assignments/assignment03_grade.rs rename to src/assignments/assignment03/small_problems_grade.rs index 04980eb..21390ec 100644 --- a/src/assignments/assignment03_grade.rs +++ b/src/assignments/assignment03/small_problems_grade.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod test { - use super::super::assignment03::*; + use crate::assignments::assignment03::small_problems::*; #[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); @@ -166,18 +120,6 @@ mod test { ); } - fn product(a: i32, b: i32) -> i32 { - a * b - } - - #[test] - fn test_option_op_or() { - 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(None, Some(5), product), Some(5)); - assert_eq!(option_op_or(Some(3), Some(5), product), Some(15)); - } - #[test] fn test_editor() { assert_eq!( @@ -234,93 +176,4 @@ mod test { "" ); } - - #[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(), - ] - ); - } - - #[test] - fn test_json() { - use std::collections::HashMap; - let json_str = r#"{ - "name": "John Doe", - "age": 30, - "city": "New York", - "active": true, - "address": { - "street": "123 Main St", - "zipCode": "10001" - }, - "skills": ["Rust", "Python", "JavaScript"], - "organization": null - }"#; - println!("{}", parse_json(json_str).unwrap()); - assert_eq!( - parse_json(json_str), - Ok(JsonValue::Object(HashMap::from([ - ( - "name".to_string(), - JsonValue::String("John Doe".to_string()) - ), - ("age".to_string(), JsonValue::Number(30)), - ( - "city".to_string(), - JsonValue::String("New York".to_string()) - ), - ("active".to_string(), JsonValue::Boolean(true)), - ( - "address".to_string(), - JsonValue::Object(HashMap::from([ - ( - "street".to_string(), - JsonValue::String("123 Main St".to_string()) - ), - ( - "zipCode".to_string(), - JsonValue::String("10001".to_string()) - ) - ])) - ), - ( - "skills".to_string(), - JsonValue::Array(vec![ - JsonValue::String("Rust".to_string()), - JsonValue::String("Python".to_string()), - JsonValue::String("JavaScript".to_string()) - ]) - ), - ("organization".to_string(), JsonValue::Null), - ]))) - ); - } } diff --git a/src/assignments/mod.rs b/src/assignments/mod.rs index ecc70a2..c490e61 100644 --- a/src/assignments/mod.rs +++ b/src/assignments/mod.rs @@ -9,9 +9,7 @@ pub mod assignment01; mod assignment01_grade; pub mod assignment02; -mod assignment02_grade; pub mod assignment03; -mod assignment03_grade; pub mod assignment04; pub mod assignment06; pub mod assignment07;