Merge branch 'main' into 'main'

assignment 1~5

See merge request kaist-cp-class/cs220-private!15
This commit is contained in:
Haechan An
2023-08-21 01:51:36 +00:00
16 changed files with 764 additions and 88 deletions

30
assets/why3/eucl_div.mlw Normal file
View File

@@ -0,0 +1,30 @@
(* Euclidean division
1. Prove correctness of euclideian divison:
`division a b` returns an integer `q` such that
`a = bq+r` and `0 <= r < b` for some `r`.
- You have to strengthen the 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

View File

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

View File

@@ -1,23 +1,17 @@
(* 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.
a. Implement the program that satisfies specification.
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
@@ -29,8 +23,8 @@ module FactRecursive
requires { n >= 0 }
ensures { result = fact n }
variant { n }
=
if n = 0 then 1 else n * fact_rec (n - 1)
= (* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
0 (*TODO*)
end
@@ -45,8 +39,10 @@ module FactLoop
= let ref m = 0 in
let ref r = 1 in
while m < n do
invariant { 0 <= m <= n /\ r = fact m }
variant { n - m }
(* IMPORTANT: DON'T MODIFY THE ABOVE LINES *)
invariant { true (* TODO *) }
variant { n (* TODO *) }
(* IMPORTANT: DON'T MODIFY THE BELOW LINES *)
m <- m + 1;
r <- r * m
done;

29
assets/why3/max.mlw Normal file
View File

@@ -0,0 +1,29 @@
(* Max
Given an array `a` of natural numbers with length `n`,
return the maximum element of the array.
You should stengthen the loop invariant.
*)
module Max
use int.Int
use ref.Ref
use array.Array
let max (a: array int) (n: int) : (max: int)
requires { n = length a }
requires { forall i. 0 <= i < n -> a[i] >= 0 }
ensures { forall i. 0 <= i < n -> a[i] <= max }
ensures { exists i. 0 <= i < n -> a[i] = max }
= let ref max = 0 in
for i = 0 to n - 1 do
(* IMPORTANT: 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
View 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

View File

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

View File

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

View File

@@ -135,6 +135,39 @@ 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.

View File

@@ -149,6 +149,48 @@ mod test {
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)

View File

@@ -1,12 +1,10 @@
//! 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.
//! 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)]
@@ -166,3 +164,125 @@ pub fn piglatin(input: String) -> String {
pub fn organize(commands: Vec<String>) -> HashMap<String, HashSet<String>> {
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, "}}")
}
}
}
}

View File

@@ -165,4 +165,162 @@ mod test {
.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),
])))
);
}
}

View File

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

View File

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

View File

@@ -4,16 +4,26 @@
//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script.
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment11.zip` to <https://gg.kaist.ac.kr>.
pub mod bst;
pub mod bst_grade;
pub mod doubly_linked_list;
pub mod doubly_linked_list_grade;
pub mod graph;
pub mod graph_grade;
pub mod linked_list;
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_grade;
pub mod turing_machine;
pub mod turing_machine_grade;
pub mod tv_room;
pub mod tv_room_grade;
pub mod turing_machine;
pub mod turing_machine_grade;

View 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!()
}
}

View 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);
}
}
}
}