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 { writeln!( f, "{:4} | {:5} | {:4.2}\t({:.2} + {:.2})\t{:.2}", if self.period % 12 == 0 { format!("Y{}", (self.period / 12) + 1) } else { // return Ok(()); "".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: MortgageUpdates) { 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!("\n========================================================"); println!( "=== HIPOTECA: {}€, A {} AÑOS, INTERÉS FIJO {:.2}% ===", self.principal, self.quotas / 12, self.i12 * 12. * 100., ); println!("========================================================"); println!("\n\n# A PRIORI\n"); 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\n# SIMULACIÓN CUOTAS\n"); println!("Year | Month | Quota\t(Intrst + Amrtzd)\tPrincipal"); while self.quotas > 0 && self.principal > 0. { print!("{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\n# RESULTADO FINAL\n"); 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!(); } 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 type MortgageUpdates = HashMap; pub enum MortgageUpdate { Amortize(f64), }