diff options
author | Guillermo Ramos | 2025-02-06 23:52:05 +0100 |
---|---|---|
committer | Guillermo Ramos | 2025-02-07 00:49:43 +0100 |
commit | 2e7780b7c13f43b61b026cfd803e0f904ef04878 (patch) | |
tree | 8fb5134e9b80d23c21c7784a4478afa417298293 /src/lib.rs | |
parent | ca057d600ca54b3efebd6770dbe52acb38d9a25f (diff) | |
download | hiccup-2e7780b7c13f43b61b026cfd803e0f904ef04878.tar.gz |
Final refactor before doing actual work
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 200 |
1 files changed, 112 insertions, 88 deletions
@@ -1,14 +1,33 @@ use std::collections::HashMap; use std::fmt; +use std::ops::AddAssign; + +#[derive(Clone, Copy)] +pub struct Capital { + principal: f64, + interest: f64, +} + +impl Capital { + fn total(&self) -> f64 { + self.principal + self.interest + } +} + +// TODO generate other operations using macros +impl AddAssign for Capital { + fn add_assign(&mut self, other: Self) { + self.principal += other.principal; + self.interest += other.interest; + } +} pub struct Simulation { - mortgage: Mortgage, - updates: MortgageUpdates, + st: SimState, + updates: SimUpdates, history: Vec<Quota>, - topay_total: f64, - topay_interest: f64, - payed_principal: f64, - payed_interest: f64, + topay: Capital, + payed: Capital, payed_amortized: f64, } @@ -17,50 +36,53 @@ impl Simulation { let pending_quotas = years * 12; let i12 = i1 / 12.0; - let mortgage = Mortgage { + let mut st = SimState { period: 0, principal, i12, - monthly: Mortgage::monthly(principal, i12, pending_quotas), + monthly: 0., pending_quotas, }; + st.calculate_monthly(); - let topay_total = mortgage.monthly * pending_quotas as f64; + let topay_total = st.monthly * pending_quotas as f64; Simulation { - mortgage, - topay_total, - topay_interest: topay_total - principal, - payed_principal: 0., - payed_interest: 0., + st, + topay: Capital { + principal, + interest: topay_total - principal, + }, + payed: Capital { + principal: 0., + interest: 0., + }, payed_amortized: 0., history: vec![], updates: HashMap::new(), } } - pub fn run(&mut self, updates: MortgageUpdates) { + pub fn run(&mut self, updates: SimUpdates) { self.updates = updates; - let mut mortgage = self.mortgage.clone(); - while mortgage.pending_quotas > 0 && mortgage.principal > 0. { - let quota = mortgage.step(); - self.payed_principal += quota.principal; - self.payed_interest += quota.interest; + let mut st = self.st.clone(); + while st.pending_quotas > 0 && st.principal > 0. { + let quota = st.step(); + self.payed += quota.payed; self.history.push(quota); - self.apply_updates(&mut mortgage); + self.apply_updates(&mut st); } } - 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; + fn apply_updates(&mut self, st: &mut SimState) { + match self.updates.get_mut(&st.period) { + Some(SimUpdate::Amortize(principal)) => { + if *principal > st.principal { + *principal = st.principal; }; - mortgage.principal -= *principal; - mortgage.monthly = - Mortgage::monthly(mortgage.principal, mortgage.i12, mortgage.pending_quotas); - + st.principal -= *principal; + st.calculate_monthly(); + self.payed.principal += *principal; self.payed_amortized += *principal; } None => (), @@ -68,82 +90,57 @@ impl Simulation { } pub fn render_table(&self) { - let mortgage = &self.mortgage; + let st = &self.st; println!("\n========================================================"); println!( "=== HIPOTECA: {}€, A {} AÑOS, INTERÉS FIJO {:.2}% ===", - mortgage.principal, - mortgage.pending_quotas / 12, - mortgage.i12 * 12. * 100., + st.principal, + st.pending_quotas / 12, + st.i12 * 12. * 100., ); println!("========================================================"); println!("\n\n# SIMULACIÓN CUOTAS\n"); println!("Year | Mon | Quota ( Amrtzd + Intrst) | Pending"); - for mortgage in self.history.iter() { - print!("{mortgage}"); - if let Some(update) = self.updates.get(&mortgage.period) { + for st in self.history.iter() { + print!("{st}"); + if let Some(update) = self.updates.get(&st.period) { print!(" {update}"); } println!(); } - let payed_total = self.payed_principal + self.payed_interest + self.payed_amortized; - println!("\n\n# A PRIORI\n"); println!( "== Total a pagar: {:.2} ({:.2} cap + {:.2} int)", - self.topay_total, mortgage.principal, self.topay_interest + self.topay.total(), + self.topay.principal, + self.topay.interest ); println!( "== Los intereses suponen un {:.2}% del total", - 100. * self.topay_interest / self.topay_total + 100. * self.topay.interest / self.topay.total() ); 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 + "== Total pagado: {:.2} ({:.2} en cuotas + {:.2} int + {:.2} amortizado)", + self.payed.total(), + self.payed.principal - self.payed_amortized, + self.payed.interest, + self.payed_amortized ); println!( "== Los intereses suponen un {:.2}% del total", - 100. * self.payed_interest / payed_total + 100. * self.payed.interest / self.payed.total() ); println!(); } } #[derive(Clone)] -pub struct Quota { - period: u32, - interest: f64, - principal: f64, - pending_principal: f64, -} - -impl fmt::Display for Quota { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:4} | {:3} | {:7.2} ({:7.2} + {:7.2}) | {:10.2}", - if (self.period - 1) % 12 == 0 { - format!("Y{}", (self.period / 12) + 1) - } else { - // return Ok(()); - "".to_string() - }, - self.period, - self.interest + self.principal, - self.principal, - self.interest, - self.pending_principal - ) - } -} - -#[derive(Clone)] -pub struct Mortgage { +struct SimState { period: u32, principal: f64, i12: f64, @@ -151,14 +148,23 @@ pub struct Mortgage { pending_quotas: u32, } -impl Mortgage { - fn monthly(principal: f64, i12: f64, pending_quotas: u32) -> f64 { - principal * i12 / (1.0 - (1.0 + i12).powi(-(pending_quotas as i32))) +impl SimState { + fn calculate_monthly(&mut self) { + self.monthly = + self.principal * self.i12 / (1.0 - (1.0 + self.i12).powi(-(self.pending_quotas as i32))) + } + + fn capital(&self) -> Capital { + let interest = self.i12 * self.principal; + let principal = self.monthly - interest; + Capital { + principal, + interest, + } } fn step(&mut self) -> Quota { - let interest = self.quota_interest(); - let principal = self.quota_principal(); + let capital = self.capital(); self.period += 1; self.pending_quotas -= 1; @@ -166,29 +172,47 @@ impl Mortgage { Quota { period: self.period, - interest, - principal, + payed: capital, pending_principal: self.principal, } } +} - fn quota_interest(&self) -> f64 { - self.i12 * self.principal - } +#[derive(Clone)] +pub struct Quota { + period: u32, + payed: Capital, + pending_principal: f64, +} - fn quota_principal(&self) -> f64 { - self.monthly - self.quota_interest() +impl fmt::Display for Quota { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:4} | {:3} | {:7.2} ({:7.2} + {:7.2}) | {:10.2}", + if (self.period - 1) % 12 == 0 { + format!("Y{}", (self.period / 12) + 1) + } else { + // return Ok(()); + "".to_string() + }, + self.period, + self.payed.total(), + self.payed.principal, + self.payed.interest, + self.pending_principal + ) } } -pub type MortgageUpdates = HashMap<u32, MortgageUpdate>; +pub type SimUpdates = HashMap<u32, SimUpdate>; #[derive(Clone)] -pub enum MortgageUpdate { +pub enum SimUpdate { Amortize(f64), } -impl fmt::Display for MortgageUpdate { +impl fmt::Display for SimUpdate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[MORTGAGE UPDATE: ")?; match self { |