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/// When a level payment (annuity installment) falls within each period.
35///
36/// Prefer this enum over a bare `bool`. For ergonomics, `false` converts to
37/// [`PaymentTiming::EndOfPeriod`] and `true` to [`PaymentTiming::BeginningOfPeriod`]
38/// (Excel `type=0` / `type=1`).
39///
40/// # Examples
41/// ```
42/// use finance_solution::{payment, PaymentTiming};
43///
44/// let end = payment(0.01, 12, 1_000.0, 0.0, PaymentTiming::EndOfPeriod).unwrap();
45/// let end_bool = payment(0.01, 12, 1_000.0, 0.0, false).unwrap();
46/// assert!((end - end_bool).abs() < 1e-12);
47///
48/// let due = payment(0.01, 12, 1_000.0, 0.0, true).unwrap();
49/// assert!(due.abs() < end.abs()); // due payments are slightly smaller in magnitude
50/// ```
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
52pub enum PaymentTiming {
53 /// Payment at the end of each period (ordinary annuity). Default / Excel `type=0`.
54 #[default]
55 EndOfPeriod,
56 /// Payment at the beginning of each period (annuity due). Excel `type=1`.
57 BeginningOfPeriod,
58}
59
60impl PaymentTiming {
61 /// True when payments fall at the start of each period.
62 #[inline]
63 pub fn is_beginning(&self) -> bool {
64 matches!(self, PaymentTiming::BeginningOfPeriod)
65 }
66
67 /// True when payments fall at the end of each period.
68 #[inline]
69 pub fn is_end(&self) -> bool {
70 matches!(self, PaymentTiming::EndOfPeriod)
71 }
72}
73
74impl fmt::Display for PaymentTiming {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 match self {
77 PaymentTiming::EndOfPeriod => write!(f, "EndOfPeriod"),
78 PaymentTiming::BeginningOfPeriod => write!(f, "BeginningOfPeriod"),
79 }
80 }
81}
82
83impl From<bool> for PaymentTiming {
84 fn from(due_at_beginning: bool) -> Self {
85 if due_at_beginning {
86 PaymentTiming::BeginningOfPeriod
87 } else {
88 PaymentTiming::EndOfPeriod
89 }
90 }
91}
92
93impl From<PaymentTiming> for bool {
94 fn from(timing: PaymentTiming) -> bool {
95 timing.is_beginning()
96 }
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
100pub enum CashflowVariable {
101 PresentValueAnnuity,
102 PresentValueAnnuityDue,
103 FutureValueAnnuity,
104 Payment,
105 FutureValueAnnuityDue,
106 NetPresentValue,
107}
108
109impl CashflowVariable {
110 /// Returns true if the variant is CashflowVariable::PresentValueAnnuity indicating that the
111 /// solution was created by calculating the present value of an annuity with the payment due at
112 /// the end of the month.
113 pub fn is_present_value_annuity(&self) -> bool {
114 match self {
115 CashflowVariable::PresentValueAnnuity => true,
116 _ => false,
117 }
118 }
119
120 /// Returns true if the variant is CashflowVariable::FutureValueAnnuity indicating that the
121 /// solution was created by calculating the future value of an annuity with the payment due at
122 /// the end of the month.
123 pub fn is_future_value_annuity(&self) -> bool {
124 match self {
125 CashflowVariable::FutureValueAnnuity => true,
126 _ => false,
127 }
128 }
129
130 /// Returns true if the variant is CashflowVariable::Payment indicating that the solution
131 /// was created in a call to [`payment_solution`].
132 pub fn is_payment(&self) -> bool {
133 match self {
134 CashflowVariable::Payment => true,
135 _ => false,
136 }
137 }
138
139 /// Returns true if the variant is CashflowVariable::PresentValueAnnuityDue indicating that
140 /// the solution was created by calculating the present value of an annuity with the payment due
141 /// at the beginning of the month.
142 pub fn is_present_value_annuity_due(&self) -> bool {
143 match self {
144 CashflowVariable::PresentValueAnnuityDue => true,
145 _ => false,
146 }
147 }
148
149 /// Returns true if the variant is CashflowVariable::FutureValueAnnuityDue indicating that
150 /// the solution was created by calculating the future value of an annuity with the payment due
151 /// at the beginning of the month.
152 pub fn is_future_value_annuity_due(&self) -> bool {
153 match self {
154 CashflowVariable::FutureValueAnnuityDue => true,
155 _ => false,
156 }
157 }
158
159 /// Returns true if the variant is CashflowVariable::NetPresentValue indicating that the
160 /// solution was created by calculating a net present value.
161 pub fn is_net_present_value(&self) -> bool {
162 match self {
163 CashflowVariable::NetPresentValue => true,
164 _ => false,
165 }
166 }
167}
168
169impl fmt::Display for CashflowVariable {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171 match *self {
172 CashflowVariable::PresentValueAnnuity => write!(f, "Present Value Annuity"),
173 CashflowVariable::FutureValueAnnuity => write!(f, "Future Value Annuity"),
174 CashflowVariable::Payment => write!(f, "Payment"),
175 CashflowVariable::PresentValueAnnuityDue => write!(f, "Present Value Annuity Due"),
176 CashflowVariable::FutureValueAnnuityDue => write!(f, "Future Value Annuity Due"),
177 CashflowVariable::NetPresentValue => write!(f, "Net Present Value"),
178 }
179 }
180}
181
182/// A record of a cash flow calculation such as payment, net present value, or the present value or
183/// future value of an annuity.
184#[derive(Clone, Debug)]
185pub struct CashflowSolution {
186 calculated_field: CashflowVariable,
187 rate: f64,
188 periods: u32,
189 present_value: f64,
190 future_value: f64,
191 due_at_beginning: bool,
192 payment: f64,
193 sum_of_payments: f64,
194 sum_of_interest: f64,
195 formula: String,
196 symbolic_formula: String,
197 // pub input_in_percent: String,
198}
199
200impl CashflowSolution {
201 pub(crate) fn new(
202 calculated_field: CashflowVariable,
203 rate: f64,
204 periods: u32,
205 present_value: f64,
206 future_value: f64,
207 due_at_beginning: bool,
208 payment: f64,
209 formula: &str,
210 symbolic_formula: &str,
211 ) -> Self {
212 // Caller validated formulas and money fields before construction.
213 debug_assert!(!formula.is_empty());
214 let sum_of_payments = payment * periods as f64;
215 let sum_of_interest = sum_of_payments + present_value + future_value;
216 Self {
217 calculated_field,
218 rate,
219 periods,
220 present_value,
221 future_value,
222 due_at_beginning,
223 payment,
224 sum_of_payments,
225 sum_of_interest,
226 formula: formula.to_string(),
227 symbolic_formula: symbolic_formula.to_string(),
228 }
229 }
230
231 pub fn calculated_field(&self) -> &CashflowVariable {
232 &self.calculated_field
233 }
234
235 pub fn rate(&self) -> f64 {
236 self.rate
237 }
238
239 pub fn periods(&self) -> u32 {
240 self.periods
241 }
242
243 pub fn present_value(&self) -> f64 {
244 self.present_value
245 }
246
247 pub fn future_value(&self) -> f64 {
248 self.future_value
249 }
250
251 pub fn due_at_beginning(&self) -> bool {
252 self.due_at_beginning
253 }
254
255 pub fn payment(&self) -> f64 {
256 self.payment
257 }
258
259 pub fn sum_of_payments(&self) -> f64 {
260 self.sum_of_payments
261 }
262
263 pub fn sum_of_interest(&self) -> f64 {
264 self.sum_of_interest
265 }
266
267 pub fn formula(&self) -> &str {
268 &self.formula
269 }
270
271 pub fn symbolic_formula(&self) -> &str {
272 &self.symbolic_formula
273 }
274}
275
276/*
277impl Debug for CashflowSolution {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 write!(f, "{{{}{}{}{}{}{}{}{}{}{}{}\n}}",
280 &format!("\n\tcalculated_field: {}", self.calculated_field.to_string().magenta()),
281 &format!("\n\trate (r): {}", format!("{:?}", self.rate).yellow()),
282 &format!("\n\tperiods (n): {}", self.periods.to_string().yellow()),
283 &format!("\n\tpresent_value (pv): {}", self.present_value),
284 &format!("\n\tfuture_value (fv): {}", self.future_value),
285 &format!("\n\tdue_at_beginning: {}", self.due_at_beginning),
286 // if self.calculated_field.is_net_present_value() { format!("\n\tcashflow: {}", self.cashflow.to_string().red()) } else { "".to_string() },
287 // if self.calculated_field.is_net_present_value() { format!("\n\tcashflow_0: {}", self.cashflow_0.to_string().red()) } else { "".to_string() },
288 &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() }),
289 &format!("\n\tsum_of_payments: {}", self.sum_of_payments),
290 &format!("\n\tsum_of_interest: {}", self.sum_of_interest),
291 &format!("\n\tformula: {:?}", self.formula),
292 &format!("\n\tsymbolic_formula: {:?}", self.symbolic_formula),
293 // &format!("input_in_percent: {:.6}%", self.input_in_percent),
294 // &format!("output: {}", self.output.to_string().green()),
295 )
296 }
297}
298*/
299
300#[derive(Clone, Debug)]
301pub struct CashflowSeries(Vec<CashflowPeriod>);
302
303impl CashflowSeries {
304 pub(crate) fn new(series: Vec<CashflowPeriod>) -> Self {
305 Self { 0: series }
306 }
307
308 pub fn filter<P>(&self, predicate: P) -> Self
309 where
310 P: Fn(&&CashflowPeriod) -> bool,
311 {
312 Self {
313 0: self.iter().filter(|x| predicate(x)).cloned().collect(),
314 }
315 }
316
317 pub fn print_table(&self, include_running_totals: bool, include_remaining_amounts: bool) {
318 self.print_table_locale_opt(
319 include_running_totals,
320 include_remaining_amounts,
321 None,
322 None,
323 );
324 }
325
326 pub fn print_table_locale(
327 &self,
328 include_running_totals: bool,
329 include_remaining_amounts: bool,
330 locale: &num_format::Locale,
331 precision: usize,
332 ) {
333 self.print_table_locale_opt(
334 include_running_totals,
335 include_remaining_amounts,
336 Some(locale),
337 Some(precision),
338 );
339 }
340
341 fn print_table_locale_opt(
342 &self,
343 include_running_totals: bool,
344 include_remaining_amounts: bool,
345 locale: Option<&num_format::Locale>,
346 precision: Option<usize>,
347 ) {
348 let columns = columns_with_strings(&[
349 ("period", "i", true),
350 ("payments_to_date", "f", include_running_totals),
351 ("payments_remaining", "f", include_remaining_amounts),
352 ("principal", "f", true),
353 ("principal_to_date", "f", include_running_totals),
354 ("principal_remaining", "f", include_remaining_amounts),
355 ("interest", "f", true),
356 ("interest_to_date", "f", include_running_totals),
357 ("interest_remaining", "f", include_remaining_amounts),
358 ]);
359 let data = self
360 .iter()
361 .map(|entry| {
362 vec![
363 entry.period.to_string(),
364 entry.payments_to_date.to_string(),
365 entry.payments_remaining.to_string(),
366 entry.principal.to_string(),
367 entry.principal_to_date.to_string(),
368 entry.principal_remaining.to_string(),
369 entry.interest.to_string(),
370 entry.interest_to_date.to_string(),
371 entry.interest_remaining.to_string(),
372 ]
373 })
374 .collect::<Vec<_>>();
375 print_table_locale_opt(&columns, data, locale, precision);
376 }
377
378 pub fn print_ab_comparison(
379 &self,
380 other: &CashflowSeries,
381 include_running_totals: bool,
382 include_remaining_amounts: bool,
383 ) {
384 self.print_ab_comparison_locale_opt(
385 other,
386 include_running_totals,
387 include_remaining_amounts,
388 None,
389 None,
390 );
391 }
392
393 pub fn print_ab_comparison_locale(
394 &self,
395 other: &CashflowSeries,
396 include_running_totals: bool,
397 include_remaining_amounts: bool,
398 locale: &num_format::Locale,
399 precision: usize,
400 ) {
401 self.print_ab_comparison_locale_opt(
402 other,
403 include_running_totals,
404 include_remaining_amounts,
405 Some(locale),
406 Some(precision),
407 );
408 }
409
410 pub(crate) fn print_ab_comparison_locale_opt(
411 &self,
412 other: &CashflowSeries,
413 include_running_totals: bool,
414 include_remaining_amounts: bool,
415 locale: Option<&num_format::Locale>,
416 precision: Option<usize>,
417 ) {
418 let columns = columns_with_strings(&[
419 ("period", "i", true),
420 ("payment_a", "f", true),
421 ("payment_b", "f", true),
422 ("pmt_to_date_a", "f", include_running_totals),
423 ("pmt_to_date_b", "f", include_running_totals),
424 // ("pmt_remaining_a", "f", include_remaining_amounts), ("pmt_remaining_b", "f", include_remaining_amounts),
425 ("pmt_remaining_a", "f", false),
426 ("pmt_remaining_b", "f", false),
427 ("principal_a", "f", true),
428 ("principal_b", "f", true),
429 ("princ_to_date_a", "f", include_running_totals),
430 ("princ_to_date_b", "f", include_running_totals),
431 ("princ_remaining_a", "f", include_remaining_amounts),
432 ("princ_remaining_b", "f", include_remaining_amounts),
433 ("interest_a", "f", false),
434 ("interest_b", "f", false),
435 ("int_to_date_a", "f", include_running_totals),
436 ("int_to_date_b", "f", include_running_totals),
437 ("int_remaining_a", "f", false),
438 ("int_remaining_b", "f", false),
439 ]);
440 let mut data = vec![];
441 let rows = max(self.len(), other.len());
442 for row_index in 0..rows {
443 data.push(vec![
444 (row_index + 1).to_string(),
445 self.get(row_index)
446 .map_or("".to_string(), |x| x.payment.to_string()),
447 other
448 .get(row_index)
449 .map_or("".to_string(), |x| x.payment.to_string()),
450 self.get(row_index)
451 .map_or("".to_string(), |x| x.payments_to_date.to_string()),
452 other
453 .get(row_index)
454 .map_or("".to_string(), |x| x.payments_to_date.to_string()),
455 self.get(row_index)
456 .map_or("".to_string(), |x| x.payments_remaining.to_string()),
457 other
458 .get(row_index)
459 .map_or("".to_string(), |x| x.payments_remaining.to_string()),
460 self.get(row_index)
461 .map_or("".to_string(), |x| x.principal.to_string()),
462 other
463 .get(row_index)
464 .map_or("".to_string(), |x| x.principal.to_string()),
465 self.get(row_index)
466 .map_or("".to_string(), |x| x.principal_to_date.to_string()),
467 other
468 .get(row_index)
469 .map_or("".to_string(), |x| x.principal_to_date.to_string()),
470 self.get(row_index)
471 .map_or("".to_string(), |x| x.principal_remaining.to_string()),
472 other
473 .get(row_index)
474 .map_or("".to_string(), |x| x.principal_remaining.to_string()),
475 self.get(row_index)
476 .map_or("".to_string(), |x| x.interest.to_string()),
477 other
478 .get(row_index)
479 .map_or("".to_string(), |x| x.interest.to_string()),
480 self.get(row_index)
481 .map_or("".to_string(), |x| x.interest_to_date.to_string()),
482 other
483 .get(row_index)
484 .map_or("".to_string(), |x| x.interest_to_date.to_string()),
485 self.get(row_index)
486 .map_or("".to_string(), |x| x.interest_remaining.to_string()),
487 other
488 .get(row_index)
489 .map_or("".to_string(), |x| x.interest_remaining.to_string()),
490 ]);
491 }
492 print_table_locale_opt(&columns, data, locale, precision);
493 }
494}
495
496impl Deref for CashflowSeries {
497 type Target = Vec<CashflowPeriod>;
498
499 fn deref(&self) -> &Self::Target {
500 &self.0
501 }
502}
503
504#[derive(Clone, Debug)]
505pub struct CashflowPeriod {
506 period: u32,
507 rate: f64,
508 due_at_beginning: bool,
509 // pub cashflow: f64,
510 // pub cashflow_0: f64,
511 payment: f64,
512 payments_to_date: f64,
513 payments_remaining: f64,
514 principal: f64,
515 principal_to_date: f64,
516 principal_remaining: f64,
517 interest: f64,
518 interest_to_date: f64,
519 interest_remaining: f64,
520 formula: String,
521 symbolic_formula: String,
522 // pub input_in_percent: String,
523}
524
525impl CashflowPeriod {
526 pub(crate) fn new(
527 period: u32,
528 rate: f64,
529 due_at_beginning: bool,
530 payment: f64,
531 payments_to_date: f64,
532 payments_remaining: f64,
533 principal: f64,
534 principal_to_date: f64,
535 principal_remaining: f64,
536 interest: f64,
537 interest_to_date: f64,
538 interest_remaining: f64,
539 formula: String,
540 symbolic_formula: String,
541 ) -> Self {
542 Self {
543 period,
544 rate,
545 due_at_beginning,
546 payment,
547 payments_to_date,
548 payments_remaining,
549 principal,
550 principal_to_date,
551 principal_remaining,
552 interest,
553 interest_to_date,
554 interest_remaining,
555 formula,
556 symbolic_formula,
557 }
558 }
559
560 pub fn rate(&self) -> f64 {
561 self.rate
562 }
563
564 pub fn period(&self) -> u32 {
565 self.period
566 }
567
568 pub fn payment(&self) -> f64 {
569 self.payment
570 }
571
572 pub fn payments_to_date(&self) -> f64 {
573 self.payments_to_date
574 }
575
576 pub fn payments_remaining(&self) -> f64 {
577 self.payments_remaining
578 }
579
580 pub fn principal(&self) -> f64 {
581 self.principal
582 }
583
584 pub fn principal_to_date(&self) -> f64 {
585 self.principal_to_date
586 }
587
588 pub fn principal_remaining(&self) -> f64 {
589 self.principal_remaining
590 }
591
592 pub fn interest(&self) -> f64 {
593 self.interest
594 }
595
596 pub fn interest_to_date(&self) -> f64 {
597 self.interest_to_date
598 }
599
600 pub fn interest_remaining(&self) -> f64 {
601 self.interest_remaining
602 }
603
604 pub fn due_at_beginning(&self) -> bool {
605 self.due_at_beginning
606 }
607
608 pub fn formula(&self) -> &str {
609 &self.formula
610 }
611
612 pub fn symbolic_formula(&self) -> &str {
613 &self.symbolic_formula
614 }
615
616 pub fn print_flat(&self, precision: usize) {
617 println!(
618 "CashflowPeriod = {{ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }}",
619 &format!("period: {}", self.period),
620 &format!("due_at_beginning: {}", self.due_at_beginning),
621 &format!("payment: {:.prec$}", self.payment, prec = precision),
622 &format!(
623 "payments_to_date: {:.prec$}",
624 self.payments_to_date,
625 prec = precision
626 ),
627 &format!(
628 "payments_remaining: {:.prec$}",
629 self.payments_remaining,
630 prec = precision
631 ),
632 &format!("principal: {:.prec$}", self.principal, prec = precision),
633 &format!(
634 "principal_to_date: {:.prec$}",
635 self.principal_to_date,
636 prec = precision
637 ),
638 &format!(
639 "principal_remaining: {:.prec$}",
640 self.principal_remaining,
641 prec = precision
642 ),
643 &format!("interest: {:.prec$}", self.interest, prec = precision),
644 &format!(
645 "interest_to_date: {:.prec$}",
646 self.interest_to_date,
647 prec = precision
648 ),
649 &format!(
650 "interest_remaining: {:.prec$}",
651 self.interest_remaining,
652 prec = precision
653 ),
654 &format!("formula: {:?}", self.formula),
655 &format!("symbolic_formula: {:?}", self.symbolic_formula)
656 );
657 }
658}
659
660// pub fn print_series_filtered(series: &[TvmPeriod], filter: )
661
662/*
663pub fn print_series_table(series: &[CashflowPeriod], precision: usize) {
664 if series.len() == 0 {
665 return;
666 }
667 let period_width = max("period".len(), series.iter().map(|x| x.period().to_string().len()).max().unwrap());
668 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());
669 let payments_remaining_width = max("payments_remaining".len(), series.iter().map(|x| format!("{:.prec$}", x.payments_remaining(), prec = precision).len()).max().unwrap());
670 let principal_width = max("principal_width".len(), series.iter().map(|x| format!("{:.prec$}", x.principal(), prec = precision).len()).max().unwrap());
671 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());
672 let principal_remaining_width = max("principal_remaining".len(), series.iter().map(|x| format!("{:.prec$}", x.principal_remaining(), prec = precision).len()).max().unwrap());
673 let interest_width = max("interest".len(), series.iter().map(|x| format!("{:.prec$}", x.interest(), prec = precision).len()).max().unwrap());
674 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());
675 let interest_remaining_width = max("interest_remaining".len(), series.iter().map(|x| format!("{:.prec$}", x.interest_remaining(), prec = precision).len()).max().unwrap());
676 println!("\ndue_at_beginning: {}", series[0].due_at_beginning);
677 println!("payment: {:.prec$}", series[0].payment, prec = precision);
678 println!("{:>pe$} {:>pmtd$} {:>pmr$} {:>pr$} {:>prtd$} {:>prr$} {:>i$} {:>itd$} {:>ir$}",
679 "period", "payments_to_date", "payments_remaining", "principal", "principal_to_date", "principal_remaining", "interest", "interest_to_date", "interest_remaining",
680 pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
681 pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
682 i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width);
683 println!("{} {} {} {} {} {} {} {} {}",
684 "-".repeat(period_width), "-".repeat(payments_to_date_width), "-".repeat(payments_remaining_width),
685 "-".repeat(principal_width), "-".repeat(principal_to_date_width), "-".repeat(principal_remaining_width),
686 "-".repeat(interest_width), "-".repeat(interest_to_date_width), "-".repeat(interest_remaining_width));
687 for entry in series.iter() {
688 println!("{:>pe$} {:>pmtd$.prec$} {:>pmr$.prec$} {:>pr$.prec$} {:>prtd$.prec$} {:>prr$.prec$} {:>i$.prec$} {:>itd$.prec$} {:>ir$.prec$}",
689 entry.period(), entry.payments_to_date(), entry.payments_remaining(),
690 entry.principal(), entry.principal_to_date(), entry.principal_remaining(),
691 entry.interest(), entry.interest_to_date(), entry.interest_remaining(),
692 pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
693 pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
694 i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width, prec = precision);
695 }
696}
697*/
698
699/*
700pub fn print_series_table_locale(series: &[CashflowPeriod], locale: &num_format::Locale, precision: usize) {
701 if series.len() == 0 {
702 return;
703 }
704 let period_width = max("period".len(), series.iter().map(|x| format_int_locale(x.period(), locale).len()).max().unwrap());
705 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());
706 let payments_remaining_width = max("payments_remaining".len(), series.iter().map(|x| format_float_locale(x.payments_remaining(), locale, precision).len()).max().unwrap());
707 let principal_width = max("principal".len(), series.iter().map(|x| format_float_locale(x.principal(), locale, precision).len()).max().unwrap());
708 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());
709 let principal_remaining_width = max("principal_remaining".len(), series.iter().map(|x| format_float_locale(x.principal_remaining(), locale, precision).len()).max().unwrap());
710 let interest_width = max("interest".len(), series.iter().map(|x| format_float_locale(x.interest(), locale, precision).len()).max().unwrap());
711 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());
712 let interest_remaining_width = max("interest_remaining".len(), series.iter().map(|x| format_float_locale(x.interest_remaining(), locale, precision).len()).max().unwrap());
713 println!("\ndue_at_beginning: {}", series[0].due_at_beginning);
714 println!("payment: {:.prec$}", series[0].payment, prec = precision);
715 println!("{:>pe$} {:>pmtd$} {:>pmr$} {:>pr$} {:>prtd$} {:>prr$} {:>i$} {:>itd$} {:>ir$}",
716 "period", "payments_to_date", "payments_remaining", "principal", "principal_to_date", "principal_remaining", "interest", "interest_to_date", "interest_remaining",
717 pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
718 pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
719 i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width);
720 println!("{} {} {} {} {} {} {} {} {}",
721 "-".repeat(period_width), "-".repeat(payments_to_date_width), "-".repeat(payments_remaining_width),
722 "-".repeat(principal_width), "-".repeat(principal_to_date_width), "-".repeat(principal_remaining_width),
723 "-".repeat(interest_width), "-".repeat(interest_to_date_width), "-".repeat(interest_remaining_width));
724 for entry in series.iter() {
725 println!("{:>pe$} {:>pmtd$} {:>pmr$} {:>pr$} {:>prtd$} {:>prr$} {:>i$} {:>itd$} {:>ir$}",
726 format_int_locale(entry.period(), locale), format_float_locale(entry.payments_to_date(), locale, precision), format_float_locale(entry.payments_remaining(), locale, precision),
727 format_float_locale(entry.principal(), locale, precision), format_float_locale(entry.principal_to_date(), locale, precision), format_float_locale(entry.principal_remaining(), locale, precision),
728 format_float_locale(entry.interest(), locale, precision), format_float_locale(entry.interest_to_date(), locale, precision), format_float_locale(entry.interest_remaining(), locale, precision),
729 pe = period_width, pmtd = payments_to_date_width, pmr = payments_remaining_width,
730 pr = principal_width, prtd = principal_to_date_width, prr = principal_remaining_width,
731 i = interest_width, itd = interest_to_date_width, ir = interest_remaining_width);
732 }
733}
734*/
735
736/*
737pub fn print_series_table_filtered(series: &[CashflowPeriod], predicate: P, precision: usize)
738 where P: FnMut(&CashflowPeriod) -> bool
739{
740}
741*/