solver 0.0.0 → 0.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.
- data/VERSION +1 -1
- data/lib/solver.rb +1 -1
- data/lib/solver/tvm.rb +72 -0
- data/solver.gemspec +6 -3
- data/test/test_tvm.rb +58 -0
- metadata +5 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.0
|
data/lib/solver.rb
CHANGED
data/lib/solver/tvm.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module Flt::Solver
|
2
|
+
|
3
|
+
# A Time-Value-of-Money solver
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
# tvm = TVM.new(Tolerance(3, :decimals), Float.context)
|
7
|
+
# puts tvm.solve(:t=>240, :m0=>10000, :m=>0, :i=>3, :p=>12).inspect # => {:pmt=>-55.45975978539105}
|
8
|
+
#
|
9
|
+
class TVM
|
10
|
+
|
11
|
+
def initialize(tol, context=Float.context)
|
12
|
+
@context = context
|
13
|
+
@var_descriptions = {
|
14
|
+
:m=>'money value at time t',
|
15
|
+
:t=>'time',
|
16
|
+
:m0=>'initial money value',
|
17
|
+
:pmt=>'payment per time unit',
|
18
|
+
:i=>'percent interest per year',
|
19
|
+
:p=>'numer of time units per year'
|
20
|
+
}
|
21
|
+
@vars = @var_descriptions.keys
|
22
|
+
vars = @vars
|
23
|
+
tvm = self
|
24
|
+
@solver = PSolver.new(context, tol) do |m, t, m0, pmt, i, p|
|
25
|
+
tvm.equation(m, t, m0, pmt, i, p)
|
26
|
+
end
|
27
|
+
@solver.default_guesses = 1,2
|
28
|
+
@one = @context.Num(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
def parameter_descriptions
|
32
|
+
@var_descriptions
|
33
|
+
end
|
34
|
+
|
35
|
+
# Parameters:
|
36
|
+
# :t time in periods
|
37
|
+
# :p number of periods per year
|
38
|
+
# :i percent yearly interest rate
|
39
|
+
# :pmt payment per period
|
40
|
+
# :m0 initial value
|
41
|
+
# :m value at time :t
|
42
|
+
def solve(parameters)
|
43
|
+
nil_vars = @vars.select{|var| parameters[var].nil?}
|
44
|
+
raise "Too many unknowns" if nil_vars.size>1
|
45
|
+
raise "Nothing to solve" if nil_vars.empty?
|
46
|
+
var = nil_vars.first
|
47
|
+
# determine sensible initial value? => parameters[var] = initial_value
|
48
|
+
{var=>@solver.root(var, parameters)}
|
49
|
+
end
|
50
|
+
|
51
|
+
def value(parameters)
|
52
|
+
@solver.equation_value(paramters)
|
53
|
+
end
|
54
|
+
|
55
|
+
def equation(m, t, m0, pmt, i, p)
|
56
|
+
i /= 100
|
57
|
+
i /= p
|
58
|
+
n = -t
|
59
|
+
k = @context.exp(lnp1(i)*n) # (i+1)**n
|
60
|
+
# Equation: -m*k = m0 + pmt*(1-k)/i
|
61
|
+
m0 + pmt*(@one-k)/i + m*k
|
62
|
+
end
|
63
|
+
|
64
|
+
# ln(x+1)
|
65
|
+
def lnp1(x)
|
66
|
+
v = x + 1
|
67
|
+
(v == 1) ? x : (x*@context.ln(v) / (v - 1))
|
68
|
+
end
|
69
|
+
|
70
|
+
end # TVM
|
71
|
+
|
72
|
+
end # Flt::Solver
|
data/solver.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{solver}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Javier Goizueta"]
|
@@ -29,12 +29,14 @@ Gem::Specification.new do |s|
|
|
29
29
|
"lib/solver/psolver.rb",
|
30
30
|
"lib/solver/rfsecant.rb",
|
31
31
|
"lib/solver/secant.rb",
|
32
|
+
"lib/solver/tvm.rb",
|
32
33
|
"solver.gemspec",
|
33
34
|
"test/helper.rb",
|
34
35
|
"test/test_function.rb",
|
35
36
|
"test/test_psolver.rb",
|
36
37
|
"test/test_rfsecant.rb",
|
37
|
-
"test/test_secant.rb"
|
38
|
+
"test/test_secant.rb",
|
39
|
+
"test/test_tvm.rb"
|
38
40
|
]
|
39
41
|
s.homepage = %q{http://github.com/jgoizueta/solver}
|
40
42
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -47,7 +49,8 @@ Gem::Specification.new do |s|
|
|
47
49
|
"test/test_function.rb",
|
48
50
|
"test/test_psolver.rb",
|
49
51
|
"test/test_rfsecant.rb",
|
50
|
-
"test/test_secant.rb"
|
52
|
+
"test/test_secant.rb",
|
53
|
+
"test/test_tvm.rb"
|
51
54
|
]
|
52
55
|
|
53
56
|
if s.respond_to? :specification_version then
|
data/test/test_tvm.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flt/float'
|
3
|
+
require 'flt/tolerance'
|
4
|
+
|
5
|
+
include Flt
|
6
|
+
|
7
|
+
class TestTVM < Test::Unit::TestCase
|
8
|
+
|
9
|
+
context "The TVM class" do
|
10
|
+
|
11
|
+
context "using Float arithmetic" do
|
12
|
+
|
13
|
+
setup do
|
14
|
+
@context = Float.context
|
15
|
+
@tolerance = Flt.Tolerance(3,:decimals)
|
16
|
+
@delta = 5E-4
|
17
|
+
@tvm = Flt::Solver::TVM.new(@tolerance, @context)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "solve correclty for :pmt" do
|
21
|
+
sol = @tvm.solve(:t=>240, :m0=>10000, :m=>0, :i=>3, :p=>12)
|
22
|
+
assert_equal [:pmt], sol.keys
|
23
|
+
assert_in_delta -55.45975978539105, sol[:pmt], @delta
|
24
|
+
end
|
25
|
+
|
26
|
+
should "solve correclty for :t" do
|
27
|
+
sol = @tvm.solve(:pmt=>-55.45975978539105, :m0=>10000, :m=>0, :i=>3, :p=>12)
|
28
|
+
assert_equal [:t], sol.keys
|
29
|
+
assert_in_delta 240, sol[:t], @delta
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "using DecNum arithmetic" do
|
37
|
+
|
38
|
+
setup do
|
39
|
+
@context = Float.context
|
40
|
+
@tolerance = Flt.Tolerance(3,:decimals)
|
41
|
+
@delta = 5E-4
|
42
|
+
@tvm = Flt::Solver::TVM.new(@tolerance, @context)
|
43
|
+
end
|
44
|
+
|
45
|
+
should "solve correclty for :pmt" do
|
46
|
+
sol = @tvm.solve(:t=>240, :m0=>10000, :m=>0, :i=>3, :p=>12)
|
47
|
+
assert_equal [:pmt], sol.keys
|
48
|
+
assert_in_delta @context.Num('-55.45975978539105'), sol[:pmt], @delta
|
49
|
+
end
|
50
|
+
|
51
|
+
should "solve correclty for :t" do
|
52
|
+
sol = @tvm.solve(:pmt=>-55.45975978539105, :m0=>10000, :m=>0, :i=>3, :p=>12)
|
53
|
+
assert_equal [:t], sol.keys
|
54
|
+
assert_in_delta 240, sol[:t], @delta
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.0
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Javier Goizueta
|
@@ -67,12 +67,14 @@ files:
|
|
67
67
|
- lib/solver/psolver.rb
|
68
68
|
- lib/solver/rfsecant.rb
|
69
69
|
- lib/solver/secant.rb
|
70
|
+
- lib/solver/tvm.rb
|
70
71
|
- solver.gemspec
|
71
72
|
- test/helper.rb
|
72
73
|
- test/test_function.rb
|
73
74
|
- test/test_psolver.rb
|
74
75
|
- test/test_rfsecant.rb
|
75
76
|
- test/test_secant.rb
|
77
|
+
- test/test_tvm.rb
|
76
78
|
has_rdoc: true
|
77
79
|
homepage: http://github.com/jgoizueta/solver
|
78
80
|
licenses: []
|
@@ -113,3 +115,4 @@ test_files:
|
|
113
115
|
- test/test_psolver.rb
|
114
116
|
- test/test_rfsecant.rb
|
115
117
|
- test/test_secant.rb
|
118
|
+
- test/test_tvm.rb
|