Add assignment 11

This commit is contained in:
Minseong Jang
2022-11-22 23:29:53 +09:00
parent 3351e8d330
commit 2ecc63770e
8 changed files with 393 additions and 0 deletions

33
scripts/grade-11.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -e
set -uo pipefail
IFS=$'\n\t'
# Imports library.
BASEDIR=$(dirname "$0")
source $BASEDIR/grade-utils.sh
RUNNERS=(
"cargo"
"cargo --release"
"cargo_asan"
"cargo_asan --release"
"cargo_tsan"
"cargo_tsan --release"
)
# Lints.
run_linters || exit 1
# Executes test for each runner.
for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..."
TESTS=("--lib assignment11_grade")
if [ $(run_tests) -ne 0 ]; then
exit 1
fi
done
exit 0

View File

@@ -7,3 +7,4 @@ BASEDIR=$(dirname "$0")/..
mkdir -p $BASEDIR/target
zip -rj $BASEDIR/target/assignment04.zip src/assignments/assignment04
zip -rj $BASEDIR/target/assignment11.zip src/assignments/assignment11

View File

@@ -0,0 +1,62 @@
//! Singly linked list.
//!
//! Consult <https://doc.rust-lang.org/book/ch15-01-box.html>.
use std::fmt::Debug;
/// Node of the list.
#[derive(Debug)]
pub struct Node<T: Debug> {
/// Value of current node.
pub value: T,
/// Pointer to the next node. If it is `None`, there is no next node.
pub next: Option<Box<Node<T>>>,
}
impl<T: Debug> Node<T> {
/// Creates a new node.
pub fn new(value: T) -> Self {
Self { value, next: None }
}
}
/// A singly-linked list.
#[derive(Debug)]
pub struct SinglyLinkedList<T: Debug> {
/// Head node of the list. If it is `None`, the list is empty.
head: Option<Node<T>>,
}
impl<T: Debug> Default for SinglyLinkedList<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Debug> SinglyLinkedList<T> {
/// Creates a new list.
pub fn new() -> Self {
Self { head: None }
}
/// Adds the given node to the front of the list.
pub fn push_front(&mut self, value: T) {
todo!()
}
/// Adds the given node to the back of the list.
pub fn push_back(&mut self, value: T) {
todo!()
}
/// Removes and returns the node at the front of the list.
pub fn pop_front(&mut self) -> Option<T> {
todo!()
}
/// Removes and returns the node at the back of the list.
pub fn pop_back(&mut self) -> Option<T> {
todo!()
}
}

View File

@@ -0,0 +1,97 @@
//! Mock storage.
//!
//! Consult <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#a-use-case-for-interior-mutability-mock-objects>.
use std::cell::RefCell;
use std::collections::HashMap;
/// Mock storage.
#[derive(Debug)]
pub struct MockStorage {
/// Files stored in the storage.
///
/// Each entry of the hashmap represents the `(name, size)` of the file.
files: RefCell<HashMap<String, usize>>,
/// Capacity of the storage.
///
/// The total size of files stored on the storage cannot exceed the capacity.
capacity: usize,
}
impl MockStorage {
/// Creates a new mock storage.
pub fn new(capacity: usize) -> Self {
Self {
files: RefCell::new(HashMap::new()),
capacity,
}
}
}
/// Trait for storage object.
pub trait Storage {
/// Uploads a file. If a file with the same name already exists in the storage, overwrite it.
///
/// Returns `Err` with insufficient memory size if there is no free space to upload a file.
fn upload(&self, name: &str, size: usize) -> Result<(), usize>;
/// Returns the used memory size of the storage.
fn used(&self) -> usize;
/// Returns the capacity of the storage.
fn capacity(&self) -> usize;
}
impl Storage for MockStorage {
fn upload(&self, name: &str, size: usize) -> Result<(), usize> {
todo!()
}
fn used(&self) -> usize {
todo!()
}
fn capacity(&self) -> usize {
todo!()
}
}
/// File uploader.
///
/// It uploads files to the internal storage.
#[derive(Debug)]
pub struct FileUploader<'a, T: Storage> {
storage: &'a T,
}
impl<'a, T: Storage> FileUploader<'a, T> {
/// Creates a new file uploader with given internal storage.
pub fn new(storage: &'a T) -> Self {
Self { storage }
}
/// Uploads a file to the internal storage.
pub fn upload(&self, name: &str, size: usize) -> Result<(), usize> {
todo!()
}
}
/// Storage usage analyzer.
#[derive(Debug)]
pub struct UsageAnalyzer<'a, T: Storage> {
storage: &'a T,
bound: f64,
}
impl<'a, T: Storage> UsageAnalyzer<'a, T> {
/// Creates a new usage analyzer.
pub fn new(storage: &'a T, bound: f64) -> Self {
Self { storage, bound }
}
/// Returns `true` if the usage of the internal storage is under the bound.
pub fn is_usage_under_bound(&self) -> bool {
todo!()
}
}

View File

@@ -0,0 +1,9 @@
//! Assignment 11: Familiarizing with smart pointers.
//!
//! You should fill out `todo!()` placeholders in such a way that `/scripts/grade-11.sh` works fine.
//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script.
//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment11.zip` to <https://gg.kaist.ac.kr>.
pub mod linked_list;
pub mod mock_storage;
pub mod tv_room;

View File

@@ -0,0 +1,108 @@
//! TV Room Simulator.
//!
//! People can come to the TV room and watch TV. There are two types of TV watchers, manager and guest.
//!
//! The rule of the TV room is as follows:
//!
//! - Closed TV room can be opened by the manager.
//! - Guests can enter the TV room by the manager.
//! - Manager can leave the TV room earlier than guests.
//! - The TV room closes when the last person left the TV room.
//!
//! Both `Manager` and `Guest` have 'Rc<Watcher>' as a field, and its reference count indicates the number of people in
//! the TV room. When the 'Manager' and 'Guest' object is dropped, it means that the person leaves the TV room.
//!
//! Consult the following documentations:
//! - <https://doc.rust-lang.org/book/ch15-04-rc.html#rct-the-reference-counted-smart-pointer>
//! - <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#having-multiple-owners-of-mutable-data-by-combining-rct-and-refcellt>
use std::{cell::RefCell, rc::Rc};
#[derive(Debug, Clone, Copy)]
enum TVRoomState {
Opened,
Closed,
}
/// TV Room
#[derive(Debug)]
pub struct TVRoom {
/// Indicates whether the TV room is state.
state: RefCell<TVRoomState>,
}
impl Default for TVRoom {
fn default() -> Self {
Self::new()
}
}
impl TVRoom {
/// Creates a new TV room.
///
/// Initial state of the TV room is closed.
pub fn new() -> Self {
Self {
state: RefCell::new(TVRoomState::Closed),
}
}
/// Opens the TV room and returns the manager.
///
/// Returns `None` if the TV room is already opened.
pub fn open(&self) -> Option<Manager<'_>> {
todo!()
}
/// Returns whether the TV room is opened or not.
pub fn is_opened(&self) -> bool {
todo!()
}
}
/// TV Room Manager.
///
/// - The manager is special TV's watcher that has privileges to add other guests.
/// - If all watchers including the manager drop (~= leave the TV room), the TV must be turned off.
/// - Note that the manager can be dropped while other watchers are watching TV.
#[derive(Debug)]
pub struct Manager<'a> {
inner: Rc<Watcher<'a>>,
}
impl<'a> Manager<'a> {
fn new(tvstate: &'a RefCell<TVRoomState>) -> Self {
Self {
inner: Rc::new(Watcher::new(tvstate)),
}
}
/// Adds new guest to the TV room.
pub fn new_guest(&self) -> Guest<'a> {
todo!()
}
}
/// TV Room Guest.
#[derive(Debug)]
pub struct Guest<'a> {
inner: Rc<Watcher<'a>>,
}
#[derive(Debug)]
struct Watcher<'a> {
tvstate: &'a RefCell<TVRoomState>,
}
impl<'a> Watcher<'a> {
fn new(tvstate: &'a RefCell<TVRoomState>) -> Self {
Self { tvstate }
}
}
impl Drop for Watcher<'_> {
fn drop(&mut self) {
// When the last person leaves the TV room, the TV room should be closed.
todo!()
}
}

View File

@@ -0,0 +1,81 @@
#[cfg(test)]
mod test {
#[test]
fn test_tv_room() {
use crate::assignments::assignment11::tv_room::*;
let tv_room = TVRoom::new();
assert!(!tv_room.is_opened());
// Turn on and add new guests.
let manager = tv_room.open().unwrap();
assert!(tv_room.is_opened());
let guest1 = manager.new_guest();
let guest2 = manager.new_guest();
drop(manager);
drop(guest1);
assert!(tv_room.open().is_none());
drop(guest2);
assert!(!tv_room.is_opened());
// Turn on and add new guests.
let manager = tv_room.open().unwrap();
assert!(tv_room.is_opened());
let guest3 = manager.new_guest();
drop(guest3);
assert!(tv_room.is_opened());
drop(manager);
assert!(!tv_room.is_opened());
}
#[test]
fn test_mock_storage() {
use crate::assignments::assignment11::mock_storage::*;
let mock_storage = MockStorage::new(100);
let uploader1 = FileUploader::new(&mock_storage);
let uploader2 = FileUploader::new(&mock_storage);
let usage_analyzer = UsageAnalyzer::new(&mock_storage, 0.75);
assert!(uploader1.upload("file1.txt", 20).is_ok());
assert!(usage_analyzer.is_usage_under_bound());
assert!(uploader2.upload("file2.txt", 30).is_ok());
assert!(usage_analyzer.is_usage_under_bound());
assert!(uploader1.upload("file3.txt", 40).is_ok());
assert!(!usage_analyzer.is_usage_under_bound());
assert_eq!(uploader2.upload("file4.txt", 50), Err(40));
assert!(!usage_analyzer.is_usage_under_bound());
assert!(uploader1.upload("file3.txt", 10).is_ok());
assert!(usage_analyzer.is_usage_under_bound());
}
#[derive(Debug, PartialEq, Eq)]
struct V(usize);
#[test]
fn test_linked_list() {
use crate::assignments::assignment11::linked_list::*;
let mut list = SinglyLinkedList::new();
list.push_back(V(3));
list.push_front(V(2));
list.push_back(V(4));
list.push_front(V(1));
list.push_back(V(5));
assert_eq!(list.pop_front(), Some(V(1)));
assert_eq!(list.pop_back(), Some(V(5)));
assert_eq!(list.pop_front(), Some(V(2)));
assert_eq!(list.pop_back(), Some(V(4)));
assert_eq!(list.pop_front(), Some(V(3)));
assert_eq!(list.pop_back(), None);
assert_eq!(list.pop_front(), None);
}
}

View File

@@ -24,3 +24,5 @@ pub mod assignment09;
mod assignment09_grade;
pub mod assignment10;
mod assignment10_grade;
pub mod assignment11;
mod assignment11_grade;