mirror of
https://github.com/kmc7468/cs220.git
synced 2025-12-12 21:08:45 +00:00
copy-paste assignment 2~5
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
|
||||
|
||||
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.
|
||||
|
||||
c. Change the code to use a for loop instead of a while loop.
|
||||
b. Select a correct variant to prove the termination.
|
||||
|
||||
*)
|
||||
|
||||
module FactRecursive
|
||||
@@ -29,9 +23,9 @@ 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
|
||||
|
||||
module FactLoop
|
||||
@@ -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
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
|
||||
|
||||
The following program sorts an array of Boolean values, with False<True.
|
||||
|
||||
E.g.
|
||||
two_way_sorted [True; False; False; True; False]
|
||||
= [False; False; False; True; True]
|
||||
|
||||
Questions:
|
||||
|
||||
1. Prove safety i.e. the absence of array access out of bounds.
|
||||
|
||||
2. Prove termination.
|
||||
|
||||
3. Prove that array a is sorted after execution of function two_way_sort
|
||||
(using the predicate sorted that is provided).
|
||||
|
||||
4. Show that after execution the array contents is a permutation of its
|
||||
initial contents. Use the library predicate "permut_all" to do so
|
||||
(the corresponding module ArrayPermut is already imported).
|
||||
|
||||
You can refer to the contents of array a at the beginning of the
|
||||
function with notation "a at Init".
|
||||
- Strengthen the 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
])))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
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