Merge branch 'jungin' into 'main'

assignment 6-7-8-10

See merge request kaist-cp-class/cs220-private!14
This commit is contained in:
Woojin Lee
2023-08-20 13:10:04 +00:00
40 changed files with 2624 additions and 310 deletions

1
Cargo.lock generated
View File

@@ -227,6 +227,7 @@ dependencies = [
"num-traits",
"pest",
"pest_derive",
"rand",
"rayon",
"thiserror",
]

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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<C: Semiring> Semiring for Polynomial<C> {
}
}
impl<C: Semiring> From<C> for Polynomial<C> {
fn from(value: C) -> Self {
todo!()
}
}
impl<C: Semiring> Polynomial<C> {
/// Constructs polynomial `x`.
pub fn x() -> Self {
@@ -139,4 +128,41 @@ impl<C: Semiring> Polynomial<C> {
pub fn eval(&self, value: C) -> C {
todo!()
}
/// Constructs polynomial `ax^n`.
pub fn term(a: C, n: u64) -> Self {
todo!()
}
}
impl<C: Semiring> From<C> for Polynomial<C> {
fn from(value: C) -> Self {
todo!()
}
}
/// Given a string `s`, parse it into a `Polynomial<C>`.
/// 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<C: Semiring> std::str::FromStr for Polynomial<C> {
type Err = (); // Ignore this for now...
fn from_str(s: &str) -> Result<Self, Self::Err> {
todo!()
}
}

View File

@@ -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::<Polynomial<i64>>().unwrap();
for i in 0..10 {
assert_eq!(poly.eval(i), f(i));
}
}
fn test_polynomial<T: Semiring>() {
// 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::<u64>();

View File

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

View File

@@ -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());
}
}

View File

@@ -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<F> {
/// Basic functions
Func(F),
/// Addition
Add(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Subtraction
Sub(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Multipliciation
Mul(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Division
Div(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
/// Composition
Comp(Box<ComplexFuncs<F>>, Box<ComplexFuncs<F>>),
}
impl<F: Differentiable> Differentiable for Box<F> {
fn diff(&self) -> Self {
todo!()
}
}
impl<F: Differentiable> Differentiable for ComplexFuncs<F> {
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<F: Evaluate> Evaluate for ComplexFuncs<F> {
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<F: Differentiable + fmt::Display> fmt::Display for ComplexFuncs<F> {
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})"),
}
}
}

View File

@@ -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<BF>;
// 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);
}
}

View File

@@ -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<T: Eq> Iterator for FindIter<'_, T> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
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<Item = usize> {
FindIter {
query,
base,
curr: 0,
}
}

View File

@@ -0,0 +1,36 @@
//! Generators
enum Yielded<T> {
Value(T),
Stop,
}
/// Generator
///
/// Reference:
/// - [Python generator](https://python-reference.readthedocs.io/en/latest/docs/generator/)
#[allow(missing_debug_implementations)]
pub struct Generator<T, S> {
state: S,
f: fn(&mut S) -> Yielded<T>,
}
impl<T, S> Iterator for Generator<T, S> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Returns a generator that yields fibonacci numbers.
pub fn fib_generator(first: usize, second: usize) -> Generator<usize, (usize, usize)> {
todo!()
}
/// Returns a generator that yields collatz numbers.
///
/// The generator stops when it reaches to 1.
pub fn collatz_conjecture(start: usize) -> Generator<usize, usize> {
todo!()
}

View File

@@ -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<_>>(),
vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
);
assert_eq!(
collatz_conjecture(12).collect::<Vec<_>>(),
vec![12, 6, 3, 10, 5, 16, 8, 4, 2, 1]
);
assert_eq!(
collatz_conjecture(19).collect::<Vec<_>>(),
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<_>>(),
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
]
);
}
}

View File

@@ -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<TDir> {
direction: TDir,
x: f32,
y: f32,
}
/// Controller of the Hubo
#[derive(Debug)]
pub struct HuboController<'s, TDir> {
hubo: &'s mut Hubo<TDir>,
}
impl<TDir> Hubo<TDir> {
/// 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<TDir>) -> 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!()
}
}

View File

@@ -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.));
}
}

View File

@@ -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;

View File

@@ -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<I: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<I>,
}
impl<I: Iterator> Iterator for Unique<I>
where
I::Item: Eq + Hash + Clone,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Iterator that chains two iterators together.
#[allow(missing_debug_implementations)]
pub struct Chain<I1: Iterator, I2: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<(I1, I2)>,
}
impl<T: Eq + Hash + Clone, I1: Iterator<Item = T>, I2: Iterator<Item = T>> Iterator
for Chain<I1, I2>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
/// Iterator that iterates over given iterator and enumerates each element.
#[allow(missing_debug_implementations)]
pub struct Enumerate<I: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<I>,
}
impl<I: Iterator> Iterator for Enumerate<I> {
type Item = (usize, I::Item);
fn next(&mut self) -> Option<Self::Item> {
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<I1: Iterator, I2: Iterator> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<(I1, I2)>,
}
impl<I1: Iterator, I2: Iterator> Iterator for Zip<I1, I2> {
type Item = (I1::Item, I2::Item);
fn next(&mut self) -> Option<Self::Item> {
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<Self>
where
Self: Sized,
{
todo!()
}
/// Returns an iterator that chains `self` and `other` together.
fn my_chain<I: Iterator>(self, other: I) -> Chain<Self, I>
where
Self: Sized,
{
todo!()
}
/// Returns an iterator that iterates over `self` and enumerates each element.
fn my_enumerate(self) -> Enumerate<Self>
where
Self: Sized,
{
todo!()
}
/// Returns an iterator that zips `self` and `other` together.
fn my_zip<I: Iterator>(self, other: I) -> Zip<Self, I>
where
Self: Sized,
{
todo!()
}
/// Foldleft for `MyIterTools`
fn my_fold<T, F>(mut self, init: T, mut f: F) -> T
where
Self: Sized,
F: FnMut(Self::Item, T) -> T,
{
todo!()
}
}
impl<T: ?Sized> MyIterTools for T where T: Iterator {}

View File

@@ -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<_>>(),
vec![10, 1, 2, 3]
);
assert_eq!(
std::iter::repeat(5)
.my_enumerate()
.map(|(i, e)| { i * e })
.take(5)
.collect::<Vec<_>>(),
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::<Vec<_>>(),
);
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::<Vec<_>>(),
take15
);
assert_eq!(
it().take(15).my_fold(0, |elt, acc| elt + acc),
take15.iter().sum()
);
}
}

View File

@@ -0,0 +1,119 @@
//! Implement functions usint `Iterator` trait
struct FindIter<'s, T: Eq> {
query: &'s [T],
base: &'s [T],
curr: usize,
}
impl<T: Eq> Iterator for FindIter<'_, T> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
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<Item = usize> {
FindIter {
query,
base,
curr: 0,
}
}
/// Implement fibonacci iterator
struct FibIter<T> {
// TODO: remove `_marker` and add necessary fields as you want
_marker: std::marker::PhantomData<T>,
}
impl<T: std::ops::Add<Output = T> + Copy> FibIter<T> {
fn new(first: T, second: T) -> Self {
todo!()
}
}
impl<T> Iterator for FibIter<T>
where
T: std::ops::Add<Output = T> + Copy,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
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<T>(first: T, second: T) -> impl Iterator<Item = T>
where
T: std::ops::Add<Output = T> + 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<Self::Item> {
todo!()
}
}
/// Returns an iterator over the range [left, right) with the given step.
pub fn range(left: Endpoint, right: Endpoint, step: isize) -> impl Iterator<Item = isize> {
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<Self::Item> {
todo!()
}
}
/// Returns an iterator over the divisors of n.
pub fn divisors(n: u64) -> impl Iterator<Item = u64> {
Divisors {
n,
// TODO: you may define additional fields here
}
}

View File

@@ -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<usize>>(),
vec![0, 4]
);
assert_eq!(
find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::<Vec<usize>>(),
vec![0, 9, 12]
);
assert_eq!(
find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
assert_eq!(
find("ababc".as_bytes(), "abc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
}
#[test]
fn test_find_usize() {
assert_eq!(
find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::<Vec<usize>>(),
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<usize>>(),
vec![0, 9, 12]
);
}
#[test]
fn test_fib_iter() {
assert_eq!(
fib(0, 1).take(10).collect::<Vec<_>>(),
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<_>>(),
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<u64>>(), vec![1, 2, 5, 10]);
assert_eq!(divisors(17).collect::<Vec<u64>>(), vec![1, 17]);
assert_eq!(divisors(49).collect::<Vec<u64>>(), vec![1, 7, 49]);
assert_eq!(
divisors(120).collect::<Vec<u64>>(),
vec![1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120]
);
assert_eq!(divisors(1).collect::<Vec<u64>>(), vec![1]);
assert_eq!(divisors(2).collect::<Vec<u64>>(), vec![1, 2]);
assert_eq!(divisors(3).collect::<Vec<u64>>(), vec![1, 3]);
}
#[test]
fn test_large() {
assert_eq!(
divisors(1_000_000_000_000_037).collect::<Vec<u64>>(),
vec![1, 1_000_000_000_000_037]
);
assert_eq!(
divisors(99_999_820_000_081).collect::<Vec<u64>>(),
vec![1, 9_999_991, 99_999_820_000_081]
);
assert_eq!(
divisors(1_234_567_890_123).collect::<Vec<u64>>(),
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);
}
}

View File

@@ -0,0 +1,95 @@
//! Tranformer
use std::marker::PhantomData;
use std::ops::Add;
/// Represents transformation of type `T`.
pub trait Transform<T> {
/// Transforms value.
fn transform(&self, value: T) -> T;
}
impl<T1, T2, Tr1: Transform<T1>, Tr2: Transform<T2>> 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<T> Transform<T> for Identity {
fn transform(&self, value: T) -> T {
todo!()
}
}
/// Custom transformation.
#[derive(Debug, Clone, Copy)]
pub struct Custom<T, F: Fn(T) -> T> {
f: F,
_marker: PhantomData<T>,
}
impl<T, F: Fn(T) -> T> From<F> for Custom<T, F> {
fn from(f: F) -> Self {
Self {
f,
_marker: PhantomData,
}
}
}
impl<T, F: Fn(T) -> T> Transform<T> for Custom<T, F> {
fn transform(&self, value: T) -> T {
todo!()
}
}
/// Repeats transformation for `n` times.
#[derive(Debug, Clone, Copy)]
pub struct Repeat<T, Tr: Transform<T>> {
inner: Tr,
n: u32,
_marker: PhantomData<T>,
}
impl<T, Tr: Transform<T>> Repeat<T, Tr> {
/// Creates a new repeat transformation.
pub fn new(inner: Tr, n: u32) -> Self {
Repeat {
inner,
n,
_marker: PhantomData,
}
}
}
impl<T, Tr: Transform<T>> Transform<T> for Repeat<T, Tr> {
fn transform(&self, mut value: T) -> T {
todo!()
}
}
/// Repeats transformation until converges.
#[derive(Debug, Clone, Copy)]
pub struct RepeatUntilConverge<T: Eq, Tr: Transform<T>> {
inner: Tr,
_marker: PhantomData<T>,
}
impl<T: Clone + Eq, Tr: Transform<T>> RepeatUntilConverge<T, Tr> {
/// Creates a new repeat transformation.
pub fn new(inner: Tr) -> Self {
RepeatUntilConverge {
inner,
_marker: PhantomData,
}
}
}
impl<T: Clone + Eq, Tr: Transform<T>> Transform<T> for RepeatUntilConverge<T, Tr> {
fn transform(&self, mut value: T) -> T {
todo!()
}
}

View File

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

View File

@@ -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<usize>>(),
vec![0, 4]
);
assert_eq!(
find("aaba".as_bytes(), "aabaacaadaabaaba".as_bytes()).collect::<Vec<usize>>(),
vec![0, 9, 12]
);
assert_eq!(
find("ababac".as_bytes(), "abababcabababcabababc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
assert_eq!(
find("ababc".as_bytes(), "abc".as_bytes()).collect::<Vec<usize>>(),
vec![]
);
}
#[test]
fn test_find_usize() {
assert_eq!(
find(&[1, 2, 3], &[1, 2, 3, 4, 1, 2, 3, 4]).collect::<Vec<usize>>(),
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<usize>>(),
vec![0, 9, 12]
);
}
}

View File

@@ -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<u32>,
}
impl BigInt {
/// Create a new `BigInt` from a `usize`.
pub fn new(n: u32) -> Self {
todo!()
}
/// Creates a new `BigInt` from a `Vec<u32>`.
///
/// # Panic
///
/// Panics if `carrier` is empty.
pub fn new_large(carrier: Vec<u32>) -> 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(())
}
}

View File

@@ -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.
}
}

View File

@@ -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;

View File

@@ -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<T>(
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<T, const N: usize>(
mut iters: [impl Iterator<Item = T>; N],
) -> impl Iterator<Item = T> {
todo!();
std::iter::empty()
}
/// Returns mean of k smallest value's mean.
///
/// # Example
@@ -172,3 +186,26 @@ pub fn find_count_n(inner: Vec<usize>, n: usize) -> Vec<usize> {
pub fn position_median<T: Ord>(inner: Vec<T>) -> Option<usize> {
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<Item = impl Iterator<Item = i64>>) -> 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 <https://en.wikipedia.org/wiki/Palindrome>.
pub fn is_palindrome(s: String) -> bool {
todo!()
}

View File

@@ -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<_>>(),
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<_>>(),
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::<String>(),
"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()));
}
}

View File

@@ -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<Self::Item> {
todo!()
}
}

View File

@@ -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<Wife>;
struct Labyrinth {
rooms: Rooms,
}
impl From<Rooms> 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... 🪓*/)
}));
}
}

View File

@@ -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;

View File

@@ -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<i64>) {
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<i64>) {
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<String>`) from table1 and a row(`Vec<String>`) 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<Vec<String>>, table2: Vec<Vec<String>>) -> Vec<Vec<String>> {
todo!()
}
struct Pythagorean;
impl Pythagorean {
fn new() -> Self {
todo!()
}
}
impl Iterator for Pythagorean {
type Item = (u64, u64, u64);
fn next(&mut self) -> Option<Self::Item> {
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<Item = (u64, u64, u64)> {
Pythagorean::new()
}

View File

@@ -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<i64> = HashSet::from_iter(vec);
let set2: HashSet<i64> = HashSet::from_iter(vec![1, 2, 3, 7, 5]);
assert_eq!(set1, set2);
}
#[test]
fn test_natural_join() {
let row1: Vec<String> = vec!["20230001", "Jack"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20231234", "Mike"]
.iter()
.map(|s| s.to_string())
.collect();
let table1 = vec![row1, row2];
let row1: Vec<String> = vec!["20230001", "CS"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "EE"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20231234", "ME"]
.iter()
.map(|s| s.to_string())
.collect();
let table2 = vec![row1, row2, row3];
let row1: Vec<String> = vec!["20230001", "Jack", "CS"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "Jack", "EE"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20231234", "Mike", "ME"]
.iter()
.map(|s| s.to_string())
.collect();
let table3 = vec![row1, row2, row3];
assert_eq!(
HashSet::<Vec<String>>::from_iter(natural_join(table1, table2)),
HashSet::<Vec<String>>::from_iter(table3)
);
let row1: Vec<String> = vec!["20230001", "Alice"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230002", "Bob"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20230003", "Charlie"]
.iter()
.map(|s| s.to_string())
.collect();
let row4: Vec<String> = vec!["20230004", "David"]
.iter()
.map(|s| s.to_string())
.collect();
let table1 = vec![row1, row2, row3, row4];
let row1: Vec<String> = vec!["20230001", "Apple"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "Avocado"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20230002", "Banana"]
.iter()
.map(|s| s.to_string())
.collect();
let row4: Vec<String> = vec!["20230002", "Berries"]
.iter()
.map(|s| s.to_string())
.collect();
let row5: Vec<String> = vec!["20230004", "Durian"]
.iter()
.map(|s| s.to_string())
.collect();
let table2 = vec![row1, row2, row3, row4, row5];
let row1: Vec<String> = vec!["20230001", "Alice", "Apple"]
.iter()
.map(|s| s.to_string())
.collect();
let row2: Vec<String> = vec!["20230001", "Alice", "Avocado"]
.iter()
.map(|s| s.to_string())
.collect();
let row3: Vec<String> = vec!["20230002", "Bob", "Banana"]
.iter()
.map(|s| s.to_string())
.collect();
let row4: Vec<String> = vec!["20230002", "Bob", "Berries"]
.iter()
.map(|s| s.to_string())
.collect();
let row5: Vec<String> = vec!["20230004", "David", "Durian"]
.iter()
.map(|s| s.to_string())
.collect();
let table3 = vec![row1, row2, row3, row4, row5];
assert_eq!(
HashSet::<Vec<String>>::from_iter(natural_join(table1, table2)),
HashSet::<Vec<String>>::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);
}
}
}

View File

@@ -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)
]
);
}
}

View File

@@ -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;