aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 7f8a58abc7925e1452cd89035a370e62314cbb73 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::collections::HashMap;
use std::fmt;

#[derive(Debug, Clone)]
pub struct Mortgage {
    period: u32,
    principal: f64,
    i12: f64,
    quota: f64,
    quotas: u32,
}

impl fmt::Display for Mortgage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{:3} | {:5} | {:4.2}\t{:.2}\t{:.2}\t{:.2}",
            if self.period % 12 == 0 {
                format!("Y{}", (self.period / 12) + 1)
            } else {
                "".to_string()
            },
            self.period,
            self.quota,
            self.quota_interest(),
            self.quota_principal(),
            self.principal
        )
    }
}

impl Mortgage {
    pub fn new(principal: f64, i1: f64, years: u32) -> Self {
        let quotas = years * 12;
        let i12 = i1 / 12.0;
        Mortgage {
            period: 0,
            principal,
            i12,
            quota: Self::quota(principal, i12, quotas),
            quotas,
        }
    }

    pub fn run(&mut self, updates: HashMap<u32, MortgageUpdate>) {
        let principal = self.principal;

        let topay_total = self.quota * self.quotas as f64;
        let topay_interest = topay_total - principal;

        let mut payed_total = 0.;
        let mut payed_principal = 0.;
        let mut payed_interest = 0.;
        let mut payed_amortized = 0.;

        println!("Año | Cuota | c\t\tIn\tAn\tCn");
        while self.quotas > 0 && self.principal > 0. {
            println!("{self}");
            if let Some(update) = updates.get(&self.period) {
                payed_amortized += self.update(update);
            }
            payed_total += self.quota;
            payed_principal += self.quota_principal();
            payed_interest += self.quota_interest();
            self.step();
        }

        payed_total += payed_amortized;

        println!("\n# A PRIORI");
        println!(
            "== Total a pagar: {:.2} ({} cap + {topay_interest:.2} int)",
            topay_total, principal
        );
        println!(
            "== Los intereses suponen un {:.2}% del total",
            100. * topay_interest / topay_total
        );

        println!("\n# A POSTERIORI");
        println!(
            "== Total pagado: {:.2} ({:.2} cap + {:.2} int + {:.2} amortizado)",
            payed_total, payed_principal, payed_interest, payed_amortized
        );
        println!(
            "== Los intereses suponen un {:.2}% del total",
            100. * payed_interest / payed_total
        );
        println!("\n");
    }

    fn update(&mut self, update: &MortgageUpdate) -> f64 {
        print!("  [MORTGAGE UPDATE: ");
        match update {
            MortgageUpdate::Amortize(principal) => {
                println!("{principal:.2} amortized to reduce quotas]");
                self.principal -= principal;
                self.quota = Self::quota(self.principal, self.i12, self.quotas);
                *principal
            }
        }
    }

    fn quota(principal: f64, i12: f64, quotas: u32) -> f64 {
        principal * i12 / (1.0 - (1.0 + i12).powi(-(quotas as i32)))
    }

    fn step(&mut self) {
        self.period += 1;
        self.quotas -= 1;
        self.principal = (1.0 + self.i12) * self.principal - self.quota;
    }

    fn quota_interest(&self) -> f64 {
        self.i12 * self.principal
    }

    fn quota_principal(&self) -> f64 {
        self.quota - self.quota_interest()
    }
}

pub enum MortgageUpdate {
    Amortize(f64),
}