#![allow(unused_imports)]
use std::fmt;
use crate::*;
use std::cmp::max;
use std::ops::Deref;
pub mod future_value_annuity;
#[doc(inline)]
pub use future_value_annuity::*;
pub mod payment;
#[doc(inline)]
pub use payment::*;
pub mod present_value_annuity;
#[doc(inline)]
pub use present_value_annuity::*;
pub mod net_present_value;
#[doc(inline)]
pub use net_present_value::*;
pub mod nper;
#[doc(inline)]
pub use nper::*;
#[derive(Debug, Clone)]
pub enum CashflowVariable {
PresentValueAnnuity,
PresentValueAnnuityDue,
FutureValueAnnuity,
Payment,
FutureValueAnnuityDue,
NetPresentValue,
}
impl CashflowVariable {
pub fn is_present_value_annuity(&self) -> bool {
match self {
CashflowVariable::PresentValueAnnuity => true,
_ => false,
}
}
pub fn is_future_value_annuity(&self) -> bool {
match self {
CashflowVariable::FutureValueAnnuity => true,
_ => false,
}
}
pub fn is_payment(&self) -> bool {
match self {
CashflowVariable::Payment => true,
_ => false,
}
}
pub fn is_present_value_annuity_due(&self) -> bool {
match self {
CashflowVariable::PresentValueAnnuityDue => true,
_ => false,
}
}
pub fn is_future_value_annuity_due(&self) -> bool {
match self {
CashflowVariable::FutureValueAnnuityDue => true,
_ => false,
}
}
pub fn is_net_present_value(&self) -> bool {
match self {
CashflowVariable::NetPresentValue => true,
_ => false,
}
}
}
impl fmt::Display for CashflowVariable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CashflowVariable::PresentValueAnnuity => write!(f, "Present Value Annuity"),
CashflowVariable::FutureValueAnnuity => write!(f, "Future Value Annuity"),
CashflowVariable::Payment => write!(f, "Payment"),
CashflowVariable::PresentValueAnnuityDue => write!(f, "Present Value Annuity Due"),
CashflowVariable::FutureValueAnnuityDue => write!(f, "Future Value Annuity Due"),
CashflowVariable::NetPresentValue => write!(f, "Net Present Value"),
}
}
}
#[derive(Clone, Debug)]
pub struct CashflowSolution {
calculated_field: CashflowVariable,
rate: f64,
periods: u32,
present_value: f64,
future_value: f64,
due_at_beginning: bool,
payment: f64,
sum_of_payments: f64,
sum_of_interest: f64,
formula: String,
symbolic_formula: String,
}
impl CashflowSolution {
pub(crate) fn new(
calculated_field: CashflowVariable,
rate: f64,
periods: u32,
present_value: f64,
future_value: f64,
due_at_beginning: bool,
payment: f64,
formula: &str,
symbolic_formula: &str,
) -> Self {
assert!(!formula.is_empty());
let sum_of_payments = payment * periods as f64;
let sum_of_interest = sum_of_payments + present_value + future_value;
Self {
calculated_field,
rate,
periods,
present_value,
future_value,
due_at_beginning,
payment,
sum_of_payments,
sum_of_interest,
formula: formula.to_string(),
symbolic_formula: symbolic_formula.to_string(),
}
}
pub fn calculated_field(&self) -> &CashflowVariable {
&self.calculated_field
}
pub fn rate(&self) -> f64 {
self.rate
}
pub fn periods(&self) -> u32 {
self.periods
}
pub fn present_value(&self) -> f64 {
self.present_value
}
pub fn future_value(&self) -> f64 {
self.future_value
}
pub fn due_at_beginning(&self) -> bool {
self.due_at_beginning
}
pub fn payment(&self) -> f64 {
self.payment
}
pub fn sum_of_payments(&self) -> f64 {
self.sum_of_payments
}
pub fn sum_of_interest(&self) -> f64 {
self.sum_of_interest
}
pub fn formula(&self) -> &str {
&self.formula
}
pub fn symbolic_formula(&self) -> &str {
&self.symbolic_formula
}
}
#[derive(Clone, Debug)]
pub struct CashflowSeries(Vec<CashflowPeriod>);
impl CashflowSeries {
pub(crate) fn new(series: Vec<CashflowPeriod>) -> Self {
Self {
0: series,
}
}
pub fn filter<P>(&self, predicate: P) -> Self
where P: Fn(&&CashflowPeriod) -> bool
{
Self {
0: self.iter().filter(|x| predicate(x)).cloned().collect()
}
}
pub fn print_table(
&self,
include_running_totals: bool,
include_remaining_amounts: bool)
{
self.print_table_locale_opt(include_running_totals, include_remaining_amounts, None, None);
}
pub fn print_table_locale(
&self,
include_running_totals: bool,
include_remaining_amounts: bool,
locale: &num_format::Locale,
precision: usize) {
self.print_table_locale_opt(include_running_totals, include_remaining_amounts, Some(locale), Some(precision));
}
fn print_table_locale_opt(
&self,
include_running_totals: bool,
include_remaining_amounts: bool,
locale: Option<&num_format::Locale>,
precision: Option<usize>)
{
let columns = columns_with_strings(&[
("period", "i", true),
("payments_to_date", "f", include_running_totals), ("payments_remaining", "f", include_remaining_amounts),
("principal", "f", true), ("principal_to_date", "f", include_running_totals), ("principal_remaining", "f", include_remaining_amounts),
("interest", "f", true), ("interest_to_date", "f", include_running_totals), ("interest_remaining", "f", include_remaining_amounts)]);
let data = self.iter()
.map(|entry| vec![entry.period.to_string(), entry.payments_to_date.to_string(), entry.payments_remaining.to_string(),
entry.principal.to_string(), entry.principal_to_date.to_string(), entry.principal_remaining.to_string(),
entry.interest.to_string(), entry.interest_to_date.to_string(), entry.interest_remaining.to_string()])
.collect::<Vec<_>>();
print_table_locale_opt(&columns, data, locale, precision);
}
pub fn print_ab_comparison(
&self,
other: &CashflowSeries,
include_running_totals: bool,
include_remaining_amounts: bool)
{
self.print_ab_comparison_locale_opt(other, include_running_totals, include_remaining_amounts, None, None);
}
pub fn print_ab_comparison_locale(
&self,
other: &CashflowSeries,
include_running_totals: bool,
include_remaining_amounts: bool,
locale: &num_format::Locale,
precision: usize) {
self.print_ab_comparison_locale_opt(other, include_running_totals, include_remaining_amounts, Some(locale), Some(precision));
}
pub(crate) fn print_ab_comparison_locale_opt(
&self,
other: &CashflowSeries,
include_running_totals: bool,
include_remaining_amounts: bool,
locale: Option<&num_format::Locale>,
precision: Option<usize>) {
let columns = columns_with_strings(&[("period", "i", true),
("payment_a", "f", true), ("payment_b", "f", true),
("pmt_to_date_a", "f", include_running_totals), ("pmt_to_date_b", "f", include_running_totals),
("pmt_remaining_a", "f", false), ("pmt_remaining_b", "f", false),
("principal_a", "f", true), ("principal_b", "f", true),
("princ_to_date_a", "f", include_running_totals), ("princ_to_date_b", "f", include_running_totals),
("princ_remaining_a", "f", include_remaining_amounts), ("princ_remaining_b", "f", include_remaining_amounts),
("interest_a", "f", false), ("interest_b", "f", false),
("int_to_date_a", "f", include_running_totals), ("int_to_date_b", "f", include_running_totals),
("int_remaining_a", "f", false), ("int_remaining_b", "f", false)]);
let mut data = vec![];
let rows = max(self.len(), other.len());
for row_index in 0..rows {
data.push(vec![
(row_index + 1).to_string(),
self.get(row_index).map_or("".to_string(), |x| x.payment.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.payment.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.payments_to_date.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.payments_to_date.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.payments_remaining.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.payments_remaining.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.principal.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.principal.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.principal_to_date.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.principal_to_date.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.principal_remaining.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.principal_remaining.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.interest.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.interest.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.interest_to_date.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.interest_to_date.to_string()),
self.get(row_index).map_or("".to_string(), |x| x.interest_remaining.to_string()),
other.get(row_index).map_or("".to_string(), |x| x.interest_remaining.to_string()),
]);
}
print_table_locale_opt(&columns, data, locale, precision);
}
}
impl Deref for CashflowSeries {
type Target = Vec<CashflowPeriod>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug)]
pub struct CashflowPeriod {
period: u32,
rate: f64,
due_at_beginning: bool,
payment: f64,
payments_to_date: f64,
payments_remaining: f64,
principal: f64,
principal_to_date: f64,
principal_remaining: f64,
interest: f64,
interest_to_date: f64,
interest_remaining: f64,
formula: String,
symbolic_formula: String,
}
impl CashflowPeriod {
pub(crate) fn new(
period: u32,
rate: f64,
due_at_beginning: bool,
payment: f64,
payments_to_date: f64,
payments_remaining: f64,
principal: f64,
principal_to_date: f64,
principal_remaining: f64,
interest: f64,
interest_to_date: f64,
interest_remaining: f64,
formula: String,
symbolic_formula: String,
) -> Self {
Self {
period,
rate,
due_at_beginning,
payment,
payments_to_date,
payments_remaining,
principal,
principal_to_date,
principal_remaining,
interest,
interest_to_date,
interest_remaining,
formula,
symbolic_formula,
}
}
pub fn rate(&self) -> f64 {
self.rate
}
pub fn period(&self) -> u32 {
self.period
}
pub fn payment(&self) -> f64 {
self.payment
}
pub fn payments_to_date(&self) -> f64 {
self.payments_to_date
}
pub fn payments_remaining(&self) -> f64 {
self.payments_remaining
}
pub fn principal(&self) -> f64 {
self.principal
}
pub fn principal_to_date(&self) -> f64 {
self.principal_to_date
}
pub fn principal_remaining(&self) -> f64 {
self.principal_remaining
}
pub fn interest(&self) -> f64 {
self.interest
}
pub fn interest_to_date(&self) -> f64 {
self.interest_to_date
}
pub fn interest_remaining(&self) -> f64 {
self.interest_remaining
}
pub fn due_at_beginning(&self) -> bool {
self.due_at_beginning
}
pub fn formula(&self) -> &str {
&self.formula
}
pub fn symbolic_formula(&self) -> &str {
&self.symbolic_formula
}
pub fn print_flat(&self, precision: usize) {
println!("CashflowPeriod = {{ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }}",
&format!("period: {}", self.period),
&format!("due_at_beginning: {}", self.due_at_beginning),
&format!("payment: {:.prec$}", self.payment, prec = precision),
&format!("payments_to_date: {:.prec$}", self.payments_to_date, prec = precision),
&format!("payments_remaining: {:.prec$}", self.payments_remaining, prec = precision),
&format!("principal: {:.prec$}", self.principal, prec = precision),
&format!("principal_to_date: {:.prec$}", self.principal_to_date, prec = precision),
&format!("principal_remaining: {:.prec$}", self.principal_remaining, prec = precision),
&format!("interest: {:.prec$}", self.interest, prec = precision),
&format!("interest_to_date: {:.prec$}", self.interest_to_date, prec = precision),
&format!("interest_remaining: {:.prec$}", self.interest_remaining, prec = precision),
&format!("formula: {:?}", self.formula),
&format!("symbolic_formula: {:?}", self.symbolic_formula));
}
}