diff options
author | Guillermo Ramos | 2025-02-04 23:49:42 +0100 |
---|---|---|
committer | Guillermo Ramos | 2025-02-06 00:13:11 +0100 |
commit | 72adba0b4521ceb3bd910044b5fb4318f22aa53d (patch) | |
tree | f950844f173279ae2f58cc74123c86ed80f15750 | |
parent | 4b49c17f91196405ed34535336147d91a805bcf5 (diff) | |
download | hiccup-72adba0b4521ceb3bd910044b5fb4318f22aa53d.tar.gz |
Big refactor: split simulation & pp
-rw-r--r-- | src/lib.rs | 189 | ||||
-rw-r--r-- | src/main.rs | 15 |
2 files changed, 129 insertions, 75 deletions
@@ -1,18 +1,102 @@ use std::collections::HashMap; use std::fmt; -#[derive(Debug, Clone)] +pub struct Simulation { + history: Vec<Mortgage>, + updates: MortgageUpdates, + topay_total: f64, + topay_interest: f64, + payed_principal: f64, + payed_interest: f64, + payed_amortized: f64, +} + +impl Simulation { + fn run(&mut self, mut mortgage: Mortgage) { + while mortgage.quotas > 0 && mortgage.principal > 0. { + self.history.push(mortgage.clone()); + self.apply_updates(&mut mortgage); + self.payed_principal += mortgage.quota_principal(); + self.payed_interest += mortgage.quota_interest(); + mortgage.step(); + } + } + + fn apply_updates(&mut self, mortgage: &mut Mortgage) { + match self.updates.get_mut(&mortgage.period) { + Some(MortgageUpdate::Amortize(principal)) => { + if *principal > mortgage.principal { + *principal = mortgage.principal; + }; + mortgage.principal -= *principal; + mortgage.monthly = + Mortgage::monthly(mortgage.principal, mortgage.i12, mortgage.quotas); + + self.payed_amortized += *principal; + } + None => (), + } + } + + pub fn render_table(&self) { + let mortgage = &self.history[0]; + + println!("\n========================================================"); + println!( + "=== HIPOTECA: {}€, A {} AÑOS, INTERÉS FIJO {:.2}% ===", + mortgage.principal, + mortgage.quotas / 12, + mortgage.i12 * 12. * 100., + ); + println!("========================================================"); + + println!("\n\n# A PRIORI\n"); + println!( + "== Total a pagar: {:.2} ({} cap + {:.2} int)", + self.topay_total, mortgage.principal, self.topay_interest + ); + println!( + "== Los intereses suponen un {:.2}% del total", + 100. * self.topay_interest / self.topay_total + ); + + println!("\n\n# SIMULACIÓN CUOTAS\n"); + println!("Year | Month | Quota\t(Intrst + Amrtzd)\tPrincipal"); + for mortgage in self.history.iter() { + print!("{mortgage}"); + if let Some(update) = self.updates.get(&mortgage.period) { + print!(" {update}"); + } + println!(); + } + + let payed_total = self.payed_principal + self.payed_interest + self.payed_amortized; + + println!("\n\n# RESULTADO FINAL\n"); + println!( + "== Total pagado: {:.2} ({:.2} cap + {:.2} int + {:.2} amortizado)", + payed_total, self.payed_principal, self.payed_interest, self.payed_amortized + ); + println!( + "== Los intereses suponen un {:.2}% del total", + 100. * self.payed_interest / payed_total + ); + println!(); + } +} + +#[derive(Clone)] pub struct Mortgage { period: u32, principal: f64, i12: f64, - quota: f64, + monthly: f64, quotas: u32, } impl fmt::Display for Mortgage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!( + write!( f, "{:4} | {:5} | {:4.2}\t({:.2} + {:.2})\t{:.2}", if self.period % 12 == 0 { @@ -22,7 +106,7 @@ impl fmt::Display for Mortgage { "".to_string() }, self.period, - self.quota, + self.monthly, self.quota_interest(), self.quota_principal(), self.principal @@ -38,88 +122,37 @@ impl Mortgage { period: 0, principal, i12, - quota: Self::quota(principal, i12, quotas), + monthly: Self::monthly(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(); - } + pub fn simulate(&mut self, updates: MortgageUpdates) -> Simulation { + let topay_total = self.monthly * self.quotas as f64; - payed_total += payed_amortized; + let mut sim = Simulation { + topay_total, + topay_interest: topay_total - self.principal, + payed_principal: 0., + payed_interest: 0., + payed_amortized: 0., + history: vec![], + updates, + }; - 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!(); - } + sim.run(self.clone()); - 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 - } - } + sim } - fn quota(principal: f64, i12: f64, quotas: u32) -> f64 { + fn monthly(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; + self.principal = (1.0 + self.i12) * self.principal - self.monthly; } fn quota_interest(&self) -> f64 { @@ -127,12 +160,24 @@ impl Mortgage { } fn quota_principal(&self) -> f64 { - self.quota - self.quota_interest() + self.monthly - self.quota_interest() } } pub type MortgageUpdates = HashMap<u32, MortgageUpdate>; +#[derive(Clone)] pub enum MortgageUpdate { Amortize(f64), } + +impl fmt::Display for MortgageUpdate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[MORTGAGE UPDATE: ")?; + match self { + Self::Amortize(principal) => { + write!(f, "{principal:.2} amortized to reduce quotas]") + } + } + } +} diff --git a/src/main.rs b/src/main.rs index c114f2a..66ce418 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,14 +3,23 @@ use hiccup::MortgageUpdate::*; use hiccup::MortgageUpdates; use std::collections::HashMap; -fn main() { +fn h1() { let m = Mortgage::new(390_000., 0.028, 30); let updates: MortgageUpdates = HashMap::from_iter((0..29).map(|y| match y { 0 => (0, Amortize(30_000.)), _ => (y * 12, Amortize(12_000.)), })); - m.clone().run(updates); + let sim = m.clone().simulate(updates); + sim.render_table(); +} - // m.clone().run(HashMap::new()); +fn h2() { + let m = Mortgage::new(200_000., 0.01621, 30); + let sim = m.clone().simulate(HashMap::new()); + sim.render_table(); +} + +fn main() { + h1(); } |