aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillermo Ramos2025-02-04 23:49:42 +0100
committerGuillermo Ramos2025-02-06 00:13:11 +0100
commit72adba0b4521ceb3bd910044b5fb4318f22aa53d (patch)
treef950844f173279ae2f58cc74123c86ed80f15750
parent4b49c17f91196405ed34535336147d91a805bcf5 (diff)
downloadhiccup-72adba0b4521ceb3bd910044b5fb4318f22aa53d.tar.gz
Big refactor: split simulation & pp
-rw-r--r--src/lib.rs189
-rw-r--r--src/main.rs15
2 files changed, 129 insertions, 75 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 3e30441..7883cd9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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();
}