use crate::error::{NumRs2Error, Result};
use num_traits::Float;
use std::fmt::Debug;
mod advanced;
mod bond_pricing;
mod future_value;
mod internal_rate;
mod net_present_value;
mod options;
mod payment;
mod periods;
mod present_value;
mod rate_calculation;
pub use advanced::*;
pub use bond_pricing::*;
pub use future_value::*;
pub use internal_rate::*;
pub use net_present_value::*;
pub use options::*;
pub use payment::*;
pub use periods::*;
pub use present_value::*;
pub use rate_calculation::*;
pub trait FinancialCalculation<T>
where
T: Float + Debug + Clone,
{
fn calculate(&self) -> Result<T>;
}
pub fn validate_financial_params<T: Float + Debug>(
rate: T,
nper: T,
pmt: T,
pv: T,
fv: T,
) -> Result<()> {
if rate.is_nan() || nper.is_nan() || pmt.is_nan() || pv.is_nan() || fv.is_nan() {
return Err(NumRs2Error::ComputationError(
"Financial parameters cannot be NaN".to_string(),
));
}
if rate.is_infinite()
|| nper.is_infinite()
|| pmt.is_infinite()
|| pv.is_infinite()
|| fv.is_infinite()
{
return Err(NumRs2Error::ComputationError(
"Financial parameters cannot be infinite".to_string(),
));
}
Ok(())
}
#[inline]
pub fn compound_factor<T: Float>(rate: T, nper: T) -> T {
if rate.is_zero() {
T::one()
} else {
(T::one() + rate).powf(nper)
}
}
#[inline]
pub fn annuity_factor<T: Float>(rate: T, nper: T) -> T {
if rate.is_zero() {
nper
} else {
let compound = compound_factor(rate, nper);
(compound - T::one()) / rate
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_compound_factor() {
let result = compound_factor(0.05, 10.0);
assert_relative_eq!(result, 1.6288946267, epsilon = 1e-9);
}
#[test]
fn test_annuity_factor() {
let result = annuity_factor(0.05, 10.0);
assert_relative_eq!(result, 12.5778925, epsilon = 1e-6);
}
#[test]
fn test_validate_financial_params() {
assert!(validate_financial_params(0.05, 10.0, -100.0, 0.0, 1000.0).is_ok());
assert!(validate_financial_params(f64::NAN, 10.0, -100.0, 0.0, 1000.0).is_err());
assert!(validate_financial_params(f64::INFINITY, 10.0, -100.0, 0.0, 1000.0).is_err());
}
}