time_value 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.