mirror of
https://github.com/kmc7468/cs220.git
synced 2025-12-17 23:48:45 +00:00
copy-paste problems
This commit is contained in:
16
src/assignments/assignment06/mod.rs
Normal file
16
src/assignments/assignment06/mod.rs
Normal 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;
|
||||
168
src/assignments/assignment06/semiring.rs
Normal file
168
src/assignments/assignment06/semiring.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
//! Semiring
|
||||
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
/// Semiring.
|
||||
///
|
||||
/// Consult <https://en.wikipedia.org/wiki/Semiring>.
|
||||
pub trait Semiring: Debug + Clone + PartialEq {
|
||||
/// Additive identity.
|
||||
fn zero() -> Self;
|
||||
/// Multiplicative identity.
|
||||
fn one() -> Self;
|
||||
/// Addition operation.
|
||||
fn add(&self, rhs: &Self) -> Self;
|
||||
/// Multiplication operation.
|
||||
fn mul(&self, rhs: &Self) -> Self;
|
||||
}
|
||||
|
||||
/// Converts integer to semiring value.
|
||||
pub fn from_usize<T: Semiring>(value: usize) -> T {
|
||||
let mut result = T::zero();
|
||||
let one = T::one();
|
||||
|
||||
for _ in 0..value {
|
||||
result = T::add(&result, &one);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
impl Semiring for u64 {
|
||||
fn zero() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mul(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Semiring for i64 {
|
||||
fn zero() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mul(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Semiring for f64 {
|
||||
fn zero() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mul(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Polynomials with coefficient in `C`.
|
||||
///
|
||||
/// For example, polynomial `x^2 + 5x + 6` is represented in `Polynomial<u64>` as follows:
|
||||
///
|
||||
/// ```ignore
|
||||
/// Polynomial {
|
||||
/// coefficients: {
|
||||
/// 2: 1,
|
||||
/// 1: 5,
|
||||
/// 0: 6,
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Polynomial<C: Semiring> {
|
||||
coefficients: HashMap<u64, C>,
|
||||
}
|
||||
|
||||
impl<C: Semiring> Semiring for Polynomial<C> {
|
||||
fn zero() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mul(&self, rhs: &Self) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Semiring> Polynomial<C> {
|
||||
/// Constructs polynomial `x`.
|
||||
pub fn x() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Evaluates the polynomial with the given value.
|
||||
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!()
|
||||
}
|
||||
}
|
||||
136
src/assignments/assignment06/semiring_grade.rs
Normal file
136
src/assignments/assignment06/semiring_grade.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
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
|
||||
let poly = Polynomial::add(
|
||||
&Polynomial::add(
|
||||
&Polynomial::mul(
|
||||
&Polynomial::from(from_usize::<T>(1)),
|
||||
&Polynomial::mul(&Polynomial::x(), &Polynomial::x()),
|
||||
),
|
||||
&Polynomial::mul(&Polynomial::from(from_usize::<T>(5)), &Polynomial::x()),
|
||||
),
|
||||
&Polynomial::from(from_usize::<T>(6)),
|
||||
);
|
||||
|
||||
// 13^2 + 5*13 + 6
|
||||
let value = poly.eval(from_usize(13));
|
||||
|
||||
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>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_polynomial_f64() {
|
||||
test_polynomial::<f64>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_polynomial_p_u64() {
|
||||
test_polynomial::<Polynomial<u64>>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_polynomial_xy() {
|
||||
// (x+1)(y+2)
|
||||
let poly: Polynomial<Polynomial<u64>> = Polynomial::mul(
|
||||
&Polynomial::from(Polynomial::add(
|
||||
&Polynomial::x(),
|
||||
&Polynomial::from(from_usize::<u64>(1)),
|
||||
)),
|
||||
&(Polynomial::add(
|
||||
&Polynomial::x(),
|
||||
&Polynomial::from(Polynomial::from(from_usize::<u64>(2))),
|
||||
)),
|
||||
);
|
||||
|
||||
// poly with y = x+3
|
||||
let value = poly.eval(Polynomial::add(
|
||||
&Polynomial::x(),
|
||||
&Polynomial::from(from_usize::<u64>(3)),
|
||||
));
|
||||
|
||||
// x^2 + 6x + 5
|
||||
let expected = Polynomial::add(
|
||||
&Polynomial::add(
|
||||
&Polynomial::mul(
|
||||
&Polynomial::from(from_usize::<u64>(1)),
|
||||
&Polynomial::mul(&Polynomial::x(), &Polynomial::x()),
|
||||
),
|
||||
&Polynomial::mul(&Polynomial::from(from_usize::<u64>(6)), &Polynomial::x()),
|
||||
),
|
||||
&Polynomial::from(from_usize::<u64>(5)),
|
||||
);
|
||||
|
||||
assert_eq!(value, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_remove() {
|
||||
// (x-1)(x+1)
|
||||
let poly: Polynomial<i64> = Polynomial::mul(
|
||||
&Polynomial::add(&Polynomial::x(), &Polynomial::from(-1)),
|
||||
&Polynomial::add(&Polynomial::x(), &Polynomial::from(1)),
|
||||
);
|
||||
|
||||
// (x-1)(x+1) == x^2 - 1
|
||||
assert_eq!(
|
||||
poly,
|
||||
Polynomial::add(
|
||||
&Polynomial::mul(&Polynomial::x(), &Polynomial::x()),
|
||||
&Polynomial::from(-1)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
88
src/assignments/assignment06/square_matrix.rs
Normal file
88
src/assignments/assignment06/square_matrix.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
72
src/assignments/assignment06/square_matrix_grade.rs
Normal file
72
src/assignments/assignment06/square_matrix_grade.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
360
src/assignments/assignment06/symbolic_differentiation.rs
Normal file
360
src/assignments/assignment06/symbolic_differentiation.rs
Normal 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})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
216
src/assignments/assignment06/symbolic_differentiation_grade.rs
Normal file
216
src/assignments/assignment06/symbolic_differentiation_grade.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user