copy-paste problems

This commit is contained in:
jungin.rhee
2023-08-18 16:07:30 +00:00
parent 3ef3cae0cd
commit b1f1f1a5fc
28 changed files with 3276 additions and 0 deletions

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

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

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

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