use num::{Float, ToPrimitive, Unsigned};
use crate::utils::checkers::check_newton_method_args;
pub fn rectangle_rule<Func, F1: Float, F2: Float, U: Unsigned + ToPrimitive + Copy>(
func: Func,
lower_limit: F1,
upper_limit: F1,
n_intervals: U,
) -> f64
where
Func: Fn(F1) -> F2,
{
check_newton_method_args(lower_limit, upper_limit, n_intervals);
let n = n_intervals.to_usize().unwrap();
let h: F1 = (upper_limit - lower_limit) / F1::from(n).expect("failed to convert n to float");
let half = F1::from(0.5).unwrap();
let integral: f64 = (0..n)
.map(|i| {
let x = lower_limit + (F1::from(i).unwrap() + half) * h;
func(x).to_f64().expect("failed to convert f(x) to f64")
})
.sum();
integral * h.to_f64().unwrap()
}
pub fn trapezoidal_rule<Func, F1: Float, F2: Float, U: Unsigned + ToPrimitive + Copy>(
func: Func,
lower_limit: F1,
upper_limit: F1,
n_intervals: U,
) -> f64
where
Func: Fn(F1) -> F2,
{
check_newton_method_args(lower_limit, upper_limit, n_intervals);
let n = n_intervals.to_usize().unwrap();
let h: F1 = (upper_limit - lower_limit) / F1::from(n).expect("failed to convert n to float");
let interior: f64 = (1..n)
.map(|i| {
func(lower_limit + F1::from(i).unwrap() * h)
.to_f64()
.unwrap()
})
.sum();
let endpoints =
0.5 * (func(lower_limit).to_f64().unwrap() + func(upper_limit).to_f64().unwrap());
(endpoints + interior) * h.to_f64().unwrap()
}
pub fn simpson_rule<Func, F1: Float, F2: Float, U: Unsigned + ToPrimitive + Copy>(
f: Func,
a: F1,
b: F1,
n: U,
) -> f64
where
Func: Fn(F1) -> F2,
{
check_newton_method_args(a, b, n);
let n_usize = n.to_usize().unwrap();
let h: F1 = (b - a) / F1::from(n_usize).unwrap();
let h2 = h / F1::from(2).unwrap();
let first = f(a).to_f64().unwrap() + 4.0 * f(a + h2).to_f64().unwrap();
let middle: f64 = (1..n_usize)
.map(|i| {
let xi = a + F1::from(i).unwrap() * h;
2.0 * f(xi).to_f64().unwrap() + 4.0 * f(xi + h2).to_f64().unwrap()
})
.sum();
let last = f(b).to_f64().unwrap();
(first + middle + last) * h.to_f64().unwrap() / 6.0
}
pub fn newton_rule<Func, F1: Float, F2: Float, U: Unsigned + ToPrimitive + Copy>(
func: Func,
lower_limit: F1,
upper_limit: F1,
n_intervals: U,
) -> f64
where
Func: Fn(F1) -> F2,
{
check_newton_method_args(lower_limit, upper_limit, n_intervals);
let n = n_intervals.to_usize().unwrap();
let h: F1 = (upper_limit - lower_limit) / F1::from(n).unwrap();
let h3 = h / F1::from(3).unwrap();
let first = func(lower_limit).to_f64().unwrap()
+ 3.0
* (func(lower_limit + h3).to_f64().unwrap()
+ func(lower_limit + F1::from(2).unwrap() * h3)
.to_f64()
.unwrap());
let middle: f64 = (1..n)
.map(|i| {
let xi = lower_limit + F1::from(i).unwrap() * h;
2.0 * func(xi).to_f64().unwrap()
+ 3.0
* (func(xi + h3).to_f64().unwrap()
+ func(xi + F1::from(2).unwrap() * h3).to_f64().unwrap())
})
.sum();
let last = func(upper_limit).to_f64().unwrap();
(first + middle + last) * h.to_f64().unwrap() / 8.0
}
#[cfg(test)]
mod rectangle_rule_tests {
use super::*;
const EPSILON: f64 = 10e-5;
const NUM_STEPS: usize = 1000;
#[test]
fn test_integral_value() {
fn square(x: f64) -> f64 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = rectangle_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f64() {
fn square(x: f32) -> f64 {
x.powi(2) as f64
}
let a = 0.0;
let b = 1.0;
let integral = rectangle_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f64_to_f32() {
fn square(x: f64) -> f32 {
x.powi(2) as f32
}
let a = 0.0;
let b = 1.0;
let integral = rectangle_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f32() {
let square = |x: f32| x * x;
let a = 0.0;
let b = 1.0;
let integral = rectangle_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_lower_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = rectangle_rule(f, f64::NAN, 1.0, 1usize);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_upper_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = rectangle_rule(f, 0.0, f64::NAN, 1usize);
}
}
#[cfg(test)]
mod trapezoidal_rule_tests {
use super::*;
const EPSILON: f64 = 10e-7;
const NUM_STEPS: usize = 1_000_000;
#[test]
fn test_integral_value() {
fn square(x: f64) -> f64 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = trapezoidal_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f64() {
fn square(x: f32) -> f64 {
x.powi(2) as f64
}
let a = 0.0;
let b = 1.0;
let integral = trapezoidal_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f64_to_f32() {
fn square(x: f64) -> f32 {
x.powi(2) as f32
}
let a = 0.0;
let b = 1.0;
let integral = trapezoidal_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f32() {
fn square(x: f32) -> f32 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = trapezoidal_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_lower_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = trapezoidal_rule(f, f64::NAN, 1.0, 1usize);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_upper_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = trapezoidal_rule(f, 0.0, f64::NAN, 1usize);
}
}
#[cfg(test)]
mod simpson_rule_tests {
use super::*;
const EPSILON: f64 = 10e-7;
const NUM_STEPS: usize = 1_000_000;
#[test]
fn test_integral_value() {
fn square(x: f64) -> f64 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = simpson_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f64() {
fn square(x: f32) -> f64 {
x.powi(2) as f64
}
let a = 0.0;
let b = 1.0;
let integral = simpson_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f64_to_f32() {
fn square(x: f64) -> f32 {
x.powi(2) as f32
}
let a = 0.0;
let b = 1.0;
let integral = simpson_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f32() {
fn square(x: f32) -> f32 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = simpson_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_lower_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = simpson_rule(f, f64::NAN, 1.0, 1usize);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_upper_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = simpson_rule(f, 0.0, f64::NAN, 1usize);
}
}
#[cfg(test)]
mod newton_rule_tests {
use super::*;
const EPSILON: f64 = 10e-4;
const NUM_STEPS: usize = 1_000;
#[test]
fn test_integral_value() {
fn square(x: f64) -> f64 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = newton_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f64() {
fn square(x: f32) -> f64 {
x.powi(2) as f64
}
let a = 0.0;
let b = 1.0;
let integral = newton_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f64_to_f32() {
fn square(x: f64) -> f32 {
x.powi(2) as f32
}
let a = 0.0;
let b = 1.0;
let integral = newton_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
fn test_f32_to_f32() {
fn square(x: f32) -> f32 {
x.powi(2)
}
let a = 0.0;
let b = 1.0;
let integral = newton_rule(square, a, b, NUM_STEPS);
let analytic_result: f64 = 1.0 / 3.0;
assert!((integral - analytic_result).abs() < EPSILON);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_lower_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = newton_rule(f, f64::NAN, 1.0, 1usize);
}
#[test]
#[should_panic(expected = "Integral limits a and b can't be NaN")]
fn test_upper_limit_nan() {
fn f(x: f64) -> f64 {
x
}
let _ = newton_rule(f, 0.0, f64::NAN, 1usize);
}
}