Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,13 @@
* [Trapped Rainwater](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/trapped_rainwater.rs)
* [Word Break](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/word_break.rs)
* Financial
* [Present Value](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/present_value.rs)
* [Compound Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/compound_interest.rs)
* [Equated Monthly Installments](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/equated_monthly_installments.rs)
* [Finance Ratios](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/finance_ratios.rs)
* [Net Present Value](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/npv.rs)
* [NPV Sensitivity](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/npv_sensitivity.rs)
* [Compound Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/compound_interest.rs)
* [Payback Period](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/payback.rs)
* [Finance Ratios](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/finance_ratios.rs)
* [Present Value](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/present_value.rs)
* [Treynor Ratio](https://github.com/TheAlgorithms/Rust/blob/master/src/financial/treynor_ratio.rs)
* General
* [Convex Hull](https://github.com/TheAlgorithms/Rust/blob/master/src/general/convex_hull.rs)
Expand Down
87 changes: 87 additions & 0 deletions src/financial/equated_monthly_installments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! Calculates the Equated Monthly Installment (EMI) for a loan.
//!
//! Formula: A = p * r * (1 + r)^n / ((1 + r)^n - 1)
//! where:
//! - `p` is the principal
//! - `r` is the monthly interest rate (annual rate / 12)
//! - `n` is the total number of monthly payments (years * 12)
//!
//! Wikipedia Reference: https://en.wikipedia.org/wiki/Equated_monthly_installment

/// Computes the monthly EMI for a loan.
///
/// # Arguments
/// * `principal` - The total amount borrowed (must be > 0)
/// * `rate_per_annum` - Annual interest rate as a decimal, e.g. 0.12 for 12% (must be >= 0)
/// * `years_to_repay` - Loan term in whole years (must be > 0)
///
/// # Errors
/// Returns an `Err(&'static str)` if any argument is out of range.
pub fn equated_monthly_installments(
principal: f64,
rate_per_annum: f64,
years_to_repay: u32,
) -> Result<f64, &'static str> {
if principal <= 0.0 {
return Err("Principal borrowed must be > 0");
}
if rate_per_annum < 0.0 {
return Err("Rate of interest must be >= 0");
}
if years_to_repay == 0 {
return Err("Years to repay must be an integer > 0");
}

// Divide annual rate by 12 to obtain the monthly rate
let rate_per_month = rate_per_annum / 12.0;

// Multiply years by 12 to obtain the total number of monthly payments
let number_of_payments = f64::from(years_to_repay * 12);

// Handle the edge case where the interest rate is 0 (simple division)
if rate_per_month == 0.0 {
return Ok(principal / number_of_payments);
}

let factor = (1.0 + rate_per_month).powf(number_of_payments);
Ok(principal * rate_per_month * factor / (factor - 1.0))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_equated_monthly_installments() {
const EPSILON: f64 = 1e-8;

// Standard cases
let result = equated_monthly_installments(25000.0, 0.12, 3).unwrap();
assert!((result - 830.357_745_321_279_3).abs() < EPSILON);

let result = equated_monthly_installments(25000.0, 0.12, 10).unwrap();
assert!((result - 358.677_371_006_468_26).abs() < EPSILON);

// With 0% interest the EMI is simply principal / number_of_payments
let result = equated_monthly_installments(12000.0, 0.0, 1).unwrap();
assert!((result - 1000.0).abs() < EPSILON);

// Error cases
assert_eq!(
equated_monthly_installments(0.0, 0.12, 3),
Err("Principal borrowed must be > 0")
);
assert_eq!(
equated_monthly_installments(-5000.0, 0.12, 3),
Err("Principal borrowed must be > 0")
);
assert_eq!(
equated_monthly_installments(25000.0, -1.0, 3),
Err("Rate of interest must be >= 0")
);
assert_eq!(
equated_monthly_installments(25000.0, 0.12, 0),
Err("Years to repay must be an integer > 0")
);
}
}
21 changes: 11 additions & 10 deletions src/financial/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
mod compound_interest;
mod equated_monthly_installments;
mod finance_ratios;
mod npv;
mod npv_sensitivity;
mod payback;
mod present_value;
mod treynor_ratio;
pub use compound_interest::compound_interest;
pub use npv::npv;
pub use npv_sensitivity::npv_sensitivity;
pub use payback::payback;
pub use present_value::present_value;
pub use treynor_ratio::treynor_ratio;

pub use finance_ratios::debt_to_equity;
pub use finance_ratios::earnings_per_sale;
pub use finance_ratios::gross_profit_margin;
pub use finance_ratios::return_on_investment;
pub use self::compound_interest::compound_interest;
pub use self::equated_monthly_installments::equated_monthly_installments;
pub use self::finance_ratios::{
debt_to_equity, earnings_per_sale, gross_profit_margin, return_on_investment,
};
pub use self::npv::npv;
pub use self::npv_sensitivity::npv_sensitivity;
pub use self::payback::payback;
pub use self::present_value::present_value;
pub use self::treynor_ratio::treynor_ratio;