# naive amortization implementation using enumerators. # used for a possible answer to compare against candidate solutions # intended to determine how a candidate breaks up a problem # similar haskell implementation - https://gist.github.com/tippenein/8dceb2d50272ec24ce13fe83f3a57bfa module Amortizable include Enumerable def estimated_monthly r = apr / 100 / 12 # percent per period top = r * (1 + r) ** term bottom = ((1 + r) ** term) - 1 principal * (top / bottom) end def amortization principal_month_pairs.map do |pair| Amortization.new(*pair, apr) end end private def principal_month_pairs principals = principal_enumerator_from(principal).take(self.term) months = month_enumerator_from(self.created_at).take(self.term) principals.zip(months) end def principal_enumerator_from(initial_principal) Enumerator.new do |g| s = initial_principal while s > 0 do next_principal = s - estimated_monthly g.yield next_principal s = next_principal end end end def month_enumerator_from(start) Enumerator.new do |g| s = start loop do next_month = 1.month.since(s) g.yield next_month s = next_month end end end class Amortization attr_accessor :date, :principal, :interest, :apr def initialize(principal, date, apr) @principal = principal > 0 ? principal : 0 @date = date @apr = apr @interest = interest_from(@principal) end def interest_from(principal) apr_percent = apr / 100 (principal * (apr_percent / 365) * 30).round(2) end end end