use super::FloatType;
use std::error::Error;
use std::fmt;
#[derive(Debug, PartialEq)]
pub struct Sample<F>
where
F: FloatType,
{
x: F,
y: F,
}
impl<F> Sample<F>
where
F: FloatType,
{
fn is_bracketed_with(&self, other: &Self) -> bool {
self.y * other.y <= F::zero()
}
}
#[derive(Debug, PartialEq)]
pub struct Interval<F>
where
F: FloatType,
{
begin: Sample<F>,
end: Sample<F>,
}
impl<F> Interval<F>
where
F: FloatType,
{
fn is_bracketed(&self) -> bool {
self.begin.is_bracketed_with(&self.end)
}
fn is_converged(&self, convergency: &mut dyn Convergency<F>) -> bool {
convergency.is_converged(self.begin.x, self.end.x)
}
fn contains_x(&self, x: &F) -> bool {
*x <= self.end.x && *x >= self.begin.x
}
fn middle(&self) -> F {
let _2 = F::from(2i16);
let _26 = F::from(26i16);
let _27 = F::from(27i16);
if self.is_bracketed() && self.begin.y != self.end.y {
let mut shift = -self.begin.y * (self.end.x - self.begin.x) / (self.end.y - self.begin.y);
if shift < (self.end.x - self.begin.x) / _27 {
shift = (self.end.x - self.begin.x) / _27;
}
if shift > (self.end.x - self.begin.x) * _26 / _27 {
shift = (self.end.x - self.begin.x) * _26 / _27;
}
self.begin.x + shift
} else {
(self.begin.x + self.end.x) / _2
}
}
}
#[derive(Debug, PartialEq)]
pub enum SearchError {
NoConvergency,
NoBracketing,
ZeroDerivative,
}
impl fmt::Display for SearchError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SearchError::NoConvergency => write!(f, "Convergency Error"),
SearchError::NoBracketing => write!(f, "Bracketing Error"),
SearchError::ZeroDerivative => write!(f, "Zero Derivative Error"),
}
}
}
impl Error for SearchError {
fn description(&self) -> &str {
match self {
SearchError::NoConvergency => "The algorithm could not converge within the given number of iterations",
SearchError::NoBracketing => "Initial values do not bracket zero",
SearchError::ZeroDerivative => "The algorithm cannot continue from the point where the derivative is zero",
}
}
}
pub trait Convergency<F: FloatType> {
fn is_root_found(&mut self, y: F) -> bool;
fn is_converged(&mut self, x1: F, x2: F) -> bool;
fn is_iteration_limit_reached(&mut self, iter: usize) -> bool;
}
impl<F: FloatType> Convergency<F> for F {
fn is_root_found(&mut self, y: F) -> bool {
y.abs() < self.abs()
}
fn is_converged(&mut self, x1: F, x2: F) -> bool {
(x1 - x2).abs() < self.abs()
}
fn is_iteration_limit_reached(&mut self, iter: usize) -> bool {
iter >= 30
}
}
pub mod brent;
pub mod eigen;
pub mod inverse_quadratic;
pub mod newton_raphson;
pub mod polynom;
pub mod regula_falsi;
pub mod secant;
pub mod debug_convergency;
pub mod simple_convergency;
#[cfg(test)]
mod test {
use super::*;
#[test]
fn sample_bracketed() {
let sample1 = Sample { x: 0f64, y: 0f64 };
let sample2 = Sample { x: 1f64, y: 1f64 };
let sample3 = Sample { x: 1f64, y: -1f64 };
let sample4 = Sample { x: -1f64, y: 0f64 };
let sample5 = Sample { x: -1f64, y: 1f64 };
assert_eq!(true, sample1.is_bracketed_with(&sample2));
assert_eq!(true, sample1.is_bracketed_with(&sample3));
assert_eq!(true, sample1.is_bracketed_with(&sample4));
assert_eq!(true, sample1.is_bracketed_with(&sample5));
assert_eq!(true, sample2.is_bracketed_with(&sample3));
assert_eq!(true, sample2.is_bracketed_with(&sample4));
assert_eq!(false, sample2.is_bracketed_with(&sample5));
assert_eq!(true, sample3.is_bracketed_with(&sample4));
assert_eq!(true, sample3.is_bracketed_with(&sample5));
assert_eq!(true, sample4.is_bracketed_with(&sample5));
}
#[test]
fn root_interval_bracketed() {
let sut1 = Interval {
begin: Sample { x: 0f64, y: 0f64 },
end: Sample { x: 0f64, y: 0f64 },
};
let sut2 = Interval {
begin: Sample { x: 0f32, y: 0f32 },
end: Sample { x: 1f32, y: 0f32 },
};
let sut3 = Interval {
begin: Sample { x: 0f64, y: 0f64 },
end: Sample { x: 0f64, y: 1f64 },
};
let sut4 = Interval {
begin: Sample { x: -1f64, y: 0f64 },
end: Sample { x: 0f64, y: 0f64 },
};
let sut5 = Interval {
begin: Sample { x: -1f64, y: 0f64 },
end: Sample { x: 0f64, y: 1f64 },
};
let sut6 = Interval {
begin: Sample { x: -1f32, y: -1f32 },
end: Sample { x: 0f32, y: 1f32 },
};
let sut7 = Interval {
begin: Sample { x: 0f64, y: 1f64 },
end: Sample { x: 1f64, y: -1f64 },
};
assert_eq!(true, sut1.is_bracketed());
assert_eq!(true, sut2.is_bracketed());
assert_eq!(true, sut3.is_bracketed());
assert_eq!(true, sut4.is_bracketed());
assert_eq!(true, sut5.is_bracketed());
assert_eq!(true, sut6.is_bracketed());
assert_eq!(true, sut7.is_bracketed());
}
#[test]
fn root_interval_not_bracketed() {
let sut1 = Interval {
begin: Sample { x: 0f64, y: 1f64 },
end: Sample { x: 1f64, y: 1f64 },
};
let sut2 = Interval {
begin: Sample { x: -1f64, y: -1f64 },
end: Sample { x: 1f64, y: -1f64 },
};
assert_eq!(false, sut1.is_bracketed());
assert_eq!(false, sut2.is_bracketed());
}
#[test]
fn root_interval_middle() {
let sut1 = Interval {
begin: Sample { x: 0f64, y: 1f64 },
end: Sample { x: 2f64, y: -3f64 },
};
let sut2 = Interval {
begin: Sample { x: -1f64, y: 0f64 },
end: Sample { x: 1f64, y: 0f64 },
};
assert_eq!(0.5f64, sut1.middle());
assert_eq!(0f64, sut2.middle());
}
}