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 {  | 
