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*/