mirror of
https://github.com/kmc7468/cs220.git
synced 2025-12-12 21:08:45 +00:00
Merge branch 'main' into 'main'
assignment 1~5 See merge request kaist-cp-class/cs220-private!15
This commit is contained in:
30
assets/why3/eucl_div.mlw
Normal file
30
assets/why3/eucl_div.mlw
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
(* Euclidean division
|
||||||
|
|
||||||
|
1. Prove correctness of euclideian divison:
|
||||||
|
`division a b` returns an integer `q` such that
|
||||||
|
`a = bq+r` and `0 <= r < b` for some `r`.
|
||||||
|
|
||||||
|
- You have to strengthen the loop invariant.
|
||||||
|
*)
|
||||||
|
|
||||||
|
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 }
|
||||||
|
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*) }
|
||||||
|
variant { r }
|
||||||
|
q <- q + 1;
|
||||||
|
r <- r - b
|
||||||
|
done;
|
||||||
|
q
|
||||||
|
|
||||||
|
end
|
||||||
@@ -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
|
|
||||||
@@ -1,23 +1,17 @@
|
|||||||
(* Two programs to compute the factorial
|
(* Two programs to compute the factorial
|
||||||
|
|
||||||
Note: function "fact" from module int.Fact (already imported)
|
|
||||||
can be used in specifications.
|
|
||||||
|
|
||||||
Questions:
|
Questions:
|
||||||
|
|
||||||
1. In module FactRecursive:
|
1. In module FactRecursive:
|
||||||
|
|
||||||
a. Prove soundness of function fact_rec.
|
a. Implement the program that satisfies specification.
|
||||||
|
|
||||||
b. Prove its termination.
|
|
||||||
|
|
||||||
2. In module FactLoop:
|
2. In module FactLoop:
|
||||||
|
|
||||||
a. Prove soundness of function fact_loop.
|
a. Strengthen the invariant to prove correctness of the given implementation.
|
||||||
|
|
||||||
b. Prove its termination.
|
b. Select a correct variant to prove the termination.
|
||||||
|
|
||||||
c. Change the code to use a for loop instead of a while loop.
|
|
||||||
*)
|
*)
|
||||||
|
|
||||||
module FactRecursive
|
module FactRecursive
|
||||||
@@ -29,9 +23,9 @@ module FactRecursive
|
|||||||
requires { n >= 0 }
|
requires { n >= 0 }
|
||||||
ensures { result = fact n }
|
ensures { result = fact n }
|
||||||
variant { n }
|
variant { n }
|
||||||
=
|
= (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
|
||||||
if n = 0 then 1 else n * fact_rec (n - 1)
|
0 (*TODO*)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module FactLoop
|
module FactLoop
|
||||||
@@ -45,8 +39,10 @@ module FactLoop
|
|||||||
= let ref m = 0 in
|
= let ref m = 0 in
|
||||||
let ref r = 1 in
|
let ref r = 1 in
|
||||||
while m < n do
|
while m < n do
|
||||||
invariant { 0 <= m <= n /\ r = fact m }
|
(* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
|
||||||
variant { n - m }
|
invariant { true (* TODO *) }
|
||||||
|
variant { n (* TODO *) }
|
||||||
|
(* IMPORTANT: DON'T MODIFY THE BELOW LINES *)
|
||||||
m <- m + 1;
|
m <- m + 1;
|
||||||
r <- r * m
|
r <- r * m
|
||||||
done;
|
done;
|
||||||
29
assets/why3/max.mlw
Normal file
29
assets/why3/max.mlw
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
(* Max
|
||||||
|
|
||||||
|
Given an array `a` of natural numbers with length `n`,
|
||||||
|
return the maximum element of the array.
|
||||||
|
|
||||||
|
You should stengthen the loop invariant.
|
||||||
|
|
||||||
|
*)
|
||||||
|
|
||||||
|
module Max
|
||||||
|
|
||||||
|
use int.Int
|
||||||
|
use ref.Ref
|
||||||
|
use array.Array
|
||||||
|
|
||||||
|
let max (a: array int) (n: int) : (max: int)
|
||||||
|
requires { n = length a }
|
||||||
|
requires { forall i. 0 <= i < n -> a[i] >= 0 }
|
||||||
|
ensures { forall i. 0 <= i < n -> a[i] <= max }
|
||||||
|
ensures { exists i. 0 <= i < n -> a[i] = max }
|
||||||
|
= let ref max = 0 in
|
||||||
|
for i = 0 to n - 1 do
|
||||||
|
(* IMPORTANT: MODIFY ONLY THIS INVARIANT, OR YOU'LL GET ZERO POINTS *)
|
||||||
|
invariant { true (*TODO*) }
|
||||||
|
if max < a[i] then max <- a[i];
|
||||||
|
done;
|
||||||
|
max
|
||||||
|
|
||||||
|
end
|
||||||
32
assets/why3/pascal.mlw
Normal file
32
assets/why3/pascal.mlw
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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 }
|
||||||
|
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. *)
|
||||||
|
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 { true (*TODO*) }
|
||||||
|
let new_row = Array.make (r+1) 1 in
|
||||||
|
for c = 1 to r-1 do
|
||||||
|
invariant { true (*TODO*) }
|
||||||
|
new_row[c] <- row[c-1] + row[c]
|
||||||
|
done;
|
||||||
|
row <- new_row
|
||||||
|
done;
|
||||||
|
row
|
||||||
|
end
|
||||||
@@ -1,22 +1,12 @@
|
|||||||
(* Two Way Sort
|
(* Two Way Sort
|
||||||
|
|
||||||
The following program sorts an array of Boolean values, with False<True.
|
The following program sorts an array of Boolean values, with False<True.
|
||||||
|
|
||||||
|
E.g.
|
||||||
|
two_way_sorted [True; False; False; True; False]
|
||||||
|
= [False; False; False; True; True]
|
||||||
|
|
||||||
Questions:
|
- Strengthen the invariants to prove correctness.
|
||||||
|
|
||||||
1. Prove safety i.e. the absence of array access out of bounds.
|
|
||||||
|
|
||||||
2. Prove termination.
|
|
||||||
|
|
||||||
3. Prove that array a is sorted after execution of function two_way_sort
|
|
||||||
(using the predicate sorted that is provided).
|
|
||||||
|
|
||||||
4. Show that after execution the array contents is a permutation of its
|
|
||||||
initial contents. Use the library predicate "permut_all" to do so
|
|
||||||
(the corresponding module ArrayPermut is already imported).
|
|
||||||
|
|
||||||
You can refer to the contents of array a at the beginning of the
|
|
||||||
function with notation "a at Init".
|
|
||||||
*)
|
*)
|
||||||
|
|
||||||
module TwoWaySort
|
module TwoWaySort
|
||||||
@@ -34,15 +24,20 @@ module TwoWaySort
|
|||||||
forall i1 i2: int. 0 <= i1 <= i2 < a.length -> a[i1] << a[i2]
|
forall i1 i2: int. 0 <= i1 <= i2 < a.length -> a[i1] << a[i2]
|
||||||
|
|
||||||
let two_way_sort (a: array bool) : unit
|
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 i = 0 in
|
||||||
let ref j = length a - 1 in
|
let ref j = length a - 1 in
|
||||||
while i < j do
|
while i < j do
|
||||||
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 { 0 <= i /\ j < length a }
|
||||||
|
(* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
|
||||||
|
invariant { forall i1: int. 0 <= i1 < i
|
||||||
|
-> true (* TODO *) }
|
||||||
|
invariant { forall i2: int. j < i2 < length a
|
||||||
|
-> true (* TODO *) }
|
||||||
|
invariant { true (* TODO *) }
|
||||||
|
(* IMPORTANT: DON'T MODIFY THE BELOW LINES *)
|
||||||
variant { j - i }
|
variant { j - i }
|
||||||
if not a[i] then
|
if not a[i] then
|
||||||
incr i
|
incr i
|
||||||
@@ -24,7 +24,7 @@ run_linters || exit 1
|
|||||||
for RUNNER in "${RUNNERS[@]}"; do
|
for RUNNER in "${RUNNERS[@]}"; do
|
||||||
echo "Running with $RUNNER..."
|
echo "Running with $RUNNER..."
|
||||||
|
|
||||||
TESTS=("--lib assignment12_grade")
|
TESTS=("--lib assignment12")
|
||||||
if [ $(run_tests) -ne 0 ]; then
|
if [ $(run_tests) -ne 0 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -135,6 +135,39 @@ pub fn fibonacci(n: u64) -> u64 {
|
|||||||
(FIBONACCI_MAT.power(n) * FIBONACCI_VEC).get_upper()
|
(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".
|
/// 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.
|
/// Hint: Google the song title for lyrics and look at the test code for the expected result.
|
||||||
|
|||||||
@@ -149,6 +149,48 @@ mod test {
|
|||||||
assert_eq!(fibonacci(92), 12200160415121876738);
|
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]
|
#[test]
|
||||||
fn test_lyrics() {
|
fn test_lyrics() {
|
||||||
assert_eq!(twelve_days_of_christmas_lyrics(), LYRICS)
|
assert_eq!(twelve_days_of_christmas_lyrics(), LYRICS)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
//! Assignment 3: Mastering common programming concepts (2/2).
|
//! 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.
|
//! 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.
|
//! See `assignment03_grade.rs` and `/scripts/grade-03.sh` for the test script.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
/// Day of week.
|
/// Day of week.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -166,3 +164,125 @@ pub fn piglatin(input: String) -> String {
|
|||||||
pub fn organize(commands: Vec<String>) -> HashMap<String, HashSet<String>> {
|
pub fn organize(commands: Vec<String>) -> HashMap<String, HashSet<String>> {
|
||||||
todo!()
|
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, F: FnOnce(T, T) -> T>(v1: Option<T>, v2: Option<T>, f: F) -> Option<T> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Events in a text editor.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TypeEvent {
|
||||||
|
/// A character is typed.
|
||||||
|
Type(char),
|
||||||
|
/// The last character is removed.
|
||||||
|
Backspace,
|
||||||
|
/// The whole string is copied to the clipboard.
|
||||||
|
Copy,
|
||||||
|
/// The string in the clipboard is appended.
|
||||||
|
Paste,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starting from an empty string and an empty clipboard,
|
||||||
|
/// processes the given `events` in order and returns the resulting string.
|
||||||
|
///
|
||||||
|
/// See the test function `test_editor` for examples.
|
||||||
|
pub fn use_editor(events: Vec<TypeEvent>) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the string as a shell command.
|
||||||
|
///
|
||||||
|
/// Usually, a shell command is whitespace-separated array of strings.
|
||||||
|
/// ```text
|
||||||
|
/// cat file --> ["cat", "file"]
|
||||||
|
/// ```
|
||||||
|
/// But sometimes, you may want to include whitespaces in each argument.
|
||||||
|
/// In that case, you can use quotes.
|
||||||
|
/// ```text
|
||||||
|
/// ls 'VirtualBox VMs' --> ["ls", 'VirtualBox VMs']
|
||||||
|
/// ls VirtualBox' 'VMs --> ["ls", 'VirtualBox VMs']
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For simplicity, you may assume that the string only contains alphanumeric characters, spaces
|
||||||
|
/// (" "), and single quotes ("'").
|
||||||
|
///
|
||||||
|
/// See `test_shell` for more examples.
|
||||||
|
pub fn parse_shell_command(command: &str) -> Vec<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<JsonValue>),
|
||||||
|
/// objects
|
||||||
|
Object(HashMap<String, JsonValue>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<JsonValue, String> {
|
||||||
|
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, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -165,4 +165,162 @@ mod test {
|
|||||||
.into()
|
.into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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!(
|
||||||
|
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
|
||||||
|
]),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
])))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
//! Singly linked list.
|
//! Singly linked list.
|
||||||
//!
|
//!
|
||||||
//! Hint: Consult <https://doc.rust-lang.org/book/ch15-01-box.html>.
|
//! Consult <https://doc.rust-lang.org/book/ch15-01-box.html>.
|
||||||
//!
|
|
||||||
//! Refer `linked_list_grade.rs` for test cases.
|
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
@@ -61,4 +59,82 @@ impl<T: Debug> SinglyLinkedList<T> {
|
|||||||
pub fn pop_back(&mut self) -> Option<T> {
|
pub fn pop_back(&mut self) -> Option<T> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new list from the given vector `vec`.
|
||||||
|
pub fn from_vec(vec: Vec<T>) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the current list into a vector.
|
||||||
|
pub fn as_vec(&self) -> Vec<T> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the length (i.e., number of nodes) of the list.
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply function `f` on every element of the list.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// `self`: `[1, 2]`, `f`: `|x| x + 1` ==> `[2, 3]`
|
||||||
|
pub fn map<F: Fn(T) -> T>(&mut self, f: F) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert given list `another` at the specified index `idx`.
|
||||||
|
/// If `idx` is out-of-bound of `self`, append `another` at the end of `self`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// `self`: `[1, 2]`, `another`: `[3, 4]`, `idx`: `1` ==> `[1, 3, 4, 2]`
|
||||||
|
/// `self`: `[1, 2]`, `another`: `[3, 4]`, `idx`: `5` ==> `[1, 2, 3, 4]`
|
||||||
|
pub fn insert(&mut self, another: &Self, idx: usize) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reverse the list in a chunk of size `n`.
|
||||||
|
/// If `n == 0`, do nothing.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// `self`: `[1, 2, 3, 4, 5, 6, 7, 8, 9]`, `n`: `3`
|
||||||
|
/// // each chunk of size `3`: `[1, 2, 3]`, `[4, 5, 6]`, `[7, 8, 9]`
|
||||||
|
/// // reversed sequence of chunks: `[7, 8, 9]`, `[4, 5, 6]`, `[1, 2, 3]`
|
||||||
|
/// ==> `[7, 8, 9, 4, 5, 6, 1, 2, 3]`,
|
||||||
|
///
|
||||||
|
/// `self`: `[1, 2, 3, 4, 5, 6, 7, 8, 9]`, `n`: `4`
|
||||||
|
/// // each chunk of size `4`: `[1, 2, 3, 4]`, `[5, 6, 7, 8]`, `[9]`
|
||||||
|
/// // reversed sequence of chunks: `[9]`, `[5, 6, 7, 8]`, `[1, 2, 3, 4]`
|
||||||
|
/// ==> `[9, 5, 6, 7, 8, 1, 2, 3, 4]`
|
||||||
|
pub fn chunk_reverse(&mut self, n: usize) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply given function `f` for each adjacent pair of elements in the list.
|
||||||
|
/// If `self.length() < 2`, do nothing.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// `self`: `[1, 2, 3, 4]`, `f`: `|x, y| x + y`
|
||||||
|
/// // each adjacent pair of elements: `(1, 2)`, `(2, 3)`, `(3, 4)`
|
||||||
|
/// // apply `f` to each pair: `f(1, 2) == 3`, `f(2, 3) == 5`, `f(3, 4) == 7`
|
||||||
|
/// ==> `[3, 5, 7]`
|
||||||
|
pub fn pair_map<F: Fn(T, T) -> T>(&mut self, f: F) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A list of lists.
|
||||||
|
impl<T: Debug> SinglyLinkedList<SinglyLinkedList<T>> {
|
||||||
|
/// Flatten the list of lists into a single list.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// `self`: `[[1, 2, 3], [4, 5, 6], [7, 8]]`
|
||||||
|
/// ==> `[1, 2, 3, 4, 5, 6, 7, 8]`
|
||||||
|
pub fn flatten(self) -> SinglyLinkedList<T> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
//! Test cases for assignment11/linked_list.rs
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_linked_list {
|
mod test_linked_list {
|
||||||
use super::super::linked_list::*;
|
use crate::assignments::assignment11::linked_list::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
struct V(usize);
|
struct V(usize);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_linked_list() {
|
fn test_push_pop() {
|
||||||
let mut list = SinglyLinkedList::new();
|
let mut list = SinglyLinkedList::new();
|
||||||
list.push_back(V(3));
|
list.push_back(V(3));
|
||||||
list.push_front(V(2));
|
list.push_front(V(2));
|
||||||
@@ -24,4 +22,101 @@ mod test_linked_list {
|
|||||||
assert_eq!(list.pop_back(), None);
|
assert_eq!(list.pop_back(), None);
|
||||||
assert_eq!(list.pop_front(), None);
|
assert_eq!(list.pop_front(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_as_vec() {
|
||||||
|
assert_eq!(SinglyLinkedList::<i32>::new().as_vec(), vec![]);
|
||||||
|
assert_eq!(
|
||||||
|
SinglyLinkedList::from_vec(vec![1, 2, 3]).as_vec(),
|
||||||
|
vec![1, 2, 3]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_length() {
|
||||||
|
let list = SinglyLinkedList::from_vec(vec![1, 2, 3]);
|
||||||
|
assert_eq!(list.length(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_map() {
|
||||||
|
let mut list = SinglyLinkedList::from_vec(vec![1, 2, 3]);
|
||||||
|
let incr = |x: i32| x + 1;
|
||||||
|
list.map(incr);
|
||||||
|
assert_eq!(list.as_vec(), vec![2, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert() {
|
||||||
|
let mut list1 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
|
||||||
|
let mut list2 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
|
||||||
|
let mut list3 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
|
||||||
|
let list4 = SinglyLinkedList::from_vec(vec![4, 5, 6]);
|
||||||
|
|
||||||
|
list1.insert(&list4, 0);
|
||||||
|
assert_eq!(list1.as_vec(), vec![4, 5, 6, 1, 2, 3]);
|
||||||
|
|
||||||
|
list2.insert(&list4, 1);
|
||||||
|
assert_eq!(list2.as_vec(), vec![1, 4, 5, 6, 2, 3]);
|
||||||
|
|
||||||
|
list3.insert(&list4, 4);
|
||||||
|
assert_eq!(list3.as_vec(), vec![1, 2, 3, 4, 5, 6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chunk_reverse() {
|
||||||
|
let mut list1 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
list1.chunk_reverse(3);
|
||||||
|
assert_eq!(list1.as_vec(), vec![7, 8, 9, 3, 4, 5, 1, 2, 3]);
|
||||||
|
|
||||||
|
let mut list2 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
list2.chunk_reverse(3);
|
||||||
|
assert_eq!(list2.as_vec(), vec![7, 8, 4, 5, 6, 1, 2, 3]);
|
||||||
|
|
||||||
|
let mut list3 = SinglyLinkedList::from_vec(vec![1, 2, 3]);
|
||||||
|
list3.chunk_reverse(4);
|
||||||
|
assert_eq!(list3.as_vec(), vec![1, 2, 3]);
|
||||||
|
|
||||||
|
let mut list4 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4]);
|
||||||
|
list4.chunk_reverse(1);
|
||||||
|
assert_eq!(list4.as_vec(), vec![4, 3, 2, 1]);
|
||||||
|
|
||||||
|
let mut list5 = SinglyLinkedList::from_vec(vec![1, 2, 3, 4]);
|
||||||
|
list4.chunk_reverse(0);
|
||||||
|
assert_eq!(list4.as_vec(), vec![1, 2, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pair_map() {
|
||||||
|
let mut list = SinglyLinkedList::from_vec(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
let add = |x: i32, y: i32| x + y;
|
||||||
|
|
||||||
|
list.pair_map(add);
|
||||||
|
assert_eq!(list.as_vec(), vec![3, 5, 7, 9, 11, 13, 15, 17]);
|
||||||
|
|
||||||
|
list.pair_map(add);
|
||||||
|
assert_eq!(list.as_vec(), vec![8, 12, 16, 20, 24, 28, 32]);
|
||||||
|
|
||||||
|
list.pair_map(add);
|
||||||
|
assert_eq!(list.as_vec(), vec![20, 28, 36, 44, 52, 60]);
|
||||||
|
|
||||||
|
list.pair_map(add);
|
||||||
|
assert_eq!(list.as_vec(), vec![48, 64, 80, 96, 112]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flatten() {
|
||||||
|
let list1 = SinglyLinkedList::from_vec(vec![1, 2]);
|
||||||
|
let list2 = SinglyLinkedList::from_vec(vec![3]);
|
||||||
|
let list3 = SinglyLinkedList::from_vec(vec![4, 5, 6, 7]);
|
||||||
|
let list4 = SinglyLinkedList::<i32>::new();
|
||||||
|
let list5 = SinglyLinkedList::from_vec(vec![8, 9, 10]);
|
||||||
|
|
||||||
|
let list_list = SinglyLinkedList::from_vec(vec![list1, list2, list3, list4, list5]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
list_list.flatten().as_vec(),
|
||||||
|
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,26 @@
|
|||||||
//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script.
|
//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script.
|
||||||
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment11.zip` to <https://gg.kaist.ac.kr>.
|
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment11.zip` to <https://gg.kaist.ac.kr>.
|
||||||
|
|
||||||
pub mod bst;
|
|
||||||
pub mod bst_grade;
|
|
||||||
pub mod doubly_linked_list;
|
|
||||||
pub mod doubly_linked_list_grade;
|
|
||||||
pub mod graph;
|
|
||||||
pub mod graph_grade;
|
|
||||||
pub mod linked_list;
|
pub mod linked_list;
|
||||||
|
mod linked_list_grade;
|
||||||
|
|
||||||
|
pub mod peano_nat;
|
||||||
|
mod peano_nat_grade;
|
||||||
|
|
||||||
|
pub mod bst;
|
||||||
|
mod bst_grade;
|
||||||
|
|
||||||
|
pub mod doubly_linked_list;
|
||||||
|
mod doubly_linked_list_grade;
|
||||||
|
|
||||||
|
pub mod graph;
|
||||||
|
mod graph_grade;
|
||||||
|
|
||||||
pub mod mock_storage;
|
pub mod mock_storage;
|
||||||
pub mod mock_storage_grade;
|
pub mod mock_storage_grade;
|
||||||
pub mod turing_machine;
|
|
||||||
pub mod turing_machine_grade;
|
|
||||||
pub mod tv_room;
|
pub mod tv_room;
|
||||||
pub mod tv_room_grade;
|
pub mod tv_room_grade;
|
||||||
|
|
||||||
|
pub mod turing_machine;
|
||||||
|
pub mod turing_machine_grade;
|
||||||
|
|||||||
56
src/assignments/assignment11/peano_nat.rs
Normal file
56
src/assignments/assignment11/peano_nat.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
//! Peano natural number.
|
||||||
|
|
||||||
|
/// We can represent any natural number using only two symbols: 0 and S.
|
||||||
|
///
|
||||||
|
/// E.g.
|
||||||
|
/// O == 0
|
||||||
|
/// S(O) == 1
|
||||||
|
/// S(S(O)) == 2
|
||||||
|
/// ... so on.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Nat {
|
||||||
|
/// Zero
|
||||||
|
O,
|
||||||
|
/// Plus one
|
||||||
|
S(Box<Nat>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Nat {
|
||||||
|
/// Create `Nat` from `usize`
|
||||||
|
pub fn from_usize(n: usize) -> Nat {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert `Nat` into nonnegative integer
|
||||||
|
pub fn as_usize(&self) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement `Add` operator (i.e. `+`) for `Nat`.
|
||||||
|
impl std::ops::Add for Nat {
|
||||||
|
type Output = Nat;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement `Sub` operator (i.e. `-`) for `Nat`.
|
||||||
|
// If the result is negative, return `Nat::O`.
|
||||||
|
impl std::ops::Sub for Nat {
|
||||||
|
type Output = Nat;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement `Mul` operator (i.e. `*`) for `Nat`.
|
||||||
|
impl std::ops::Mul for Nat {
|
||||||
|
type Output = Nat;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/assignments/assignment11/peano_nat_grade.rs
Normal file
38
src/assignments/assignment11/peano_nat_grade.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod test_peano_nat {
|
||||||
|
use crate::assignments::assignment11::peano_nat::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_as_usize() {
|
||||||
|
assert_eq!(Nat::from_usize(0), Nat::O);
|
||||||
|
assert_eq!(
|
||||||
|
Nat::from_usize(2),
|
||||||
|
Nat::S(Box::new(Nat::S(Box::new(Nat::O))))
|
||||||
|
);
|
||||||
|
|
||||||
|
for n in 0..100 {
|
||||||
|
assert_eq!(Nat::from_usize(n).as_usize(), n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn safe_sub(i: usize, j: usize) -> usize {
|
||||||
|
if i > j {
|
||||||
|
i - j
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_sub_mul() {
|
||||||
|
for i in 0..30 {
|
||||||
|
let n = Nat::from_usize(i);
|
||||||
|
for j in 0..30 {
|
||||||
|
let m = Nat::from_usize(j);
|
||||||
|
assert_eq!((n.clone() + m.clone()).as_usize(), i + j);
|
||||||
|
assert_eq!((n.clone() - m.clone()).as_usize(), safe_sub(i, j));
|
||||||
|
assert_eq!((n.clone() * m.clone()).as_usize(), i * j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user