finance_solution/cashflow/
mod.rs

1#![allow(unused_imports)]
2
3//! The internal module which supports the solution struct for the Cashflow family of functions (e.g., `payment`).
4
5// use std::fmt::Debug;
6use std::fmt;
7// use colored::*;
8
9// Import needed for the function references in the Rustdoc comments.
10use crate::*;
11use std::cmp::max;
12use std::ops::Deref;
13
14pub mod future_value_annuity;
15#[doc(inline)]
16pub use future_value_annuity::*;
17
18pub mod payment;
19#[doc(inline)]
20pub use payment::*;
21
22pub mod present_value_annuity;
23#[doc(inline)]
24pub use present_value_annuity::*;
25
26pub mod net_present_value;
27#[doc(inline)]
28pub use net_present_value::*;
29
30pub mod nper;
31#[doc(inline)]
32pub use nper::*;
33
34#[derive(Debug, Clone)]
35pub enum CashflowVariable {
36    PresentValueAnnuity,
37    PresentValueAnnuityDue,
38    FutureValueAnnuity,
39    Payment,
40    FutureValueAnnuityDue,
41    NetPresentValue,
42}
43
44impl CashflowVariable {
45    /// Returns true if the variant is CashflowVariable::PresentValueAnnuity indicating that the
46    /// solution was created by calculating the present value of an annuity with the payment due at
47    /// the end of the month.
48    pub fn is_present_value_annuity(&self) -> bool {
49        match self {
50            CashflowVariable::PresentValueAnnuity => true,
51            _ => false,
52        }
53    }
54
55    /// Returns true if the variant is CashflowVariable::FutureValueAnnuity indicating that the
56    /// solution was created by calculating the future value of an annuity with the payment due at
57    /// the end of the month.
58    pub fn is_future_value_annuity(&self) -> bool {
59        match self {
60            CashflowVariable::FutureValueAnnuity => true,
61            _ => false,
62        }
63    }
64
65    /// Returns true if the variant is CashflowVariable::Payment indicating that the solution
66    /// was created in a call to [`payment_solution`].
67    pub fn is_payment(&self) -> bool {
68        match self {
69            CashflowVariable::Payment => true,
70            _ => false,
71        }
72    }
73
74    /// Returns true if the variant is CashflowVariable::PresentValueAnnuityDue indicating that
75    /// the solution was created by calculating the present value of an annuity with the payment due
76    /// at the beginning of the month.
77    pub fn is_present_value_annuity_due(&self) -> bool {
78        match self {
79            CashflowVariable::PresentValueAnnuityDue => true,
80            _ => false,
81        }
82    }
83
84    /// Returns true if the variant is CashflowVariable::FutureValueAnnuityDue indicating that
85    /// the solution was created by calculating the future value of an annuity with the payment due
86    /// at the beginning of the month.
87    pub fn is_future_value_annuity_due(&self) -> bool {
88        match self {
89            CashflowVariable::FutureValueAnnuityDue => true,
90            _ => false,
91        }
92    }
93
94    /// Returns true if the variant is CashflowVariable::NetPresentValue indicating that the
95    /// solution was created by calculating a net present value.
96    pub fn is_net_present_value(&self) -> bool {
97        match self {
98            CashflowVariable::NetPresentValue => true,
99            _ => false,
100        }
101    }
102}
103
104impl fmt::Display for CashflowVariable {
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        match *self {
107            CashflowVariable::PresentValueAnnuity => write!(f, "Present Value Annuity"),
108            CashflowVariable::FutureValueAnnuity => write!(f, "Future Value Annuity"),
109            CashflowVariable::Payment => write!(f, "Payment"),
110            CashflowVariable::PresentValueAnnuityDue => write!(f, "Present Value Annuity Due"),
111            CashflowVariable::FutureValueAnnuityDue => write!(f, "Future Value Annuity Due"),
112            CashflowVariable::NetPresentValue => write!(f, "Net Present Value"),
113        }
114    }
115}
116
117/// A record of a cash flow calculation such as payment, net present value, or the present value or
118/// future value of an annuity.
119#[derive(Clone, Debug)]
120pub struct CashflowSolution {
121    calculated_field: CashflowVariable,
122    rate: f64,
123    periods: u32,
124    present_value: f64,
125    future_value: f64,
126    due_at_beginning: bool,
127    payment: f64,
128    sum_of_payments: f64,
129    sum_of_interest: f64,
130    formula: String,
131    symbolic_formula: String,
132    // pub input_in_percent: String,
133}
134
135impl CashflowSolution {
136    pub(crate) fn new(
137        calculated_field: CashflowVariable,
138        rate: f64,
139        periods: u32,
140        present_value: f64,
141        future_value: f64,
142        due_at_beginning: bool,
143        payment: f64,
144        formula: &str,
145        symbolic_formula: &str,
146    ) -> Self {
147        assert!(!formula.is_empty());
148        let sum_of_payments = payment * periods as f64;
149        let sum_of_interest = sum_of_payments + present_value + future_value;
150        Self {
151            calculated_field,
152            rate,
153            periods,
154            present_value,
155            future_value,
156            due_at_beginning,
157            payment,
158            sum_of_payments,
159            sum_of_interest,
160            formula: formula.to_string(),
161            symbolic_formula: symbolic_formula.to_string(),
162        }
163    }
164
165    pub fn calculated_field(&self) -> &CashflowVariable {
166        &self.calculated_field
167    }
168
169    pub fn rate(&self) -> f64 {
170        self.rate
171    }
172
173    pub fn periods(&self) -> u32 {
174        self.periods
175    }
176
177    pub fn present_value(&self) -> f64 {
178        self.present_value
179    }
180
181    pub fn future_value(&self) -> f64 {
182        self.future_value
183    }
184
185    pub fn due_at_beginning(&self) -> bool {
186        self.due_at_beginning
187    }
188
189    pub fn payment(&self) -> f64 {
190        self.payment
191    }
192
193    pub fn sum_of_payments(&self) -> f64 {
194        self.sum_of_payments
195    }
196
197    pub fn sum_of_interest(&self) -> f64 {
198        self.sum_of_interest
199    }
200
201    pub fn formula(&self) -> &str {
202        &self.formula
203    }
204
205    pub fn symbolic_formula(&self) -> &str {
206        &self.symbolic_formula
207    }
208
209}
210
211/*
212impl Debug for CashflowSolution {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        write!(f, "{{{}{}{}{}{}{}{}{}{}{}{}\n}}",
215               &format!("\n\tcalculated_field: {}", self.calculated_field.to_string().magenta()),
216               &format!("\n\trate (r): {}", format!("{:?}", self.rate).yellow()),
217               &format!("\n\tperiods (n): {}", self.periods.to_string().yellow()),
218               &format!("\n\tpresent_value (pv): {}", self.present_value),
219               &format!("\n\tfuture_value (fv): {}", self.future_value),
220               &format!("\n\tdue_at_beginning: {}", self.due_at_beginning),
221               //    if self.calculated_field.is_net_present_value() { format!("\n\tcashflow: {}", self.cashflow.to_string().red()) } else { "".to_string() },
222               //    if self.calculated_field.is_net_present_value() { format!("\n\tcashflow_0: {}", self.cashflow_0.to_string().red()) } else { "".to_string() },
223               &format!("\n\tpayment (pmt): {}", if self.calculated_field.is_payment() || self.calculated_field.is_payment_due() { self.payment.to_string().green() } else { self.payment.to_string().normal() }),
224               &format!("\n\tsum_of_payments: {}", self.sum_of_payments),
225               &format!("\n\tsum_of_interest: {}", self.sum_of_interest),
226               &format!("\n\tformula: {:?}", self.formula),
227               &format!("\n\tsymbolic_formula: {:?}", self.symbolic_formula),
228               // &format!("input_in_percent: {:.6}%", self.input_in_percent),
229               // &format!("output: {}", self.output.to_string().green()),
230        )
231    }
232}
233*/
234
235#[derive(Clone, Debug)]
236pub struct CashflowSeries(Vec<CashflowPeriod>);
237
238impl CashflowSeries {
239    pub(crate) fn new(series: Vec<CashflowPeriod>) -> Self {
240        Self {
241            0: series,
242        }
243    }
244
245    pub fn filter<P>(&self, predicate: P) -> Self
246        where P: Fn(&&CashflowPeriod) -> bool
247    {
248        Self {
249            0: self.iter().filter(|x| predicate(x)).cloned().collect()
250        }
251    }
252
253    pub fn print_table(
254        &self,
255        include_running_totals: bool,
256        include_remaining_amounts: bool)
257    {
258        self.print_table_locale_opt(include_running_totals, include_remaining_amounts, None, None);
259    }
260
261    pub fn print_table_locale(
262        &self,
263        include_running_totals: bool,
264        include_remaining_amounts: bool,
265        locale: &num_format::Locale,
266        precision: usize) {
267        self.print_table_locale_opt(include_running_totals, include_remaining_amounts, Some(locale), Some(precision));
268    }
269
270    fn print_table_locale_opt(
271        &self,
272        include_running_totals: bool,
273        include_remaining_amounts: bool,
274        locale: Option<&num_format::Locale>,
275        precision: Option<usize>)
276    {
277        let columns = columns_with_strings(&[
278            ("period", "i", true),
279            ("payments_to_date", "f", include_running_totals), ("payments_remaining", "f", include_remaining_amounts),
280            ("principal", "f", true), ("principal_to_date", "f", include_running_totals), ("principal_remaining", "f", include_remaining_amounts),
281            ("interest", "f", true), ("interest_to_date", "f", include_running_totals), ("interest_remaining", "f", include_remaining_amounts)]);
282        let data = self.iter()
283            .map(|entry| vec![entry.period.to_string(), entry.payments_to_date.to_string(), entry.payments_remaining.to_string(),
284                              entry.principal.to_string(), entry.principal_to_date.to_string(), entry.principal_remaining.to_string(),
285                              entry.interest.to_string(), entry.interest_to_date.to_string(), entry.interest_remaining.to_string()])
286            .collect::<Vec<_>>();
287        print_table_locale_opt(&columns, data, locale, precision);
288    }
289
290    pub fn print_ab_comparison(
291        &self,
292        other: &CashflowSeries,
293        include_running_totals: bool,
294        include_remaining_amounts: bool)
295    {
296        self.print_ab_comparison_locale_opt(other, include_running_totals, include_remaining_amounts, None, None);
297    }
298
299    pub fn print_ab_comparison_locale(
300            &self,
301            other: &CashflowSeries,
302            include_running_totals: bool,
303            include_remaining_amounts: bool,
304            locale: &num_format::Locale,
305            precision: usize) {
306        self.print_ab_comparison_locale_opt(other, include_running_totals, include_remaining_amounts, Some(locale), Some(precision));
307    }
308
309    pub(crate) fn print_ab_comparison_locale_opt(
310            &self,
311            other: &CashflowSeries,
312            include_running_totals: bool,
313            include_remaining_amounts: bool,
314            locale: Option<&num_format::Locale>,
315            precision: Option<usize>) {
316        let columns = columns_with_strings(&[("period", "i", true),
317                           ("payment_a", "f", true), ("payment_b", "f", true),
318                           ("pmt_to_date_a", "f", include_running_totals), ("pmt_to_date_b", "f", include_running_totals),
319            // ("pmt_remaining_a", "f", include_remaining_amounts), ("pmt_remaining_b", "f", include_remaining_amounts),
320            ("pmt_remaining_a", "f", false), ("pmt_remaining_b", "f", false),
321                           ("principal_a", "f", true), ("principal_b", "f", true),
322                           ("princ_to_date_a", "f", include_running_totals), ("princ_to_date_b", "f", include_running_totals),
323                           ("princ_remaining_a", "f", include_remaining_amounts), ("princ_remaining_b", "f", include_remaining_amounts),
324                           ("interest_a", "f", false), ("interest_b", "f", false),
325                           ("int_to_date_a", "f", include_running_totals), ("int_to_date_b", "f", include_running_totals),
326                           ("int_remaining_a", "f", false), ("int_remaining_b", "f", false)]);
327        let mut data = vec![];
328        let rows = max(self.len(), other.len());
329        for row_index in 0..rows {
330            data.push(vec![
331                (row_index + 1).to_string(),
332                self.get(row_index).map_or("".to_string(), |x| x.payment.to_string()),
333                other.get(row_index).map_or("".to_string(), |x| x.payment.to_string()),
334                self.get(row_index).map_or("".to_string(), |x| x.payments_to_date.to_string()),
335                other.get(row_index).map_or("".to_string(), |x| x.payments_to_date.to_string()),
336                self.get(row_index).map_or("".to_string(), |x| x.payments_remaining.to_string()),
337                other.get(row_index).map_or("".to_string(), |x| x.payments_remaining.to_string()),
338                self.get(row_index).map_or("".to_string(), |x| x.principal.to_string()),
339                other.get(row_index).map_or("".to_string(), |x| x.principal.to_string()),
340                self.get(row_index).map_or("".to_string(), |x| x.principal_to_date.to_string()),
341                other.get(row_index).map_or("".to_string(), |x| x.principal_to_date.to_string()),
342                self.get(row_index).map_or("".to_string(), |x| x.principal_remaining.to_string()),
343                other.get(row_index).map_or("".to_string(), |x| x.principal_remaining.to_string()),
344                self.get(row_index).map_or("".to_string(), |x| x.interest.to_string()),
345                other.get(row_index).map_or("".to_string(), |x| x.interest.to_string()),
346                self.get(row_index).map_or("".to_string(), |x| x.interest_to_date.to_string()),
347                other.get(row_index).map_or("".to_string(), |x| x.interest_to_date.to_string()),
348                self.get(row_index).map_or("".to_string(), |x| x.interest_remaining.to_string()),
349                other.get(row_index).map_or("".to_string(), |x| x.interest_remaining.to_string()),
350            ]);
351        }
352        print_table_locale_opt(&columns, data, locale, precision);
353    }
354
355}
356
357impl Deref for CashflowSeries {
358    type Target = Vec<CashflowPeriod>;
359
360    fn deref(&self) -> &Self::Target {
361        &self.0
362    }
363}
364
365#[derive(Clone, Debug)]
366pub struct CashflowPeriod {
367    period: u32,
368    rate: f64,
369    due_at_beginning: bool,
370    // pub cashflow: f64,
371    // pub cashflow_0: f64,
372    payment: f64,
373    payments_to_date: f64,
374    payments_remaining: f64,
375    principal: f64,
376    principal_to_date: f64,
377    principal_remaining: f64,
378    interest: f64,
379    interest_to_date: f64,
380    interest_remaining: f64,
381    formula: String,
382    symbolic_formula: String,
383    // pub input_in_percent: String,
384}
385
386impl CashflowPeriod {
387    pub(crate) fn new(
388        period: u32,
389        rate: f64,
390        due_at_beginning: bool,
391        payment: f64,
392        payments_to_date: f64,
393        payments_remaining: f64,
394        principal: f64,
395        principal_to_date: f64,
396        principal_remaining: f64,
397        interest: f64,
398        interest_to_date: f64,
399        interest_remaining: f64,
400        formula: String,
401        symbolic_formula: String,
402    ) -> Self {
403        Self {
404            period,
405            rate,
406            due_at_beginning,
407            payment,
408            payments_to_date,
409            payments_remaining,
410            principal,
411            principal_to_date,
412            principal_remaining,
413            interest,
414            interest_to_date,
415            interest_remaining,
416            formula,
417            symbolic_formula,
418        }
419    }
420
421    pub fn rate(&self) -> f64 {
422        self.rate
423    }
424
425    pub fn period(&self) -> u32 {
426        self.period
427    }
428
429    pub fn payment(&self) -> f64 {
430        self.payment
431    }
432
433    pub fn payments_to_date(&self) -> f64 {
434        self.payments_to_date
435    }
436
437    pub fn payments_remaining(&self) -> f64 {
438        self.payments_remaining
439    }
440
441    pub fn principal(&self) -> f64 {
442        self.principal
443    }
444
445    pub fn principal_to_date(&self) -> f64 {
446        self.principal_to_date
447    }
448
449    pub fn principal_remaining(&self) -> f64 {
450        self.principal_remaining
451    }
452
453    pub fn interest(&self) -> f64 {
454        self.interest
455    }
456
457    pub fn interest_to_date(&self) -> f64 {
458        self.interest_to_date
459    }
460
461    pub fn interest_remaining(&self) -> f64 {
462        self.interest_remaining
463    }
464
465    pub fn due_at_beginning(&self) -> bool {
466        self.due_at_beginning
467    }
468
469    pub fn formula(&self) -> &str {
470        &self.formula
471    }
472
473    pub fn symbolic_formula(&self) -> &str {
474        &self.symbolic_formula
475    }
476
477    pub fn print_flat(&self, precision: usize) {
478        println!("CashflowPeriod = {{ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }}",
479                 &format!("period: {}", self.period),
480                 &format!("due_at_beginning: {}", self.due_at_beginning),
481                 &format!("payment: {:.prec$}", self.payment, prec = precision),
482                 &format!("payments_to_date: {:.prec$}", self.payments_to_date, prec = precision),
483                 &format!("payments_remaining: {:.prec$}", self.payments_remaining, prec = precision),
484                 &format!("principal: {:.prec$}", self.principal, prec = precision),
485                 &format!("principal_to_date: {:.prec$}", self.principal_to_date, prec = precision),
486                 &format!("principal_remaining: {:.prec$}", self.principal_remaining, prec = precision),
487                 &format!("interest: {:.prec$}", self.interest, prec = precision),
488                 &format!("interest_to_date: {:.prec$}", self.interest_to_date, prec = precision),
489                 &format!("interest_remaining: {:.prec$}", self.interest_remaining, prec = precision),
490                 &format!("formula: {:?}", self.formula),
491                 &format!("symbolic_formula: {:?}", self.symbolic_formula));
492    }
493}
494
495// pub fn print_series_filtered(series: &[TvmPeriod], filter: )
496
497/*
498pub fn print_series_table(series: &[CashflowPeriod], precision: usize) {
499    if series.len() == 0 {
500        return;
501    }
502    let period_width = max("period".len(), series.iter().map(|x| x.period().to_string().len()).max().unwrap());
503    let payments_to_date_width = max("payments_to_date".len(), series.iter().map(|x| format!("{:.prec$}", x.payments_to_date(), prec = precision).len()).max().unwrap());
504    let payments_remaining_width = max("payments_remaining".len(), series.iter().map(|x| format!("{:.prec$}", x.payments_remaining(), prec = precision).len()).max().unwrap());
505    let principal_width = max("principal_width".len(), series.iter().map(|x| format!("{:.prec$}", x.principal(), prec = precision).len()).max().unwrap());
506    let principal_to_date_width = max("principal_to_date".len(), series.iter().map(|x| format!("{:.prec$}", x.principal_to_date(), prec = precision).len()).max().unwrap());
507    let principal_remaining_width = max("principal_remaining".len(), series.iter().map(|x| format!("{:.prec$}", x.principal_remaining(), prec = precision).len()).max().unwrap());
508    let interest_width = max("interest".len(), series.iter().map(|x| format!("{:.prec$}", x.interest(), prec = precision).len()).max().unwrap());
509    let interest_to_date_width = max("interest_to_date".len(), series.iter().map(|x| format!("{:.prec$}", x.interest_to_date(), prec = precision).len()).max().unwrap());
510    let interest_remaining_width = max("interest_remaining".len(), series.iter().map(|x| format!("{:.prec$}", x.interest_remaining(), prec = precision).len()).max().unwrap());
511    println!("\ndue_at_beginning: {}", series[0].due_at_beginning);
512    println!("payment: {:.prec$}", series[0].payment, prec = precision);
513    println!("{:>pe$}  {:>pmtd$}  {:>pmr$}  {:>pr$}  {:>prtd$}  {:>prr$}  {:>i$}  {:>itd$}  {:>ir$}",
514             "period", "payments_to_date", "payments_remaining", "principal", "principal_to_date", "principal_remaining", "interest", "interest_to_date", "interest_remaining",
515             pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
516             pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
517             i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width);
518    println!("{}  {}  {}  {}  {}  {}  {}  {}  {}",
519             "-".repeat(period_width), "-".repeat(payments_to_date_width), "-".repeat(payments_remaining_width),
520             "-".repeat(principal_width), "-".repeat(principal_to_date_width), "-".repeat(principal_remaining_width),
521             "-".repeat(interest_width), "-".repeat(interest_to_date_width), "-".repeat(interest_remaining_width));
522    for entry in series.iter() {
523        println!("{:>pe$}  {:>pmtd$.prec$}  {:>pmr$.prec$}  {:>pr$.prec$}  {:>prtd$.prec$}  {:>prr$.prec$}  {:>i$.prec$}  {:>itd$.prec$}  {:>ir$.prec$}",
524                 entry.period(), entry.payments_to_date(), entry.payments_remaining(),
525                 entry.principal(), entry.principal_to_date(), entry.principal_remaining(),
526                 entry.interest(), entry.interest_to_date(), entry.interest_remaining(),
527                 pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
528                 pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
529                 i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width, prec = precision);
530    }
531}
532*/
533
534/*
535pub fn print_series_table_locale(series: &[CashflowPeriod], locale: &num_format::Locale, precision: usize) {
536    if series.len() == 0 {
537        return;
538    }
539    let period_width = max("period".len(), series.iter().map(|x| format_int_locale(x.period(), locale).len()).max().unwrap());
540    let payments_to_date_width = max("payments_to_date".len(), series.iter().map(|x| format_float_locale(x.payments_to_date(), locale, precision).len()).max().unwrap());
541    let payments_remaining_width = max("payments_remaining".len(), series.iter().map(|x| format_float_locale(x.payments_remaining(), locale, precision).len()).max().unwrap());
542    let principal_width = max("principal".len(), series.iter().map(|x| format_float_locale(x.principal(), locale, precision).len()).max().unwrap());
543    let principal_to_date_width = max("principal_to_date".len(), series.iter().map(|x| format_float_locale(x.principal_to_date(), locale, precision).len()).max().unwrap());
544    let principal_remaining_width = max("principal_remaining".len(), series.iter().map(|x| format_float_locale(x.principal_remaining(), locale, precision).len()).max().unwrap());
545    let interest_width = max("interest".len(), series.iter().map(|x| format_float_locale(x.interest(), locale, precision).len()).max().unwrap());
546    let interest_to_date_width = max("interest_to_date".len(), series.iter().map(|x| format_float_locale(x.interest_to_date(), locale, precision).len()).max().unwrap());
547    let interest_remaining_width = max("interest_remaining".len(), series.iter().map(|x| format_float_locale(x.interest_remaining(), locale, precision).len()).max().unwrap());
548    println!("\ndue_at_beginning: {}", series[0].due_at_beginning);
549    println!("payment: {:.prec$}", series[0].payment, prec = precision);
550    println!("{:>pe$}  {:>pmtd$}  {:>pmr$}  {:>pr$}  {:>prtd$}  {:>prr$}  {:>i$}  {:>itd$}  {:>ir$}",
551             "period", "payments_to_date", "payments_remaining", "principal", "principal_to_date", "principal_remaining", "interest", "interest_to_date", "interest_remaining",
552             pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
553             pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
554             i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width);
555    println!("{}  {}  {}  {}  {}  {}  {}  {}  {}",
556             "-".repeat(period_width), "-".repeat(payments_to_date_width), "-".repeat(payments_remaining_width),
557             "-".repeat(principal_width), "-".repeat(principal_to_date_width), "-".repeat(principal_remaining_width),
558             "-".repeat(interest_width), "-".repeat(interest_to_date_width), "-".repeat(interest_remaining_width));
559    for entry in series.iter() {
560        println!("{:>pe$}  {:>pmtd$}  {:>pmr$}  {:>pr$}  {:>prtd$}  {:>prr$}  {:>i$}  {:>itd$}  {:>ir$}",
561                 format_int_locale(entry.period(), locale), format_float_locale(entry.payments_to_date(), locale, precision), format_float_locale(entry.payments_remaining(), locale, precision),
562                 format_float_locale(entry.principal(), locale, precision), format_float_locale(entry.principal_to_date(), locale, precision), format_float_locale(entry.principal_remaining(), locale, precision),
563                 format_float_locale(entry.interest(), locale, precision), format_float_locale(entry.interest_to_date(), locale, precision), format_float_locale(entry.interest_remaining(), locale, precision),
564                 pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
565                 pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
566                 i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width);
567    }
568}
569*/
570
571/*
572pub fn print_series_table_filtered(series: &[CashflowPeriod], predicate: P, precision: usize)
573    where P: FnMut(&CashflowPeriod) -> bool
574{
575}
576*/