time_value 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c6e6b94a394a16cbd8e93aa1af3110fccff9377
4
- data.tar.gz: 032d98858c34aa563b7d1e7a1407c9fefd6673d7
3
+ metadata.gz: 8cb8d84e2f74389800bec8f0d5bf9c8858dbecf5
4
+ data.tar.gz: 009f75557b934dd9cf8bd71ad9b8d83db6889d29
5
5
  SHA512:
6
- metadata.gz: db23d74db7237412e2bc019cfa4859b1239db26d32fabcb1bdd4ebd3dc7b789d7916e5f93d9702fe92f877ad6b26b106e37d95ca77346fe24d8b7c65c7d03be4
7
- data.tar.gz: 2561d85d74677bbc4a3e77d37716bc8573219e5fb047494e4d632a00e51402b567b6544232b84f3cb7bf41782de4a4d4ecc9240dee7268767045ee5c48696dd0
6
+ metadata.gz: ea2d1631b57c1d0b95df5886981c312c8c1bc2ed7a0a704b4c3e24391e7ab7e3d1074656347e1dd321b224746756f67f3cd5cf9c7060172dab0e67ba9c5b59c9
7
+ data.tar.gz: 95e053b40b686c0d961de9991648b448b810fe0f369536ba61716f99bb159aa8bcce1ccf7d37af7a926009677cb76b53a7d7309e20ab2700288673875644fa77
@@ -1,40 +1,42 @@
1
+ # frozen_string_literal: true
1
2
  class Solver
2
3
  PRECISION = 3
3
- INTERVAL = 10 ** (-1 * PRECISION)
4
+ INTERVAL = 10**(-1 * PRECISION)
4
5
  MAX_ITERATIONS = 20
5
6
  attr_reader :time_value, :goal
6
- attr_accessor :lower_bound, :upper_bound, :upper_cap_met
7
+ attr_accessor :lower_bound, :upper_bound, :upper_cap_met, :iteration_count
7
8
 
8
- def initialize(time_value:, lower_bound: 0.00, upper_bound: nil, guess: 0.00)
9
+ def initialize(time_value:, lower_bound: 0.00, upper_bound: nil, guess: 10.00)
9
10
  @upper_bound = upper_bound || guess
10
11
  @lower_bound = lower_bound
11
12
  @time_value = time_value.dup
12
13
  @time_value.i = guess
13
14
  @goal = time_value.fv
14
15
  @upper_cap_met = false
16
+ @iteration_count = 0
15
17
  end
16
18
 
17
19
  def solve!
18
- iteration_count = 0
19
- while (upper_bound - lower_bound).round(PRECISION) > INTERVAL &&
20
- iteration_count < MAX_ITERATIONS
21
- iteration_count += 1
22
- begin
23
- result = time_value.calc_fv
24
- rescue FloatDomainError
25
- return nil
26
- end
27
- adjust_bounds!(result)
20
+ while !within_range? && iteration_count < MAX_ITERATIONS
21
+ result = time_value.calc_fv
22
+ adjust_bounds!(result: result)
28
23
  time_value.i = new_guess
24
+ self.iteration_count += 1
29
25
  end
30
26
  # TODO: This will not handle the case where the 20th iteration
31
27
  # finds the solution
32
- rate if iteration_count < MAX_ITERATIONS
28
+ return rate if iteration_count < MAX_ITERATIONS
29
+ rescue FloatDomainError
30
+ return nil
33
31
  end
34
32
 
35
33
  private
36
34
 
37
- def adjust_bounds!(result)
35
+ def within_range?
36
+ (upper_bound - lower_bound).round(PRECISION) <= INTERVAL
37
+ end
38
+
39
+ def adjust_bounds!(result:)
38
40
  if result > goal
39
41
  # interest rate too high
40
42
  self.upper_cap_met = true
@@ -1,51 +1,61 @@
1
+ # frozen_string_literal: true
1
2
  require 'solver'
2
3
 
3
4
  class TimeValue
4
5
  attr_accessor :n, :i, :pv, :pmt, :fv
5
6
 
6
7
  def initialize(n: 0, i: 0, pv: 0.0, pmt: 0.0, fv: 0.0)
7
- @n = n
8
- @i = i.to_f
9
- @pv = pv.to_f
10
- @pmt = pmt.to_f
11
- @fv = fv.to_f
8
+ @n = n
9
+ @i = i.to_f
10
+ @pv = pv.to_f
11
+ @pmt = pmt.to_f
12
+ @fv = fv.to_f
12
13
  end
13
14
 
14
- #Base formula, ordinary annuity: -PV = [FV/((1+i)^n)] + (PMT/i)*[1-(1/(1+i)^n)]
15
- def calc_pv()
16
- i = @i / 100.0
17
- #Initial contribution
18
- pvf = @fv / ((1 + i) ** @n)
19
- #Present value of annuity
20
- pva = (@pmt/i) * (1-(1/((1+i)**@n)))
21
- @pv = (pvf + pva) * (-1)
22
- #Round
23
- @pv = (@pv*100).round / 100.0
15
+ # Base formula, ordinary annuity: -PV = [FV/((1+i)^n)] +
16
+ # (PMT/i)*[1-(1/(1+i)^n)]
17
+ # rubocop:disable Metrics/AbcSize
18
+ def calc_pv
19
+ i = @i / 100.0
20
+ # Initial contribution
21
+ pvf = @fv / ((1 + i)**@n)
22
+ # Present value of annuity
23
+ pva = (@pmt / i) * (1 - (1 / ((1 + i)**@n)))
24
+ @pv = -1 * (pvf + pva)
25
+ # Round
26
+ @pv = (@pv * 100).round / 100.0
24
27
  end
28
+ # rubocop:enable Metrics/AbcSize
25
29
 
26
- def calc_fv()
27
- i = @i / 100.0
28
- #Growth of initial contribution
29
- fvp = @pv * ((1 + i) ** @n)
30
- #Growth of payments
31
- fva = @pmt * (((1 + i) ** @n)-1) / i
32
- @fv = (fvp + fva) * (-1)
33
- #Round
34
- @fv = (@fv*100).round / 100.0
30
+ def calc_fv
31
+ i = @i / 100.0
32
+ # Growth of initial contribution
33
+ fvp = @pv * ((1 + i)**@n)
34
+ # Growth of payments
35
+ fva = @pmt * (((1 + i)**@n) - 1) / i
36
+ @fv = -1 * (fvp + fva)
37
+ # Round
38
+ @fv = (@fv * 100).round / 100.0
35
39
  end
36
40
 
37
- def calc_n()
38
- i = @i / 100.0
39
- @n = (Math.log((@pmt - (i * @fv))/(@pmt + (i * @pv)))/Math.log(1 + i)).round(0)
41
+ def calc_n
42
+ i = @i / 100.0
43
+ numerator = Math.log((@pmt - (i * @fv)) / (@pmt + (i * @pv)))
44
+ denominator = Math.log(1 + i)
45
+ @n = (numerator / denominator).round(0)
40
46
  end
41
47
 
42
- def calc_pmt()
43
- i = @i / 100.0
44
- @pmt = (-i*(@fv+(@pv*((1+i)**n))))/(((1+i)**n)-1)
45
- @pmt = (@pmt*100).round / 100.0
48
+ # rubocop:disable Metrics/AbcSize
49
+ def calc_pmt
50
+ i = @i / 100.0
51
+ numerator = (-i * (@fv + (@pv * ((1 + i)**n))))
52
+ denominator = (((1 + i)**n) - 1)
53
+ @pmt = numerator / denominator
54
+ @pmt = (@pmt * 100).round / 100.0
46
55
  end
56
+ # rubocop:enable Metrics/AbcSize
47
57
 
48
- def calc_i(guess: 10.0)
58
+ def calc_i
49
59
  solver = Solver.new(time_value: self, lower_bound: 0.00, guess: 10.00)
50
60
  solver.solve!
51
61
  end
@@ -1,3 +1,3 @@
1
1
  module TimeValue
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_value
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Randall Reed, Jr.