diff --git a/Cargo.lock b/Cargo.lock index 75ed63e..4ab1c58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,6 +227,7 @@ dependencies = [ "num-traits", "pest", "pest_derive", + "rand", "rayon", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 543cac1..f7271a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ approx = "0.5.1" num-traits = "0.2" ndarray = "0.15.0" ndarray-rand = "0.14.0" +rand = "0.8.5" diff --git a/scripts/grade-06.sh b/scripts/grade-06.sh index d139dcb..bd89d59 100755 --- a/scripts/grade-06.sh +++ b/scripts/grade-06.sh @@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0") source $BASEDIR/grade-utils.sh RUNNERS=( - "cargo" - "cargo --release" - "cargo_asan" - "cargo_asan --release" - "cargo_tsan" - "cargo_tsan --release" + "cargo" + "cargo --release" + "cargo_asan" + "cargo_asan --release" + "cargo_tsan" + "cargo_tsan --release" ) # Lints. @@ -22,12 +22,12 @@ run_linters || exit 1 # Executes test for each runner. for RUNNER in "${RUNNERS[@]}"; do - echo "Running with $RUNNER..." + echo "Running with $RUNNER..." - TESTS=("--lib assignment06_grade") - if [ $(run_tests) -ne 0 ]; then - exit 1 - fi + TESTS=("--lib assignment06") + if [ $(run_tests) -ne 0 ]; then + exit 1 + fi done exit 0 diff --git a/scripts/grade-07.sh b/scripts/grade-07.sh index e323e09..766286d 100755 --- a/scripts/grade-07.sh +++ b/scripts/grade-07.sh @@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0") source $BASEDIR/grade-utils.sh RUNNERS=( - "cargo" - "cargo --release" - "cargo_asan" - "cargo_asan --release" - "cargo_tsan" - "cargo_tsan --release" + "cargo" + "cargo --release" + "cargo_asan" + "cargo_asan --release" + "cargo_tsan" + "cargo_tsan --release" ) # Lints. @@ -22,12 +22,12 @@ run_linters || exit 1 # Executes test for each runner. for RUNNER in "${RUNNERS[@]}"; do - echo "Running with $RUNNER..." + echo "Running with $RUNNER..." - TESTS=("--lib assignment07_grade") - if [ $(run_tests) -ne 0 ]; then - exit 1 - fi + TESTS=("--lib assignment07") + if [ $(run_tests) -ne 0 ]; then + exit 1 + fi done exit 0 diff --git a/scripts/grade-09.sh b/scripts/grade-09.sh index 8120c66..fc449ea 100755 --- a/scripts/grade-09.sh +++ b/scripts/grade-09.sh @@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0") source $BASEDIR/grade-utils.sh RUNNERS=( - "cargo" - "cargo --release" - "cargo_asan" - "cargo_asan --release" - "cargo_tsan" - "cargo_tsan --release" + "cargo" + "cargo --release" + "cargo_asan" + "cargo_asan --release" + "cargo_tsan" + "cargo_tsan --release" ) # Lints. @@ -22,12 +22,12 @@ run_linters || exit 1 # Executes test for each runner. for RUNNER in "${RUNNERS[@]}"; do - echo "Running with $RUNNER..." + echo "Running with $RUNNER..." - TESTS=("--lib assignment09_grade") - if [ $(run_tests) -ne 0 ]; then - exit 1 - fi + TESTS=("--lib assignment09") + if [ $(run_tests) -ne 0 ]; then + exit 1 + fi done exit 0 diff --git a/scripts/grade-10.sh b/scripts/grade-10.sh index a221be6..87897bb 100755 --- a/scripts/grade-10.sh +++ b/scripts/grade-10.sh @@ -9,12 +9,12 @@ BASEDIR=$(dirname "$0") source $BASEDIR/grade-utils.sh RUNNERS=( - "cargo" - "cargo --release" - "cargo_asan" - "cargo_asan --release" - "cargo_tsan" - "cargo_tsan --release" + "cargo" + "cargo --release" + "cargo_asan" + "cargo_asan --release" + "cargo_tsan" + "cargo_tsan --release" ) # Lints. @@ -22,12 +22,12 @@ run_linters || exit 1 # Executes test for each runner. for RUNNER in "${RUNNERS[@]}"; do - echo "Running with $RUNNER..." + echo "Running with $RUNNER..." - TESTS=("--lib assignment10_grade") - if [ $(run_tests) -ne 0 ]; then - exit 1 - fi + TESTS=("--lib assignment10") + if [ $(run_tests) -ne 0 ]; then + exit 1 + fi done exit 0 diff --git a/scripts/grade-11.sh b/scripts/grade-11.sh index 34fb51a..8666182 100755 --- a/scripts/grade-11.sh +++ b/scripts/grade-11.sh @@ -24,7 +24,7 @@ run_linters || exit 1 for RUNNER in "${RUNNERS[@]}"; do echo "Running with $RUNNER..." - TESTS=("--lib assignment11") + TESTS=("--lib assignment11_grade") if [ $(run_tests) -ne 0 ]; then exit 1 fi diff --git a/scripts/grade-12.sh b/scripts/grade-12.sh index 34fb51a..6a87f98 100755 --- a/scripts/grade-12.sh +++ b/scripts/grade-12.sh @@ -24,7 +24,7 @@ run_linters || exit 1 for RUNNER in "${RUNNERS[@]}"; do echo "Running with $RUNNER..." - TESTS=("--lib assignment11") + TESTS=("--lib assignment12_grade") if [ $(run_tests) -ne 0 ]; then exit 1 fi diff --git a/src/assignments/assignment06/mod.rs b/src/assignments/assignment06/mod.rs new file mode 100644 index 0000000..da14417 --- /dev/null +++ b/src/assignments/assignment06/mod.rs @@ -0,0 +1,16 @@ +//! Assignment 6: Mastering advanced types (1/2). +//! +//! The primary goal of this assignment is to understand generics, traits, and lifetimes. +//! +//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-06.sh` works fine. +//! See `assignment06_grade.rs` and `/scripts/grade-06.sh` for the test script. + +use std::{collections::HashMap, fmt::Debug}; + +pub mod semiring; +pub mod square_matrix; +pub mod symbolic_differentiation; + +mod semiring_grade; +mod square_matrix_grade; +mod symbolic_differentiation_grade; diff --git a/src/assignments/assignment06.rs b/src/assignments/assignment06/semiring.rs similarity index 69% rename from src/assignments/assignment06.rs rename to src/assignments/assignment06/semiring.rs index e9a9646..1add902 100644 --- a/src/assignments/assignment06.rs +++ b/src/assignments/assignment06/semiring.rs @@ -1,9 +1,4 @@ -//! Assignment 6: Mastering advanced types (1/2). -//! -//! The primary goal of this assignment is to understand generics, traits, and lifetimes. -//! -//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-06.sh` works fine. -//! See `assignment06_grade.rs` and `/scripts/grade-06.sh` for the test script. +//! Semiring use std::{collections::HashMap, fmt::Debug}; @@ -123,12 +118,6 @@ impl Semiring for Polynomial { } } -impl From for Polynomial { - fn from(value: C) -> Self { - todo!() - } -} - impl Polynomial { /// Constructs polynomial `x`. pub fn x() -> Self { @@ -139,4 +128,41 @@ impl Polynomial { pub fn eval(&self, value: C) -> C { todo!() } + + /// Constructs polynomial `ax^n`. + pub fn term(a: C, n: u64) -> Self { + todo!() + } +} + +impl From for Polynomial { + fn from(value: C) -> Self { + todo!() + } +} + +/// Given a string `s`, parse it into a `Polynomial`. +/// You may assume that `s` follows the criteria below. +/// Therefore, you do not have to return `Err`. +/// +/// Assumptions: +/// - Each term is separated by ` + `. +/// - Each term is one of the following form: +/// `a`, `x`, `ax`, `x^n`, and `ax^n`, +/// where `a` is a `usize` number and `n` is a `u64` number. +/// This `a` should then be converted to a `C` type. +/// - In `a`, it is guaranteed that `a >= 1`. +/// - In `ax` and `ax^n`, it is guaranteed that `a >= 2`. +/// - In `x^n` and `ax^n`, it is guaranteed that `n >= 2`. +/// - All terms have unique degrees. +/// +/// Consult `assignment06_jaemin_choi_grade.rs` for example valid strings. +/// +/// Hint: `.split`, `.parse`, and `Polynomial::term` +impl std::str::FromStr for Polynomial { + type Err = (); // Ignore this for now... + + fn from_str(s: &str) -> Result { + todo!() + } } diff --git a/src/assignments/assignment06_grade.rs b/src/assignments/assignment06/semiring_grade.rs similarity index 73% rename from src/assignments/assignment06_grade.rs rename to src/assignments/assignment06/semiring_grade.rs index 60339f6..bca3278 100644 --- a/src/assignments/assignment06_grade.rs +++ b/src/assignments/assignment06/semiring_grade.rs @@ -1,6 +1,14 @@ #[cfg(test)] mod test { - use super::super::assignment06::*; + use super::super::semiring::*; + use ntest::assert_about_eq; + + fn test_from_str(s: &str, f: impl Fn(i64) -> i64) { + let poly = s.parse::>().unwrap(); + for i in 0..10 { + assert_eq!(poly.eval(i), f(i)); + } + } fn test_polynomial() { // x^2 + 5x + 6 @@ -21,6 +29,43 @@ mod test { assert_eq!(value, from_usize(13 * 13 + 5 * 13 + 6)); } + #[test] + fn test_123() { + test_from_str("123", |x| 123); + } + + #[test] + fn test_x() { + test_from_str("x", |x| x); + } + + #[test] + fn test_24x() { + test_from_str("24x", |x| 24 * x); + } + + #[test] + fn test_2x_3() { + test_from_str("2x + 3", |x| 2 * x + 3); + } + + #[test] + fn test_x3() { + test_from_str("x^3", |x| x * x * x); + } + + #[test] + fn test_2x3_3x2_5x_12() { + test_from_str("2x^3 + 3x^2 + 5x + 12", |x| { + 2 * x * x * x + 3 * x * x + 5 * x + 12 + }); + } + + #[test] + fn test_x5_1() { + test_from_str("x^5 + 1", |x| x * x * x * x * x + 1); + } + #[test] fn test_polynomial_u64() { test_polynomial::(); diff --git a/src/assignments/assignment06/square_matrix.rs b/src/assignments/assignment06/square_matrix.rs new file mode 100644 index 0000000..f81209e --- /dev/null +++ b/src/assignments/assignment06/square_matrix.rs @@ -0,0 +1,88 @@ +//! Square matrix + +/// Square matrix +pub trait SquareMatrix { + /// The type of the submatrix of this square matrix. + /// For example, the submatrix of a 3 x 3 matrix is a 2 x 2 matrix. + /// https://en.wikipedia.org/wiki/Matrix_(mathematics)#Submatrix + type Submatrix; + + /// Returns the submatrix obtained by removing the `row`th row and `col`th column + /// from the original matrix. + /// https://en.wikipedia.org/wiki/Matrix_(mathematics)#Submatrix + fn sub_matrix(&self, row: usize, col: usize) -> Self::Submatrix; + + /// Returns the determinant of the matrix. + fn det(&self) -> i64; + + /// Returns the determinant of ab, where a is self, b is given, and ab is the matrix product of them. + /// Note that the size of a and b are the same. + /// Hint: Use the fact that det(ab) = det(a) * det(b) + /// https://en.wikipedia.org/wiki/Determinant#Multiplicativity_and_matrix_groups + fn det_ab(&self, b: &Self) -> i64 { + todo!() + } +} + +/// 2 x 2 matrix +#[derive(Debug, PartialEq)] +pub struct Mat2 { + /// inner is a 2 dimensional array (size: 2 x 2) + pub inner: [[i64; 2]; 2], +} + +impl SquareMatrix for Mat2 { + type Submatrix = i64; + + fn sub_matrix(&self, row: usize, col: usize) -> Self::Submatrix { + // Hint: The submatrix of a 2 x 2 matrix is simply a single number. + todo!() + } + + // Hint: https://en.wikipedia.org/wiki/Determinant + fn det(&self) -> i64 { + todo!() + } +} + +/// 3 x 3 matrix +#[derive(Debug, PartialEq)] +pub struct Mat3 { + /// inner is a 2 dimensional array (size: 3 x 3) + pub inner: [[i64; 3]; 3], +} + +impl SquareMatrix for Mat3 { + type Submatrix = Mat2; + + fn sub_matrix(&self, row: usize, col: usize) -> Self::Submatrix { + todo!() + } + + // Hint: Use the determinant of the sub-matrices. + // https://semath.info/src/determinant-three-by-three.html + fn det(&self) -> i64 { + todo!() + } +} + +/// 4 x 4 matrix +#[derive(Debug, PartialEq)] +pub struct Mat4 { + /// inner is a 2 dimensional array (size: 4 x 4) + pub inner: [[i64; 4]; 4], +} + +impl SquareMatrix for Mat4 { + type Submatrix = Mat3; + + fn sub_matrix(&self, row: usize, col: usize) -> Self::Submatrix { + todo!() + } + + // Hint: Use the determinant of the sub-matrices. + // https://semath.info/src/determinant-four-by-four.html + fn det(&self) -> i64 { + todo!() + } +} diff --git a/src/assignments/assignment06/square_matrix_grade.rs b/src/assignments/assignment06/square_matrix_grade.rs new file mode 100644 index 0000000..7270c73 --- /dev/null +++ b/src/assignments/assignment06/square_matrix_grade.rs @@ -0,0 +1,72 @@ +#[cfg(test)] +mod test { + use super::super::square_matrix::*; + use ntest::assert_about_eq; + + #[test] + fn test_mat2() { + let mat = Mat2 { + inner: [[1, 2], [3, 4]], + }; + assert_eq!(mat.sub_matrix(1, 1), 4); + assert_eq!(mat.sub_matrix(1, 2), 3); + assert_eq!(mat.sub_matrix(2, 1), 2); + assert_eq!(mat.sub_matrix(2, 2), 1); + assert_eq!(mat.det(), -2); + + let mat2 = Mat2 { + inner: [[2, 3], [5, 7]], + }; + assert_eq!(mat.det_ab(&mat2), mat.det() * mat2.det()); + } + + #[test] + fn test_mat3() { + let mat = Mat3 { + inner: [[1, 2, 3], [5, 5, 6], [7, 8, 10]], + }; + assert_eq!( + mat.sub_matrix(1, 2), + Mat2 { + inner: [[5, 6], [7, 10]] + } + ); + assert_eq!(mat.det(), 1); + + let mat2 = Mat3 { + inner: [[2, 3, 5], [7, 10, 11], [12, 14, 20]], + }; + assert_eq!(mat2.det(), -42); + assert_eq!(mat.det_ab(&mat2), mat.det() * mat2.det()); + } + + #[test] + fn test_mat4() { + let mat = Mat4 { + inner: [ + [1, 11, 3, 4], + [5, 6, 7, 9], + [25, 10, 11, 20], + [36, 14, 15, 30], + ], + }; + assert_eq!( + mat.sub_matrix(2, 3), + Mat3 { + inner: [[1, 11, 4], [25, 10, 20], [36, 14, 30]] + } + ); + assert_eq!(mat.det(), 2089); + + let mat2 = Mat4 { + inner: [ + [2, 3, 5, 5], + [7, 10, 11, 20], + [12, 14, 20, 30], + [1, 2, 5, 10], + ], + }; + assert_eq!(mat2.det(), -340); + assert_eq!(mat.det_ab(&mat2), mat.det() * mat2.det()); + } +} diff --git a/src/assignments/assignment06/symbolic_differentiation.rs b/src/assignments/assignment06/symbolic_differentiation.rs new file mode 100644 index 0000000..28d3b4a --- /dev/null +++ b/src/assignments/assignment06/symbolic_differentiation.rs @@ -0,0 +1,360 @@ +//! Symbolic differentiation with rational coefficents. + +use std::fmt; +use std::ops::*; + +/// Rational number represented by two isize, numerator and denominator. +/// +/// Each Rational number should be normalized so that `demoninator` is nonnegative and `numerator` and `demoninator` are coprime. +/// See [`normalize`] for examples. As a corner case, 0 is represented by Rational { numerator: 0, demoninator: 0 }. +/// +/// For "natural use", Rational also overloads standard arithmetic operations, i.e, `+`, `-`, `*`, `/`. +/// +/// See [here](https://doc.rust-lang.org/core/ops/index.html) for details. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rational { + numerator: isize, + denominator: isize, +} + +// Some useful constants. + +/// Zero +pub const ZERO: Rational = Rational::new(0, 0); +/// One +pub const ONE: Rational = Rational::new(1, 1); +/// Minus one +pub const MINUS_ONE: Rational = Rational::new(-1, 1); + +impl Rational { + /// Creates a new rational number. + pub const fn new(numerator: isize, denominator: isize) -> Self { + Self { + numerator, + denominator, + } + } +} + +impl Add for Rational { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Mul for Rational { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Sub for Rational { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Div for Rational { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + todo!() + } +} + +/// Differentiable functions. +/// +/// For simplicity, we only consider infinitely differentiable functions. +pub trait Differentiable: Clone { + /// Differentiate. + /// + /// Since the return type is `Self`, this trait can only be implemented + /// for types that are closed under differentiation. + fn diff(&self) -> Self; +} + +impl Differentiable for Rational { + fn diff(&self) -> Self { + todo!() + } +} + +/// Singleton polynomial. +/// +/// Unlike regular polynomials, this type only represents a single term. +/// The `Const` variant is included to make `Polynomial` closed under differentiation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SingletonPolynomial { + /// Constant polynomial. + Const(Rational), + /// Non-const polynomial. + Polynomial { + /// coefficent of polynomial. Must be non-zero. + coeff: Rational, + /// power of polynomial. Must be non-zero. + power: Rational, + }, +} + +impl SingletonPolynomial { + /// Creates a new const polynomial. + pub fn new_c(r: Rational) -> Self { + todo!() + } + + /// Creates a new polynomial. + pub fn new_poly(coeff: Rational, power: Rational) -> Self { + todo!() + } +} + +impl Differentiable for SingletonPolynomial { + fn diff(&self) -> Self { + todo!() + } +} + +/// Expoential function. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Exp; + +impl Exp { + /// Creates a new exponential function. + pub fn new() -> Self { + todo!() + } +} + +impl Default for Exp { + fn default() -> Self { + Self::new() + } +} + +impl Differentiable for Exp { + fn diff(&self) -> Self { + todo!() + } +} + +/// Trigonometric functions. +/// +/// The trig fucntions carry their coefficents to be closed under differntiation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Trignometric { + /// Sine function. + Sine { + /// Coefficent + coeff: Rational, + }, + /// Sine function. + Cosine { + /// Coefficent + coeff: Rational, + }, +} + +impl Trignometric { + /// Creates a new sine function. + pub fn new_sine(coeff: Rational) -> Self { + todo!() + } + + /// Creates a new cosine function. + pub fn new_cosine(coeff: Rational) -> Self { + todo!() + } +} + +impl Differentiable for Trignometric { + fn diff(&self) -> Self { + todo!() + } +} + +/// Basic functions +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BaseFuncs { + /// Constant + Const(Rational), + /// Polynomial + Poly(SingletonPolynomial), + /// Exponential + Exp(Exp), + /// Trignometirc + Trig(Trignometric), +} + +impl Differentiable for BaseFuncs { + fn diff(&self) -> Self { + todo!() + } +} + +/// Complex functions. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ComplexFuncs { + /// Basic functions + Func(F), + /// Addition + Add(Box>, Box>), + /// Subtraction + Sub(Box>, Box>), + /// Multipliciation + Mul(Box>, Box>), + /// Division + Div(Box>, Box>), + /// Composition + Comp(Box>, Box>), +} + +impl Differentiable for Box { + fn diff(&self) -> Self { + todo!() + } +} + +impl Differentiable for ComplexFuncs { + fn diff(&self) -> Self { + todo!() + } +} + +/// Evaluate functions. +pub trait Evaluate { + /// Evaluate `self` at `x`. + fn evaluate(&self, x: f64) -> f64; +} + +impl Evaluate for Rational { + fn evaluate(&self, x: f64) -> f64 { + todo!() + } +} + +impl Evaluate for SingletonPolynomial { + fn evaluate(&self, x: f64) -> f64 { + todo!() + } +} + +impl Evaluate for Exp { + fn evaluate(&self, x: f64) -> f64 { + todo!() + } +} + +impl Evaluate for Trignometric { + fn evaluate(&self, x: f64) -> f64 { + todo!() + } +} + +impl Evaluate for BaseFuncs { + fn evaluate(&self, x: f64) -> f64 { + todo!() + } +} + +impl Evaluate for ComplexFuncs { + fn evaluate(&self, x: f64) -> f64 { + todo!() + } +} + +impl fmt::Display for Rational { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if *self == ZERO { + return write!(f, "0"); + } else if self.denominator == 1 { + return write!(f, "{}", self.numerator); + } + write!(f, "{}/{}", self.numerator, self.denominator) + } +} + +impl fmt::Display for SingletonPolynomial { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Const(r) => write!(f, "{r}"), + Self::Polynomial { coeff, power } => { + // coeff or power is zero + if *coeff == ZERO { + return write!(f, "0"); + } else if *power == ZERO { + return write!(f, "{coeff}"); + } + + // Standard form of px^q + let coeff = if *coeff == ONE { + "".to_string() + } else if *coeff == MINUS_ONE { + "-".to_string() + } else { + format!("({coeff})") + }; + let var = if *power == ONE { + "x".to_string() + } else { + format!("x^({power})") + }; + write!(f, "{coeff}{var}") + } + } + } +} + +impl fmt::Display for Exp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "exp(x)") + } +} + +impl fmt::Display for Trignometric { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (func, coeff) = match self { + Trignometric::Sine { coeff } => ("sin(x)", coeff), + Trignometric::Cosine { coeff } => ("cos(x)", coeff), + }; + + if *coeff == ZERO { + write!(f, "0") + } else if *coeff == ONE { + write!(f, "{func}") + } else if *coeff == MINUS_ONE { + write!(f, "-{func}") + } else { + write!(f, "({coeff}){func}") + } + } +} + +impl fmt::Display for BaseFuncs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Const(r) => write!(f, "{r}"), + Self::Poly(p) => write!(f, "{p}"), + Self::Exp(e) => write!(f, "{e}"), + Self::Trig(t) => write!(f, "{t}"), + } + } +} + +impl fmt::Display for ComplexFuncs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ComplexFuncs::Func(func) => write!(f, "{func}"), + ComplexFuncs::Add(l, r) => write!(f, "({l} + {r})"), + ComplexFuncs::Sub(l, r) => write!(f, "({l} - {r})"), + ComplexFuncs::Mul(l, r) => write!(f, "({l} * {r})"), + ComplexFuncs::Div(l, r) => write!(f, "({l} / {r})"), + ComplexFuncs::Comp(l, r) => write!(f, "({l} ∘ {r})"), + } + } +} diff --git a/src/assignments/assignment06/symbolic_differentiation_grade.rs b/src/assignments/assignment06/symbolic_differentiation_grade.rs new file mode 100644 index 0000000..0526219 --- /dev/null +++ b/src/assignments/assignment06/symbolic_differentiation_grade.rs @@ -0,0 +1,216 @@ +#[cfg(test)] +mod test { + use super::super::symbolic_differentiation::*; + use ntest::assert_about_eq; + + // Constant rationals to use + const TWO: Rational = Rational::new(2, 1); + const FOUR: Rational = Rational::new(4, 1); + const THIRD: Rational = Rational::new(1, 3); + const FIVE_THIRD: Rational = Rational::new(5, 3); + const TWO_SEVENTH: Rational = Rational::new(2, 7); + + #[test] + fn test_rational_simpl() { + assert_eq!(format!("{}", Rational::new(1, 2)), "1/2".to_string()); + + assert_eq!(format!("{}", Rational::new(0, 0)), "0".to_string()); + + assert_eq!(format!("{}", Rational::new(1, 1)), "1".to_string()); + + assert_eq!(format!("{}", Rational::new(-3, 7)), "-3/7".to_string()); + } + + #[test] + fn test_rational_arithmetic() { + assert_eq!( + format!("{}", Rational::new(1, 4) + Rational::new(3, 4)), + "1".to_string() + ); + + assert_eq!( + format!("{}", Rational::new(1, 3) + Rational::new(1, 6)), + "1/2".to_string() + ); + + assert_eq!( + format!("{}", Rational::new(1, 5) - Rational::new(1, 2)), + "-3/10".to_string() + ); + + assert_eq!( + format!("{}", Rational::new(-5, 12) * Rational::new(6, 125)), + "-1/50".to_string() + ); + + assert_eq!( + format!("{}", Rational::new(-3, 4) / Rational::new(-7, 13)), + "39/28".to_string() + ); + } + + #[test] + fn test_rational_arithmetic_long() { + assert_eq!( + format!( + "{}", + Rational::new(1, 2) + Rational::new(1, 2) + Rational::new(1, 2) + ), + "3/2".to_string() + ); + + assert_eq!( + format!( + "{}", + Rational::new(1, 2) - Rational::new(1, 4) + Rational::new(1, 5) + ), + "9/20".to_string() + ); + + assert_eq!( + format!( + "{}", + Rational::new(1, 2) + * Rational::new(1, 4) + * Rational::new(1, 8) + * Rational::new(1, 16) + / Rational::new(1, 1024) + ), + "1".to_string() + ); + + assert_eq!( + format!( + "{}", + Rational::new(123, 798) + + Rational::new(684, 32) / (Rational::new(13, 44) - Rational::new(123, 4472)) + * Rational::new(1237, 2) + ), + "12356494070/250439".to_string() + ); + } + + #[test] + fn test_differentiate_simple() { + // Constant + assert_eq!(format!("{}", Rational::new(3, 2).diff()), "0".to_string()); + + // Polynomials + assert_eq!( + format!("{}", SingletonPolynomial::new_c(Rational::new(3, 1)).diff()), + "0".to_string() + ); + assert_eq!( + format!("{}", SingletonPolynomial::new_poly(TWO, FOUR).diff()), + "(8)x^(3)".to_string() + ); + assert_eq!( + format!( + "{}", + SingletonPolynomial::new_poly(FIVE_THIRD, THIRD).diff() + ), + "(5/9)x^(-2/3)".to_string() + ); + + // Exponential + assert_eq!(format!("{}", Exp::new().diff()), "exp(x)".to_string()); + + // Trigonometric + assert_eq!( + format!("{}", Trignometric::new_sine(ONE).diff()), + "cos(x)".to_string() + ); + assert_eq!( + format!("{}", Trignometric::new_cosine(ONE).diff()), + "-sin(x)".to_string() + ); + assert_eq!( + format!("{}", Trignometric::new_sine(FIVE_THIRD).diff()), + "(5/3)cos(x)".to_string() + ); + assert_eq!( + format!("{}", Trignometric::new_cosine(TWO_SEVENTH).diff()), + "(-2/7)sin(x)".to_string() + ); + } + + #[test] + fn test_differentiate_complex() { + type BF = BaseFuncs; + type CF = ComplexFuncs; + + // Unlike the above simple test, it is hard to state a canonical + // form for derivative of more complex functions. Thus, we only test that the + // derivative is correct at certain points. + + // Add + // + // d/dx (2x^4 + exp(x)) = 8x^3 + exp(x) + let f1 = SingletonPolynomial::new_poly(TWO, FOUR); + let f2 = Exp::new(); + let deriv = CF::Add( + Box::new(CF::Func(BF::Poly(f1))), + Box::new(CF::Func(BF::Exp(f2))), + ) + .diff(); + assert_about_eq!(deriv.evaluate(2.2), 94.2090134994f64); + assert_about_eq!(deriv.evaluate(4.5), 819.017131301); + + // Sub + // + // d/dx ((5/3)cos(x) - sin(x)) = (-5/3)sin(x) - cos(x) + let f1 = Trignometric::new_cosine(FIVE_THIRD); + let f2 = Trignometric::new_sine(ONE); + let deriv = CF::Sub( + Box::new(CF::Func(BF::Trig(f1))), + Box::new(CF::Func(BF::Trig(f2))), + ) + .diff(); + assert_about_eq!(deriv.evaluate(2.7), 0.191772341627); + assert_about_eq!(deriv.evaluate(0.01), -1.01661638931); + + // Mult + // + // d/dx (2x^4 * cos(x) * exp(x)) = + // 8x^2 * cos(x) * exp(x) - 2x^4 * sin(x) * exp(x) + 2x^4 * cos(x) * exp(x) + let f1 = SingletonPolynomial::new_poly(TWO, FOUR); + let f2 = Trignometric::new_cosine(ONE); + let f3 = Exp::new(); + let deriv = CF::Mul( + Box::new(CF::Func(BF::Poly(f1))), + Box::new(CF::Mul( + Box::new(CF::Func(BF::Trig(f2))), + Box::new(CF::Func(BF::Exp(f3))), + )), + ) + .diff(); + assert_about_eq!(deriv.evaluate(3.4), -14804.9016757); + assert_about_eq!(deriv.evaluate(0.07), 0.00298352866); + + // Div + // + // (d/dx) (sin(x)/cos(x)) = (cos(x)*cos(x) + sin(x)*sin(x)) / cos(x)*cos(x) + let f1 = Trignometric::new_sine(ONE); + let f2 = Trignometric::new_cosine(ONE); + let deriv = CF::Div( + Box::new(CF::Func(BF::Trig(f1))), + Box::new(CF::Func(BF::Trig(f2))), + ) + .diff(); + assert_about_eq!(deriv.evaluate(core::f64::consts::PI), 1f64); + assert_about_eq!(deriv.evaluate(0f64), 1f64); + + // Comp + // + // d/dx (cos(x^2)) = -2x * sin(x^2) + let f1 = Trignometric::new_cosine(ONE); + let f2 = SingletonPolynomial::new_poly(ONE, TWO); + let deriv = CF::Comp( + Box::new(CF::Func(BF::Trig(f1))), + Box::new(CF::Func(BF::Poly(f2))), + ) + .diff(); + assert_about_eq!(deriv.evaluate(2.714), -4.79392977); + assert_about_eq!(deriv.evaluate(3.9), -3.72556973); + } +} diff --git a/src/assignments/assignment07.rs b/src/assignments/assignment07.rs deleted file mode 100644 index e1e4753..0000000 --- a/src/assignments/assignment07.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Assignment 7: Mastering advanced types (2/2). -//! -//! The primary goal of this assignment is to understand generics, traits, and lifetimes. -//! -//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-07.sh` works fine. -//! See `assignment07_grade.rs` and `/scripts/grade-07.sh` for the test script. - -struct FindIter<'s, T: Eq> { - query: &'s [T], - base: &'s [T], - curr: usize, -} - -impl Iterator for FindIter<'_, T> { - type Item = usize; - - fn next(&mut self) -> Option { - todo!() - } -} - -/// Returns an iterator over substring query indexes in the base. -pub fn find<'s, T: Eq>(query: &'s [T], base: &'s [T]) -> impl 's + Iterator { - FindIter { - query, - base, - curr: 0, - } -} diff --git a/src/assignments/assignment07/generator.rs b/src/assignments/assignment07/generator.rs new file mode 100644 index 0000000..fb0243b --- /dev/null +++ b/src/assignments/assignment07/generator.rs @@ -0,0 +1,36 @@ +//! Generators + +enum Yielded { + Value(T), + Stop, +} + +/// Generator +/// +/// Reference: +/// - [Python generator](https://python-reference.readthedocs.io/en/latest/docs/generator/) +#[allow(missing_debug_implementations)] +pub struct Generator { + state: S, + f: fn(&mut S) -> Yielded, +} + +impl Iterator for Generator { + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Returns a generator that yields fibonacci numbers. +pub fn fib_generator(first: usize, second: usize) -> Generator { + todo!() +} + +/// Returns a generator that yields collatz numbers. +/// +/// The generator stops when it reaches to 1. +pub fn collatz_conjecture(start: usize) -> Generator { + todo!() +} diff --git a/src/assignments/assignment07/generator_grade.rs b/src/assignments/assignment07/generator_grade.rs new file mode 100644 index 0000000..5ff51d7 --- /dev/null +++ b/src/assignments/assignment07/generator_grade.rs @@ -0,0 +1,38 @@ +#[cfg(test)] +mod test { + use itertools::Itertools; + use ntest::assert_about_eq; + + use super::super::generator::*; + + #[test] + fn test_generator() { + assert_eq!( + fib_generator(0, 1).take(10).collect::>(), + vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + ); + + assert_eq!( + collatz_conjecture(12).collect::>(), + vec![12, 6, 3, 10, 5, 16, 8, 4, 2, 1] + ); + + assert_eq!( + collatz_conjecture(19).collect::>(), + vec![19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1] + ); + + assert_eq!( + collatz_conjecture(27).collect::>(), + vec![ + 27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, + 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, + 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, + 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, + 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, + 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, + 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1 + ] + ); + } +} diff --git a/src/assignments/assignment07/hubo.rs b/src/assignments/assignment07/hubo.rs new file mode 100644 index 0000000..581cea9 --- /dev/null +++ b/src/assignments/assignment07/hubo.rs @@ -0,0 +1,84 @@ +//! Hubo is back! + +/// Types that represent a direction. +pub trait Direction { + /// Get the direction in the form of a 2-dimensional vector. + /// The resulting value doesn't have to be normalized. + fn get_vector(&self) -> (f32, f32); +} + +/// 4-way enum to indicate directions. +#[derive(Debug)] +pub enum Dir4 { + /// +x direction + Right, + /// -x direction + Left, + /// +y direction + Up, + /// -y direction + Down, +} + +impl Direction for Dir4 { + fn get_vector(&self) -> (f32, f32) { + todo!() + } +} + +impl Direction for (f32, f32) { + fn get_vector(&self) -> (f32, f32) { + todo!() + } +} + +/// Hubo. +/// It's direction can be represented by an arbitrary type. +/// +/// It can be controlled by [HuboController] only if the direction type implements the [Direction] trait. +#[derive(Debug)] +pub struct Hubo { + direction: TDir, + x: f32, + y: f32, +} + +/// Controller of the Hubo +#[derive(Debug)] +pub struct HuboController<'s, TDir> { + hubo: &'s mut Hubo, +} + +impl Hubo { + /// Create a Hubo. + pub fn new(direction: TDir, x: f32, y: f32) -> Self { + Self { direction, x, y } + } + + /// Return the current position of Hubo. + pub fn get_position(&self) -> (f32, f32) { + (self.x, self.y) + } +} + +impl<'s, TDir: Direction> HuboController<'s, TDir> { + /// Return the controller of the given Hubo. + /// Note that the lifetime of hubo's mutable reference \['s\] is repeated in the return type. + /// + /// This represents that the controller cannot live longer than the mutable reference, + /// since the controller takes and stores the reference. + pub fn new(hubo: &'s mut Hubo) -> HuboController<'s, TDir> { + todo!() + } + + /// Make Hubo move forward by the given distance. You might need to normalize the vector + /// acquired from `Direction::get_move_vector`. + pub fn move_hubo_forward(&mut self, distance: f32) { + todo!() + } + + /// Make Hubo turn to the given direction. + pub fn set_hubo_direction(&mut self, dir: TDir) { + todo!() + } +} diff --git a/src/assignments/assignment07/hubo_grade.rs b/src/assignments/assignment07/hubo_grade.rs new file mode 100644 index 0000000..50a636a --- /dev/null +++ b/src/assignments/assignment07/hubo_grade.rs @@ -0,0 +1,40 @@ +#[cfg(test)] +mod test { + use itertools::Itertools; + use ntest::assert_about_eq; + + use super::super::hubo::*; + + #[test] + fn test_hubo_dir4_movement() { + let mut hubo = Hubo::new(Dir4::Right, 0.0, 0.0); + let mut controller = HuboController::new(&mut hubo); + + // Test moving forward + controller.move_hubo_forward(5.0); + + controller.set_hubo_direction(Dir4::Up); + controller.move_hubo_forward(3.0); + + controller.set_hubo_direction(Dir4::Left); + controller.move_hubo_forward(2.0); + + assert_eq!(hubo.get_position(), (3.0, 3.0)); + } + + #[test] + fn test_hubo_tuple_movement() { + let mut hubo = Hubo::new((1., 0.), 0.0, 0.0); + let mut controller = HuboController::new(&mut hubo); + + // Test moving forward + controller.move_hubo_forward(5.0); + + controller.set_hubo_direction((3., 4.)); + controller.move_hubo_forward(5.0); + + controller.set_hubo_direction((-8., -6.)); + controller.move_hubo_forward(15.0); + assert_eq!(hubo.get_position(), (-4., -5.)); + } +} diff --git a/src/assignments/assignment07/mod.rs b/src/assignments/assignment07/mod.rs new file mode 100644 index 0000000..87c48ed --- /dev/null +++ b/src/assignments/assignment07/mod.rs @@ -0,0 +1,18 @@ +//! Assignment 7: Mastering advanced types (2/2). +//! +//! The primary goal of this assignment is to understand generics, traits, and lifetimes. +//! +//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-07.sh` works fine. +//! See `assignment07_grade.rs` and `/scripts/grade-07.sh` for the test script. + +pub mod generator; +pub mod hubo; +pub mod my_itertools; +pub mod small_exercises; +pub mod transform; + +mod generator_grade; +mod hubo_grade; +mod my_itertools_grade; +mod small_exercises_grade; +mod transform_grade; diff --git a/src/assignments/assignment07/my_itertools.rs b/src/assignments/assignment07/my_itertools.rs new file mode 100644 index 0000000..9e2efcf --- /dev/null +++ b/src/assignments/assignment07/my_itertools.rs @@ -0,0 +1,117 @@ +//! Implement your own minimal `itertools` crate. + +use std::hash::Hash; + +/// Iterator that iterates over the given iterator and returns only unique elements. +#[allow(missing_debug_implementations)] +pub struct Unique { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData, +} + +impl Iterator for Unique +where + I::Item: Eq + Hash + Clone, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Iterator that chains two iterators together. +#[allow(missing_debug_implementations)] +pub struct Chain { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData<(I1, I2)>, +} + +impl, I2: Iterator> Iterator + for Chain +{ + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Iterator that iterates over given iterator and enumerates each element. +#[allow(missing_debug_implementations)] +pub struct Enumerate { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData, +} + +impl Iterator for Enumerate { + type Item = (usize, I::Item); + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Iterator that zips two iterators together. +/// +/// If one iterator is longer than the other one, the remaining elements for the longer element +/// should be ignored. +#[allow(missing_debug_implementations)] +pub struct Zip { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData<(I1, I2)>, +} + +impl Iterator for Zip { + type Item = (I1::Item, I2::Item); + + fn next(&mut self) -> Option { + todo!() + } +} + +/// My Itertools trait. +pub trait MyIterTools: Iterator { + /// Returns an iterator that iterates over the `self` and returns only unique elements. + fn my_unique(self) -> Unique + where + Self: Sized, + { + todo!() + } + + /// Returns an iterator that chains `self` and `other` together. + fn my_chain(self, other: I) -> Chain + where + Self: Sized, + { + todo!() + } + + /// Returns an iterator that iterates over `self` and enumerates each element. + fn my_enumerate(self) -> Enumerate + where + Self: Sized, + { + todo!() + } + + /// Returns an iterator that zips `self` and `other` together. + fn my_zip(self, other: I) -> Zip + where + Self: Sized, + { + todo!() + } + + /// Foldleft for `MyIterTools` + fn my_fold(mut self, init: T, mut f: F) -> T + where + Self: Sized, + F: FnMut(Self::Item, T) -> T, + { + todo!() + } +} + +impl MyIterTools for T where T: Iterator {} diff --git a/src/assignments/assignment07/my_itertools_grade.rs b/src/assignments/assignment07/my_itertools_grade.rs new file mode 100644 index 0000000..9e9e061 --- /dev/null +++ b/src/assignments/assignment07/my_itertools_grade.rs @@ -0,0 +1,65 @@ +#[cfg(test)] +mod test { + use itertools::Itertools; + use ntest::assert_about_eq; + + use super::super::my_itertools::*; + + #[test] + fn test_itertools() { + assert_eq!( + [10, 1, 1, 1, 2, 3, 4, 1, 3, 2] + .into_iter() + .my_chain(std::iter::repeat(100)) + .my_unique() + .take(4) + .collect::>(), + vec![10, 1, 2, 3] + ); + + assert_eq!( + std::iter::repeat(5) + .my_enumerate() + .map(|(i, e)| { i * e }) + .take(5) + .collect::>(), + vec![0, 5, 10, 15, 20] + ); + + assert_eq!( + vec![0, 1, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,], + [0, 1, 10].into_iter().my_chain(0..10).collect::>(), + ); + + let it = || (1..=5).cycle().my_zip((1..=3).cycle()).map(|(x, y)| x * y); + let take15 = vec![ + 2, // 1 * 1, + 4, // 2 * 2, + 9, // 3 * 3, + 4, // 4 * 1, + 10, // 5 * 2, + 3, // 1 * 3, + 2, // 2 * 1, + 6, // 3 * 2, + 12, // 4 * 3, + 5, // 5 * 1, + 2, // 1 * 2, + 6, // 2 * 3, + 3, // 3 * 1, + 8, // 4 * 2, + 15, // 5 * 3, + ]; + + assert_eq!( + // 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 + // 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 + it().take(15).collect::>(), + take15 + ); + + assert_eq!( + it().take(15).my_fold(0, |elt, acc| elt + acc), + take15.iter().sum() + ); + } +} diff --git a/src/assignments/assignment07/small_exercises.rs b/src/assignments/assignment07/small_exercises.rs new file mode 100644 index 0000000..86c5661 --- /dev/null +++ b/src/assignments/assignment07/small_exercises.rs @@ -0,0 +1,119 @@ +//! Implement functions usint `Iterator` trait + +struct FindIter<'s, T: Eq> { + query: &'s [T], + base: &'s [T], + curr: usize, +} + +impl Iterator for FindIter<'_, T> { + type Item = usize; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Returns an iterator over substring query indexes in the base. +pub fn find<'s, T: Eq>(query: &'s [T], base: &'s [T]) -> impl 's + Iterator { + FindIter { + query, + base, + curr: 0, + } +} + +/// Implement fibonacci iterator +struct FibIter { + // TODO: remove `_marker` and add necessary fields as you want + _marker: std::marker::PhantomData, +} + +impl + Copy> FibIter { + fn new(first: T, second: T) -> Self { + todo!() + } +} + +impl Iterator for FibIter +where + T: std::ops::Add + Copy, +{ + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Returns and iterator over the generic fibonacci sequence starting from `first` and `second`. +/// This is a generic version of `fibonacci` function, which works for any types that implements `std::ops::Add` trait. +pub fn fib(first: T, second: T) -> impl Iterator +where + T: std::ops::Add + Copy, +{ + todo!("remove below"); + std::iter::empty() +} + +/// Endpoint of range, inclusive or exclusive. +#[derive(Debug)] +pub enum Endpoint { + /// Inclusive endpoint + Inclusive(isize), + + /// Exclusive endpoint + Exclusive(isize), +} + +struct RangeIter { + // TODO: add necessary fields as you want +} + +impl RangeIter { + fn new(endpoints: (Endpoint, Endpoint), step: isize) -> Self { + todo!() + } +} + +impl Iterator for RangeIter { + type Item = isize; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Returns an iterator over the range [left, right) with the given step. +pub fn range(left: Endpoint, right: Endpoint, step: isize) -> impl Iterator { + todo!("remove below"); + std::iter::empty() +} + +/// Write an iterator that returns all divisors of n in increasing order. +/// Assume n > 0. +/// +/// Hint: trying all candidates from 1 to n will most likely time out! +/// To optimize it, make use of the following fact: +/// if x is a divisor of n that is greater than sqrt(n), +/// then n/x is a divisor of n that is smaller than sqrt(n). +struct Divisors { + n: u64, + // TODO: you may define additional fields here +} + +impl Iterator for Divisors { + type Item = u64; + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Returns an iterator over the divisors of n. +pub fn divisors(n: u64) -> impl Iterator { + Divisors { + n, + // TODO: you may define additional fields here + } +} diff --git a/src/assignments/assignment07/small_exercises_grade.rs b/src/assignments/assignment07/small_exercises_grade.rs new file mode 100644 index 0000000..fced451 --- /dev/null +++ b/src/assignments/assignment07/small_exercises_grade.rs @@ -0,0 +1,189 @@ +#[cfg(test)] +mod test { + use itertools::Itertools; + use ntest::assert_about_eq; + + use super::super::small_exercises::*; + + #[test] + fn test_find() { + assert_eq!( + find("abc".as_bytes(), "abcdabcd".as_bytes()).collect::>(), + vec![0, 4] + ); + + assert_eq!( + find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::>(), + vec![0, 9, 12] + ); + + assert_eq!( + find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::>(), + vec![] + ); + + assert_eq!( + find("ababc".as_bytes(), "abc".as_bytes()).collect::>(), + vec![] + ); + } + + #[test] + fn test_find_usize() { + assert_eq!( + find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::>(), + vec![0, 4] + ); + + assert_eq!( + find( + &[5, 5, 7, 5], + &[5, 5, 7, 5, 5, 8, 5, 5, 9, 5, 5, 7, 5, 5, 7, 5] + ) + .collect::>(), + vec![0, 9, 12] + ); + } + + #[test] + fn test_fib_iter() { + assert_eq!( + fib(0, 1).take(10).collect::>(), + vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + ); + + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + struct Rgb(u8, u8, u8); + + impl std::ops::Add for Rgb { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self( + ((self.0 as u16 + rhs.0 as u16) / 2) as u8, + ((self.1 as u16 + rhs.1 as u16) / 2) as u8, + ((self.2 as u16 + rhs.2 as u16) / 2) as u8, + ) + } + } + + assert_eq!( + fib(Rgb(255, 0, 100), Rgb(1, 128, 0)) + .take(20) + .collect::>(), + vec![ + Rgb(255, 0, 100), + Rgb(1, 128, 0), + Rgb(128, 64, 50), + Rgb(64, 96, 25), + Rgb(96, 80, 37), + Rgb(80, 88, 31), + Rgb(88, 84, 34), + Rgb(84, 86, 32), + Rgb(86, 85, 33), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32), + Rgb(85, 85, 32) + ] + ); + } + + #[test] + fn test_range_iter() { + let one_to_tens = vec![ + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + range(Endpoint::Inclusive(1), Endpoint::Inclusive(10), 1).collect(), + range(Endpoint::Exclusive(0), Endpoint::Inclusive(10), 1).collect(), + range(Endpoint::Inclusive(1), Endpoint::Exclusive(11), 1).collect(), + range(Endpoint::Exclusive(0), Endpoint::Exclusive(11), 1).collect(), + ]; + assert!(one_to_tens.iter().all_equal()); + + let ten_to_ones = vec![ + vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1], + range(Endpoint::Inclusive(10), Endpoint::Inclusive(1), -1).collect(), + range(Endpoint::Exclusive(11), Endpoint::Inclusive(1), -1).collect(), + range(Endpoint::Inclusive(10), Endpoint::Exclusive(0), -1).collect(), + range(Endpoint::Exclusive(11), Endpoint::Exclusive(0), -1).collect(), + ]; + assert!(ten_to_ones.iter().all_equal()); + + let five_evens = vec![ + vec![2, 4, 6, 8, 10], + range(Endpoint::Inclusive(2), Endpoint::Inclusive(10), 2).collect(), + range(Endpoint::Inclusive(2), Endpoint::Inclusive(11), 2).collect(), + range(Endpoint::Exclusive(1), Endpoint::Inclusive(10), 2).collect(), + range(Endpoint::Exclusive(1), Endpoint::Inclusive(11), 2).collect(), + range(Endpoint::Inclusive(2), Endpoint::Exclusive(11), 2).collect(), + range(Endpoint::Inclusive(2), Endpoint::Exclusive(12), 2).collect(), + range(Endpoint::Exclusive(1), Endpoint::Exclusive(11), 2).collect(), + range(Endpoint::Exclusive(1), Endpoint::Exclusive(12), 2).collect(), + ]; + assert!(five_evens.iter().all_equal()); + + let emptys = vec![ + vec![], + range(Endpoint::Inclusive(2), Endpoint::Inclusive(10), -1).collect(), + range(Endpoint::Inclusive(10), Endpoint::Inclusive(-100), 1).collect(), + range(Endpoint::Inclusive(1), Endpoint::Exclusive(1), 1).collect(), + ]; + assert!(emptys.iter().all_equal()); + } + + #[test] + fn test_small() { + assert_eq!(divisors(10).collect::>(), vec![1, 2, 5, 10]); + + assert_eq!(divisors(17).collect::>(), vec![1, 17]); + + assert_eq!(divisors(49).collect::>(), vec![1, 7, 49]); + + assert_eq!( + divisors(120).collect::>(), + vec![1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120] + ); + + assert_eq!(divisors(1).collect::>(), vec![1]); + + assert_eq!(divisors(2).collect::>(), vec![1, 2]); + + assert_eq!(divisors(3).collect::>(), vec![1, 3]); + } + + #[test] + fn test_large() { + assert_eq!( + divisors(1_000_000_000_000_037).collect::>(), + vec![1, 1_000_000_000_000_037] + ); + + assert_eq!( + divisors(99_999_820_000_081).collect::>(), + vec![1, 9_999_991, 99_999_820_000_081] + ); + + assert_eq!( + divisors(1_234_567_890_123).collect::>(), + vec![ + 1, + 3, + 3_541, + 10_623, + 116_216_501, + 348_649_503, + 411_522_630_041, + 1_234_567_890_123 + ] + ); + + assert_eq!(divisors(97_821_761_637_600).count(), 17280); + } +} diff --git a/src/assignments/assignment07/transform.rs b/src/assignments/assignment07/transform.rs new file mode 100644 index 0000000..e24588d --- /dev/null +++ b/src/assignments/assignment07/transform.rs @@ -0,0 +1,95 @@ +//! Tranformer +use std::marker::PhantomData; +use std::ops::Add; + +/// Represents transformation of type `T`. +pub trait Transform { + /// Transforms value. + fn transform(&self, value: T) -> T; +} + +impl, Tr2: Transform> Transform<(T1, T2)> for (Tr1, Tr2) { + fn transform(&self, value: (T1, T2)) -> (T1, T2) { + todo!() + } +} + +/// Identity transformation. +#[derive(Debug, Clone, Copy)] +pub struct Identity; + +impl Transform for Identity { + fn transform(&self, value: T) -> T { + todo!() + } +} + +/// Custom transformation. +#[derive(Debug, Clone, Copy)] +pub struct Custom T> { + f: F, + _marker: PhantomData, +} + +impl T> From for Custom { + fn from(f: F) -> Self { + Self { + f, + _marker: PhantomData, + } + } +} + +impl T> Transform for Custom { + fn transform(&self, value: T) -> T { + todo!() + } +} + +/// Repeats transformation for `n` times. +#[derive(Debug, Clone, Copy)] +pub struct Repeat> { + inner: Tr, + n: u32, + _marker: PhantomData, +} + +impl> Repeat { + /// Creates a new repeat transformation. + pub fn new(inner: Tr, n: u32) -> Self { + Repeat { + inner, + n, + _marker: PhantomData, + } + } +} + +impl> Transform for Repeat { + fn transform(&self, mut value: T) -> T { + todo!() + } +} + +/// Repeats transformation until converges. +#[derive(Debug, Clone, Copy)] +pub struct RepeatUntilConverge> { + inner: Tr, + _marker: PhantomData, +} + +impl> RepeatUntilConverge { + /// Creates a new repeat transformation. + pub fn new(inner: Tr) -> Self { + RepeatUntilConverge { + inner, + _marker: PhantomData, + } + } +} + +impl> Transform for RepeatUntilConverge { + fn transform(&self, mut value: T) -> T { + todo!() + } +} diff --git a/src/assignments/assignment07/transform_grade.rs b/src/assignments/assignment07/transform_grade.rs new file mode 100644 index 0000000..7fb7870 --- /dev/null +++ b/src/assignments/assignment07/transform_grade.rs @@ -0,0 +1,62 @@ +#[cfg(test)] +mod test { + use itertools::Itertools; + use ntest::assert_about_eq; + + use super::super::transform::*; + + #[test] + fn test_transform_identity() { + let tr = Identity; + + assert_eq!(tr.transform(3), 3); + assert_eq!(tr.transform(3.0), 3.0); + assert_eq!(tr.transform("abc"), "abc"); + } + + #[test] + fn test_transform_tuple() { + let f1 = |x: u32| x + 1; + let f2 = |x: String| x.clone() + &x; + + let tr1: Custom<_, _> = f1.into(); + let tr2: Custom<_, _> = f2.into(); + + let list1 = 0u32..10u32; + let list2 = ["a".to_string(), "bb".to_string(), "ccc".to_string()]; + + for v1 in list1 { + for v2 in list2.clone() { + let tr = (tr1, tr2.clone()); + let input = (v1, v2.clone()); + let expected = (f1(v1), f2(v2)); + assert_eq!(tr.transform(input), expected); + } + } + } + + #[test] + fn test_transform_repeat() { + let inc = Custom::from(|x: i32| x + 1); + let dec = Custom::from(|x: i32| x - 1); + + for i in 0..10 { + for j in -10..10 { + assert_eq!(Repeat::new(inc, i as u32).transform(j), j + i); + assert_eq!(Repeat::new(dec, i as u32).transform(j), j - i); + } + } + } + + #[test] + fn test_transform_repeat_until_converge() { + let inc = Custom::from(|x: i32| if x < 50 { x + 1 } else { x }); + let dec = Custom::from(|x: i32| if x > 50 { x - 1 } else { x }); + + assert_eq!(RepeatUntilConverge::new(inc).transform(40), 50); + assert_eq!(RepeatUntilConverge::new(inc).transform(60), 60); + + assert_eq!(RepeatUntilConverge::new(dec).transform(40), 40); + assert_eq!(RepeatUntilConverge::new(dec).transform(60), 50); + } +} diff --git a/src/assignments/assignment07_grade.rs b/src/assignments/assignment07_grade.rs deleted file mode 100644 index 1ecf90c..0000000 --- a/src/assignments/assignment07_grade.rs +++ /dev/null @@ -1,44 +0,0 @@ -#[cfg(test)] -mod test { - use super::super::assignment07::*; - - #[test] - fn test_find() { - assert_eq!( - find("abc".as_bytes(), "abcdabcd".as_bytes()).collect::>(), - vec![0, 4] - ); - - assert_eq!( - find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::>(), - vec![0, 9, 12] - ); - - assert_eq!( - find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::>(), - vec![] - ); - - assert_eq!( - find("ababc".as_bytes(), "abc".as_bytes()).collect::>(), - vec![] - ); - } - - #[test] - fn test_find_usize() { - assert_eq!( - find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::>(), - vec![0, 4] - ); - - assert_eq!( - find( - &[5, 5, 7, 5], - &[5, 5, 7, 5, 5, 8, 5, 5, 9, 5, 5, 7, 5, 5, 7, 5] - ) - .collect::>(), - vec![0, 9, 12] - ); - } -} diff --git a/src/assignments/assignment09/bigint.rs b/src/assignments/assignment09/bigint.rs new file mode 100644 index 0000000..1c1db51 --- /dev/null +++ b/src/assignments/assignment09/bigint.rs @@ -0,0 +1,94 @@ +//! Big integer with infinite precision. + +use std::fmt; +use std::{iter::zip, ops::*}; + +/// An signed integer with infinite precision implemented with an "carrier" vector of `u32`s. +/// +/// The vector is interpreted as a base 2^(32 * (len(carrier) - 1)) integer, where negative +/// integers are represented in their [2's complement form](https://en.wikipedia.org/wiki/Two%27s_complement). +/// +/// For example, the vector `vec![44,345,3]` represents the integer +/// `44 * (2^32)^2 + 345 * (2^32) + 3`, +/// and the vector `vec![u32::MAX - 5, u32::MAX - 7]` represents the integer +/// `- (5 * 2^32 + 8) +/// +/// You will implement the `Add` and `Sub` trait for this type. +/// +/// Unlike standard fix-sized intergers in Rust where overflow will panic, the carrier is extended to save the overflowed bit. +/// On the contrary, if the precision is too much (e.g, vec![0,0] is used to represent 0, where `vec![0]` is sufficent), the carrier is truncated. +/// +/// See [this section](https://en.wikipedia.org/wiki/Two%27s_complement#Arithmetic_operations) for a rouge guide on implementation, +/// while keeping in mind that the carrier should be extended to deal with overflow. +/// +/// The `sign_extension()`, `two_complement()`, and `truncate()` are non-mandatory helper methods. +/// +/// For testing and debugging pruposes, the `Display` trait is implemented for you, which shows the integer in hexadecimal form. +#[derive(Debug, Clone)] +pub struct BigInt { + /// The carrier for `BigInt`. + /// + /// Note that the carrier should always be non-empty. + pub carrier: Vec, +} + +impl BigInt { + /// Create a new `BigInt` from a `usize`. + pub fn new(n: u32) -> Self { + todo!() + } + + /// Creates a new `BigInt` from a `Vec`. + /// + /// # Panic + /// + /// Panics if `carrier` is empty. + pub fn new_large(carrier: Vec) -> Self { + todo!() + } +} + +const SIGN_MASK: u32 = 1 << 31; + +impl BigInt { + /// Extend `self` to `len` bits. + fn sign_extension(&self, len: usize) -> Self { + todo!() + } + + /// Compute the two's complement of `self`. + fn two_complement(&self) -> Self { + todo!() + } + + /// Truncate a `BigInt` to the minimum length. + fn truncate(&self) -> Self { + todo!() + } +} + +impl Add for BigInt { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl Sub for BigInt { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + todo!() + } +} + +impl fmt::Display for BigInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Hex formatting so that each u32 can be formatted independently. + for i in self.carrier.iter() { + write!(f, "{:08x}", i)?; + } + Ok(()) + } +} diff --git a/src/assignments/assignment09/bigint_grade.rs b/src/assignments/assignment09/bigint_grade.rs new file mode 100644 index 0000000..a707026 --- /dev/null +++ b/src/assignments/assignment09/bigint_grade.rs @@ -0,0 +1,104 @@ +#[cfg(test)] +mod test { + + use ntest::{assert_false, assert_true}; + + use super::super::bigint::*; + + #[test] + fn test_inf_prec_simple() { + // Basic + assert_eq!("00000000", format!("{}", BigInt::new(0))); + assert_eq!("ffffffff", format!("{}", BigInt::new(u32::MAX))); + assert_eq!("00bc4fdc", format!("{}", BigInt::new(12_341_212))); + assert_eq!("fffffed8", format!("{}", BigInt::new(4_294_967_000u32))); + + // Add Basic + assert_eq!("00000001", format!("{}", BigInt::new(0) + BigInt::new(1))); + + assert_eq!( + "0df655df", + format!("{}", BigInt::new(13_413) + BigInt::new(234_234_234)) + ); + + assert_eq!( + "ffffff03", + format!("{}", BigInt::new(4_294_967_000u32) + BigInt::new(43)) + ); + + // Sub Basic + assert_eq!("ffffffff", format!("{}", BigInt::new(0) - BigInt::new(1))); + + assert_eq!( + "f20a12eb", + format!("{}", BigInt::new(13_413) - BigInt::new(234_234_234)) + ); + + assert_eq!( + "fffffead", + format!("{}", BigInt::new(4_294_967_000u32) - BigInt::new(43)) + ); + } + + #[test] + #[should_panic] + fn test_inf_prec_panic() { + let _ = BigInt::new_large(vec![]); + } + + #[test] + fn test_inf_prec_complex() { + // Positive overflow + assert_eq!( + "0000000080000000", + format!("{}", BigInt::new(i32::MAX as u32) + BigInt::new(1)) + ); + + // Negative overflow + assert_eq!( + "ffffffff7fffffff", + format!("{}", BigInt::new(i32::MIN as u32) - BigInt::new(1)) + ); + + // Larger positive overflow + assert_eq!( + "00000000fffffffe00000000", + format!( + "{}", + BigInt::new_large(vec![i32::MAX as u32, 0]) + + BigInt::new_large(vec![i32::MAX as u32, 0]) + ) + ); + + // Smaller negative overflow + assert_eq!( + "ffffffff000000000119464a", + format!( + "{}", + BigInt::new_large(vec![i32::MIN as u32, 2_871_572]) + + BigInt::new_large(vec![i32::MIN as u32, 15_562_038]) + ) + ); + + // Truncate + assert_eq!( + "00000000", + format!( + "{}", + BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_456]) + - BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_456]) + ) + ); + + assert_eq!( + "ffffffff", + format!( + "{}", + BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_456]) + - BigInt::new_large(vec![i32::MIN as u32, 2_871_572, 123_457]) + ) + ); + + // TODO: add a test case testing sign extension. + } +} diff --git a/src/assignments/assignment09/mod.rs b/src/assignments/assignment09/mod.rs new file mode 100644 index 0000000..d82be95 --- /dev/null +++ b/src/assignments/assignment09/mod.rs @@ -0,0 +1,12 @@ +//! Assignment 9: Iterators (1/2). +//! +//! The primary goal of this assignment is to get used to iterators. +//! +//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-09.sh` works fine. +//! See `assignment09_grade.rs` and `/scripts/grade-09.sh` for the test script. + +pub mod bigint; +pub mod small_exercises; + +mod bigint_grade; +mod small_exercises_grade; diff --git a/src/assignments/assignment09.rs b/src/assignments/assignment09/small_exercises.rs similarity index 79% rename from src/assignments/assignment09.rs rename to src/assignments/assignment09/small_exercises.rs index 38dec0e..b2950a4 100644 --- a/src/assignments/assignment09.rs +++ b/src/assignments/assignment09/small_exercises.rs @@ -1,14 +1,7 @@ -//! Assignment 9: Iterators (1/2). -//! -//! The primary goal of this assignment is to get used to iterators. -//! -//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-09.sh` works fine. -//! See `assignment09_grade.rs` and `/scripts/grade-09.sh` for the test script. +//! Small exercises. use std::collections::HashMap; -use itertools::*; - /// Returns whether the given sequence is a fibonacci sequence starts from the given sequence's first two terms. /// /// Returns `true` if the length of sequence is less or equal than 2. @@ -61,6 +54,27 @@ pub fn interleave3( todo!() } +/// Alternate elements from array of n iterators until they have run out. +/// +/// You can assume that the number of elements of iterators are same. +/// +/// # Example +/// +/// ``` +/// use cs220::assignments::assignment09::interleave_n; +/// +/// assert_eq!( +/// interleave_n(&mut [[1, 2].into_iter(), [3, 4].into_iter(), [5, 6].into_iter()]), +/// vec![1, 3, 5, 2, 4, 6] +/// ); +/// ``` +pub fn interleave_n( + mut iters: [impl Iterator; N], +) -> impl Iterator { + todo!(); + std::iter::empty() +} + /// Returns mean of k smallest value's mean. /// /// # Example @@ -172,3 +186,26 @@ pub fn find_count_n(inner: Vec, n: usize) -> Vec { pub fn position_median(inner: Vec) -> Option { todo!() } + +/// Returns the sum of all elements in a two-dimensional array. +/// +/// # Example +/// ``` +/// assert_eq!( +/// two_dimensional_sum([[1, 2, 3].into_iter(), [4, 5, 6].into_iter()].into_iter()), +/// 21 +/// ); +/// ``` +pub fn two_dimensional_sum(inner: impl Iterator>) -> i64 { + todo!() +} + +/// Returns whether the given string is palindrome or not. +/// +/// A palindrome is a word, number, phrase, or other sequence of characters which reads the same backward as forward. +/// We consider the empty string is palindrome. +/// +/// Consult . +pub fn is_palindrome(s: String) -> bool { + todo!() +} diff --git a/src/assignments/assignment09_grade.rs b/src/assignments/assignment09/small_exercises_grade.rs similarity index 77% rename from src/assignments/assignment09_grade.rs rename to src/assignments/assignment09/small_exercises_grade.rs index ed8c293..efeeb10 100644 --- a/src/assignments/assignment09_grade.rs +++ b/src/assignments/assignment09/small_exercises_grade.rs @@ -1,6 +1,9 @@ #[cfg(test)] mod test { - use super::super::assignment09::*; + + use ntest::{assert_false, assert_true}; + + use super::super::small_exercises::*; #[test] fn test_is_fibonacci() { @@ -74,6 +77,35 @@ mod test { ); } + #[test] + fn test_interleave_n() { + assert_eq!( + interleave_n([[1, 2].into_iter(), [3, 4].into_iter(), [5, 6].into_iter()]) + .collect::>(), + vec![1, 3, 5, 2, 4, 6] + ); + + assert_eq!( + interleave_n([ + [1, 2, 3].into_iter(), + [4, 5, 6].into_iter(), + [7, 8, 9].into_iter() + ]) + .collect::>(), + vec![1, 4, 7, 2, 5, 8, 3, 6, 9] + ); + + assert_eq!( + interleave_n([ + ["a", "b", "c"].into_iter(), + ["d", "e", "f"].into_iter(), + ["g", "h", "i"].into_iter() + ]) + .collect::(), + "adgbehcfi" + ); + } + #[test] fn test_k_smallest_man() { assert_eq!( @@ -200,4 +232,37 @@ mod test { assert_eq!(position_median(vec![1, 3, 3, 6, 7, 8, 9]), Some(3)); assert_eq!(position_median(vec![1, 2, 3, 4, 5, 6, 8, 9]), Some(4)); } + + #[test] + fn test_two_dimensional_sum() { + assert_eq!( + two_dimensional_sum([[1, 2, 3].into_iter(), [4, 5, 6].into_iter()].into_iter()), + 21 + ); + assert_eq!( + two_dimensional_sum( + [ + [1, 2, 3, 4, 5].into_iter(), + [10, 20, 30, 40, 50].into_iter() + ] + .into_iter() + ), + 165 + ); + } + + #[test] + fn test_is_palindrome() { + assert_true!(is_palindrome("kayak".to_string())); + assert_true!(is_palindrome("dammitimmad".to_string())); + assert_true!(is_palindrome("deified".to_string())); + assert_true!(is_palindrome("rotator".to_string())); + assert_true!(is_palindrome("noon".to_string())); + assert_true!(is_palindrome("".to_string())); + assert_true!(is_palindrome("a".to_string())); + + assert_false!(is_palindrome("moon".to_string())); + assert_false!(is_palindrome("hello".to_string())); + assert_false!(is_palindrome("apple".to_string())); + } } diff --git a/src/assignments/assignment10/labyrinth.rs b/src/assignments/assignment10/labyrinth.rs new file mode 100644 index 0000000..001b522 --- /dev/null +++ b/src/assignments/assignment10/labyrinth.rs @@ -0,0 +1,50 @@ +//! Labyrinth +//! +//! Look at the [test code](labyrinth_grade.rs) below before you start. +//! HINT: https://en.wikipedia.org/wiki/100_prisoners_problem +//! +//! NOTE: You will have to implement a probabilistic algorithm, which means, the algorithm can fail +//! even if you have implemented the solution. We recommend running multiple times (at least 5 times) to check your +//! solution works well. + +#![allow(missing_docs)] + +use std::cell::RefCell; + +/// Husband +#[derive(Debug)] +pub struct Husband { + brain: RefCell<[usize; 100]>, +} + +impl Husband { + /// What might a husband, who is looking for his wife's ID my_wife, be thinking? + pub fn seeking(my_wife: usize) -> Self { + todo!() + } + + #[allow(missing_docs)] + pub fn has_devised_a_strategy(&self) -> Strategy<'_> { + Strategy { husband: self } + } + + /// Based on the information about currently visited room number and someone's wife ID trapped inside, + /// what the husband should do next? + pub fn carefully_checks_whos_inside(&self, room: usize, wife: usize) { + todo!() + } +} + +/// Strategy of husband +#[derive(Debug)] +pub struct Strategy<'a> { + husband: &'a Husband, +} + +impl Iterator for Strategy<'_> { + type Item = usize; + + fn next(&mut self) -> Option { + todo!() + } +} diff --git a/src/assignments/assignment10/labyrinth_grade.rs b/src/assignments/assignment10/labyrinth_grade.rs new file mode 100644 index 0000000..a7fa5a1 --- /dev/null +++ b/src/assignments/assignment10/labyrinth_grade.rs @@ -0,0 +1,62 @@ +#[cfg(test)] +mod test { + use rand::seq::SliceRandom; + use rand::thread_rng; + + use super::super::labyrinth::*; + + type Wife = usize; + type Rooms = Vec; + + struct Labyrinth { + rooms: Rooms, + } + + impl From for Labyrinth { + fn from(rooms: Rooms) -> Self { + Self { rooms } + } + } + + impl Labyrinth { + fn open_the_door(&self, index: usize) -> Wife { + self.rooms[index] + } + } + + #[test] + fn can_every_husband_rescue_his_wife() { + // HINT: https://en.wikipedia.org/wiki/100_prisoners_problem + const WIVES: usize = 100; + + // One day, wives of 100 husbands were kidnapped by the Minotaur + // and imprisoned in a labyrinth.... 🏰 + let labyrinth = Labyrinth::from({ + let mut rooms: Vec<_> = (0..WIVES).collect(); + rooms.shuffle(&mut thread_rng()); + rooms + }); + + assert!((0..WIVES).all(|his_wife| { + // A new husband steps into the labyrinth to rescue his wife...! + let husband = Box::new(Husband::seeking(his_wife /*👩*/)); + let strategy = Box::new(husband.has_devised_a_strategy()); + + #[allow(clippy::all)] + /* The Minotaur🐂 will arrive in */ + (0..50) /* steps... */ + .zip(strategy) + .find(|(_, room)| { + // The husband contemplates his next move... 🤔 + // and finally, + let someone/*👤*/ = labyrinth.open_the_door(*room); // 🚪 + husband.carefully_checks_whos_inside(*room, someone); + + // Has the husband found his wife...? + someone/*👤*/ == his_wife /*👩*/ + }) + .is_some(/* The husband has successfully rescued his wife! 👫*/) + // or is_none(/* The unfortunate husband has encountered the Minotaur and... 🪓*/) + })); + } +} diff --git a/src/assignments/assignment10/mod.rs b/src/assignments/assignment10/mod.rs new file mode 100644 index 0000000..5b7390a --- /dev/null +++ b/src/assignments/assignment10/mod.rs @@ -0,0 +1,12 @@ +//! +//! Assignment 10: Iterators (2/2). +//! The primary goal of this assignment is to get used to iterators. +//! +//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-10.sh` works fine. +//! See `assignment10_grade.rs` and `/scripts/grade-10.sh` for the test script. + +pub mod labyrinth; +pub mod small_exercises; + +mod labyrinth_grade; +mod small_exercises_grade; diff --git a/src/assignments/assignment10.rs b/src/assignments/assignment10/small_exercises.rs similarity index 54% rename from src/assignments/assignment10.rs rename to src/assignments/assignment10/small_exercises.rs index 6e3e510..e4d59cb 100644 --- a/src/assignments/assignment10.rs +++ b/src/assignments/assignment10/small_exercises.rs @@ -1,10 +1,4 @@ -//! Assignment 10: Iterators (2/2). -//! -//! The primary goal of this assignment is to get used to iterators. -//! -//! You should fill out the `todo!()` placeholders in such a way that `/scripts/grade-10.sh` works fine. -//! See `assignment10_grade.rs` and `/scripts/grade-10.sh` for the test script. - +//! Small exercises. use itertools::*; /// Returns the pairs of `(i, j)` where `i < j` and `inner[i] > inner[j]` in increasing order. @@ -117,3 +111,83 @@ pub enum File { pub fn du_sort(root: &File) -> Vec<(&str, usize)> { todo!() } + +/// Remove all even numbers inside a vector using the given mutable reference. +/// That is, you must modify the vector using the given mutable reference instead +/// of returning a new vector. +/// +/// # Example +/// ``` +/// let mut vec = vec![1, 2, 3, 4, 5]; +/// remove_even(&mut vec); +/// assert_eq!(*vec, vec![1, 3, 5]); +/// ``` +#[allow(clippy::ptr_arg)] +pub fn remove_even(inner: &mut Vec) { + todo!() +} + +/// Remove all duplicate occurences of a number inside the array. +/// That is, if an integer appears more than once, remove some occurences +/// of it so that it only appears once. Note that you must modify the vector +/// using the given mutable reference instead of returning a new vector. +/// Also, note that the order does not matter. +/// +/// # Example +/// ``` +/// let mut vec = vec![1, 2, 1, 1, 3, 7, 5, 7]; +/// remove_duplicate(&mut vec); +/// assert_eq!(*vec, vec![1, 2, 3, 7, 5]); +/// ``` +#[allow(clippy::ptr_arg)] +pub fn remove_duplicate(inner: &mut Vec) { + todo!() +} + +/// Returns the natural join of two tables using the first column as the join argument. +/// That is, for each pair of a row(`Vec`) from table1 and a row(`Vec`) from table2, +/// if the first element of them are equal, then add all elements of the row from table2 +/// except its first element to the row from table1 and add it to the results. +/// Note that the order of results does not matter. +/// +/// # Example +/// +/// table1 table2 +/// ---------------------- ---------------------- +/// 20230001 | Jack 20230001 | CS +/// 20231234 | Mike 20230001 | EE +/// 20231234 | ME +/// +/// +/// result +/// ----------------------------------- +/// 20230001 | Jack | CS +/// 20230001 | Jack | EE +/// 20231234 | Mike | ME +/// +pub fn natural_join(table1: Vec>, table2: Vec>) -> Vec> { + todo!() +} + +struct Pythagorean; + +impl Pythagorean { + fn new() -> Self { + todo!() + } +} + +impl Iterator for Pythagorean { + type Item = (u64, u64, u64); + + fn next(&mut self) -> Option { + todo!() + } +} + +/// Generates sequence of unique [primitive Pythagorean +/// triples](https://en.wikipedia.org/wiki/Pythagorean_triple), i.e. (a,b,c) such that a² + b² = +/// c², a and b are coprimes, and a < b. Generate in the increasing order of c. +pub fn pythagorean() -> impl Iterator { + Pythagorean::new() +} diff --git a/src/assignments/assignment10/small_exercises_grade.rs b/src/assignments/assignment10/small_exercises_grade.rs new file mode 100644 index 0000000..e1d1e54 --- /dev/null +++ b/src/assignments/assignment10/small_exercises_grade.rs @@ -0,0 +1,351 @@ +#[cfg(test)] +mod test { + use std::collections::HashSet; + + use super::super::small_exercises::*; + + #[test] + fn test_inversion() { + assert_eq!(inversion(vec![3, 5, 4]), vec![(1, 2)]); + assert_eq!(inversion(vec!["c", "a", "b", "d"]), vec![(0, 1), (0, 2)]); + assert_eq!( + inversion(vec![2, 5, 4, 6, 3, 1]), + vec![ + (0, 5), + (1, 2), + (1, 4), + (1, 5), + (2, 4), + (2, 5), + (3, 4), + (3, 5), + (4, 5) + ] + ); + } + + #[test] + fn test_traverse_preorder() { + let root = Node::NonLeaf(( + 1, + vec![ + Node::NonLeaf((2, vec![Node::Leaf(5), Node::Leaf(6)])), + Node::Leaf(3), + Node::NonLeaf((4, vec![Node::Leaf(7), Node::Leaf(8), Node::Leaf(9)])), + ], + )); + + assert_eq!(traverse_preorder(root), vec![1, 2, 5, 6, 3, 4, 7, 8, 9]); + } + + #[test] + fn test_du_sort() { + let rootfile = File::Directory( + "root".to_string(), + vec![ + File::Directory( + "a".to_string(), + vec![ + File::Data("a1".to_string(), 1), + File::Data("a2".to_string(), 3), + ], + ), + File::Directory( + "b".to_string(), + vec![ + File::Data("b1".to_string(), 3), + File::Data("b2".to_string(), 15), + ], + ), + File::Data("c".to_string(), 8), + ], + ); + + assert_eq!( + du_sort(&rootfile), + vec![ + ("a1", 1), + ("a2", 3), + ("b1", 3), + ("a", 4), + ("c", 8), + ("b2", 15), + ("b", 18), + ("root", 1 + 3 + 3 + 15 + 8) + ] + ); + + let rootfile = File::Directory( + "root".to_string(), + vec![ + File::Directory( + "b".to_string(), + vec![ + File::Data("b1".to_string(), 3), + File::Data("b2".to_string(), 15), + ], + ), + File::Data("c".to_string(), 8), + File::Directory( + "a".to_string(), + vec![ + File::Data("a1".to_string(), 1), + File::Data("a2".to_string(), 3), + ], + ), + ], + ); + + assert_eq!( + du_sort(&rootfile), + vec![ + ("a1", 1), + ("a2", 3), + ("b1", 3), + ("a", 4), + ("c", 8), + ("b2", 15), + ("b", 18), + ("root", 1 + 3 + 3 + 15 + 8) + ] + ); + + let rootfile = File::Directory( + "root".to_string(), + vec![ + File::Directory( + "a".to_string(), + vec![ + File::Data("a1".to_string(), 1), + File::Data("a2".to_string(), 3), + File::Directory( + "a3".to_string(), + vec![ + File::Data("a31".to_string(), 1), + File::Data("a32".to_string(), 3), + File::Data("a33".to_string(), 6), + ], + ), + ], + ), + File::Directory( + "b".to_string(), + vec![ + File::Data("b1".to_string(), 3), + File::Data("b2".to_string(), 15), + ], + ), + File::Data("c".to_string(), 16), + ], + ); + + assert_eq!( + du_sort(&rootfile), + vec![ + ("a1", 1), + ("a31", 1), + ("a2", 3), + ("a32", 3), + ("b1", 3), + ("a33", 6), + ("a3", 10), + ("a", 14), + ("b2", 15), + ("c", 16), + ("b", 18), + ("root", 48) + ] + ); + } + + #[test] + fn test_remove_even() { + let mut vec = vec![1, 2, 3, 4, 5]; + remove_even(&mut vec); + assert_eq!(*vec, vec![1, 3, 5]); + + let mut vec = vec![11, 1000, 12, 101, 105, 104, 200]; + remove_even(&mut vec); + assert_eq!(*vec, vec![11, 101, 105]); + } + + #[test] + fn test_remove_duplicate() { + let mut vec = vec![1, 2, 1, 1, 3, 7, 5, 7]; + remove_duplicate(&mut vec); + + let set1: HashSet = HashSet::from_iter(vec); + let set2: HashSet = HashSet::from_iter(vec![1, 2, 3, 7, 5]); + assert_eq!(set1, set2); + } + + #[test] + fn test_natural_join() { + let row1: Vec = vec!["20230001", "Jack"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20231234", "Mike"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table1 = vec![row1, row2]; + let row1: Vec = vec!["20230001", "CS"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "EE"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20231234", "ME"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table2 = vec![row1, row2, row3]; + let row1: Vec = vec!["20230001", "Jack", "CS"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "Jack", "EE"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20231234", "Mike", "ME"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table3 = vec![row1, row2, row3]; + + assert_eq!( + HashSet::>::from_iter(natural_join(table1, table2)), + HashSet::>::from_iter(table3) + ); + + let row1: Vec = vec!["20230001", "Alice"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230002", "Bob"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20230003", "Charlie"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row4: Vec = vec!["20230004", "David"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table1 = vec![row1, row2, row3, row4]; + let row1: Vec = vec!["20230001", "Apple"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "Avocado"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20230002", "Banana"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row4: Vec = vec!["20230002", "Berries"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row5: Vec = vec!["20230004", "Durian"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table2 = vec![row1, row2, row3, row4, row5]; + let row1: Vec = vec!["20230001", "Alice", "Apple"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row2: Vec = vec!["20230001", "Alice", "Avocado"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row3: Vec = vec!["20230002", "Bob", "Banana"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row4: Vec = vec!["20230002", "Bob", "Berries"] + .iter() + .map(|s| s.to_string()) + .collect(); + let row5: Vec = vec!["20230004", "David", "Durian"] + .iter() + .map(|s| s.to_string()) + .collect(); + let table3 = vec![row1, row2, row3, row4, row5]; + + assert_eq!( + HashSet::>::from_iter(natural_join(table1, table2)), + HashSet::>::from_iter(table3) + ); + } + + #[test] + fn test_pythagorean() { + let pythagoreans = [ + (3, 4, 5), + (5, 12, 13), + (8, 15, 17), + (7, 24, 25), + (20, 21, 29), + (12, 35, 37), + (9, 40, 41), + (28, 45, 53), + (11, 60, 61), + (16, 63, 65), + (33, 56, 65), + (48, 55, 73), + (13, 84, 85), + (36, 77, 85), + (39, 80, 89), + (65, 72, 97), + (20, 99, 101), + (60, 91, 109), + (15, 112, 113), + (44, 117, 125), + (88, 105, 137), + (17, 144, 145), + (24, 143, 145), + (51, 140, 149), + (85, 132, 157), + (119, 120, 169), + (52, 165, 173), + (19, 180, 181), + (57, 176, 185), + (104, 153, 185), + (95, 168, 193), + (28, 195, 197), + (84, 187, 205), + (133, 156, 205), + (21, 220, 221), + (140, 171, 221), + (60, 221, 229), + (105, 208, 233), + (120, 209, 241), + (32, 255, 257), + (23, 264, 265), + (96, 247, 265), + (69, 260, 269), + (115, 252, 277), + (160, 231, 281), + (161, 240, 289), + (68, 285, 293), + ]; + + for (i, t) in pythagorean().enumerate().take(1000) { + if i < pythagoreans.len() { + assert_eq!(pythagoreans[i], t) + } + let (a, b, c) = t; + assert_eq!(a * a + b * b, c * c); + } + } +} diff --git a/src/assignments/assignment10_grade.rs b/src/assignments/assignment10_grade.rs deleted file mode 100644 index e7c8b5d..0000000 --- a/src/assignments/assignment10_grade.rs +++ /dev/null @@ -1,158 +0,0 @@ -#[cfg(test)] -mod test { - use super::super::assignment10::*; - - #[test] - fn test_inversion() { - assert_eq!(inversion(vec![3, 5, 4]), vec![(1, 2)]); - assert_eq!(inversion(vec!["c", "a", "b", "d"]), vec![(0, 1), (0, 2)]); - assert_eq!( - inversion(vec![2, 5, 4, 6, 3, 1]), - vec![ - (0, 5), - (1, 2), - (1, 4), - (1, 5), - (2, 4), - (2, 5), - (3, 4), - (3, 5), - (4, 5) - ] - ); - } - - #[test] - fn test_traverse_preorder() { - let root = Node::NonLeaf(( - 1, - vec![ - Node::NonLeaf((2, vec![Node::Leaf(5), Node::Leaf(6)])), - Node::Leaf(3), - Node::NonLeaf((4, vec![Node::Leaf(7), Node::Leaf(8), Node::Leaf(9)])), - ], - )); - - assert_eq!(traverse_preorder(root), vec![1, 2, 5, 6, 3, 4, 7, 8, 9]); - } - - #[test] - fn test_du_sort() { - let rootfile = File::Directory( - "root".to_string(), - vec![ - File::Directory( - "a".to_string(), - vec![ - File::Data("a1".to_string(), 1), - File::Data("a2".to_string(), 3), - ], - ), - File::Directory( - "b".to_string(), - vec![ - File::Data("b1".to_string(), 3), - File::Data("b2".to_string(), 15), - ], - ), - File::Data("c".to_string(), 8), - ], - ); - - assert_eq!( - du_sort(&rootfile), - vec![ - ("a1", 1), - ("a2", 3), - ("b1", 3), - ("a", 4), - ("c", 8), - ("b2", 15), - ("b", 18), - ("root", 1 + 3 + 3 + 15 + 8) - ] - ); - - let rootfile = File::Directory( - "root".to_string(), - vec![ - File::Directory( - "b".to_string(), - vec![ - File::Data("b1".to_string(), 3), - File::Data("b2".to_string(), 15), - ], - ), - File::Data("c".to_string(), 8), - File::Directory( - "a".to_string(), - vec![ - File::Data("a1".to_string(), 1), - File::Data("a2".to_string(), 3), - ], - ), - ], - ); - - assert_eq!( - du_sort(&rootfile), - vec![ - ("a1", 1), - ("a2", 3), - ("b1", 3), - ("a", 4), - ("c", 8), - ("b2", 15), - ("b", 18), - ("root", 1 + 3 + 3 + 15 + 8) - ] - ); - - let rootfile = File::Directory( - "root".to_string(), - vec![ - File::Directory( - "a".to_string(), - vec![ - File::Data("a1".to_string(), 1), - File::Data("a2".to_string(), 3), - File::Directory( - "a3".to_string(), - vec![ - File::Data("a31".to_string(), 1), - File::Data("a32".to_string(), 3), - File::Data("a33".to_string(), 6), - ], - ), - ], - ), - File::Directory( - "b".to_string(), - vec![ - File::Data("b1".to_string(), 3), - File::Data("b2".to_string(), 15), - ], - ), - File::Data("c".to_string(), 16), - ], - ); - - assert_eq!( - du_sort(&rootfile), - vec![ - ("a1", 1), - ("a31", 1), - ("a2", 3), - ("a32", 3), - ("b1", 3), - ("a33", 6), - ("a3", 10), - ("a", 14), - ("b2", 15), - ("c", 16), - ("b", 18), - ("root", 48) - ] - ); - } -} diff --git a/src/assignments/mod.rs b/src/assignments/mod.rs index ff16701..e7e80a2 100644 --- a/src/assignments/mod.rs +++ b/src/assignments/mod.rs @@ -15,15 +15,11 @@ mod assignment03_grade; pub mod assignment04; mod assignment04_grade; pub mod assignment06; -mod assignment06_grade; pub mod assignment07; -mod assignment07_grade; pub mod assignment08; mod assignment08_grade; pub mod assignment09; -mod assignment09_grade; pub mod assignment10; -mod assignment10_grade; pub mod assignment11; pub mod assignment12; pub mod assignment13;