bigdecimal 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require 'bigdecimal.so'
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'bigdecimal'
4
+
5
+ # require 'bigdecimal/jacobian'
6
+ #
7
+ # Provides methods to compute the Jacobian matrix of a set of equations at a
8
+ # point x. In the methods below:
9
+ #
10
+ # f is an Object which is used to compute the Jacobian matrix of the equations.
11
+ # It must provide the following methods:
12
+ #
13
+ # f.values(x):: returns the values of all functions at x
14
+ #
15
+ # f.zero:: returns 0.0
16
+ # f.one:: returns 1.0
17
+ # f.two:: returns 2.0
18
+ # f.ten:: returns 10.0
19
+ #
20
+ # f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
21
+ #
22
+ # x is the point at which to compute the Jacobian.
23
+ #
24
+ # fx is f.values(x).
25
+ #
26
+ module Jacobian
27
+ module_function
28
+
29
+ # Determines the equality of two numbers by comparing to zero, or using the epsilon value
30
+ def isEqual(a,b,zero=0.0,e=1.0e-8)
31
+ aa = a.abs
32
+ bb = b.abs
33
+ if aa == zero && bb == zero then
34
+ true
35
+ else
36
+ if ((a-b)/(aa+bb)).abs < e then
37
+ true
38
+ else
39
+ false
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ # Computes the derivative of f[i] at x[i].
46
+ # fx is the value of f at x.
47
+ def dfdxi(f,fx,x,i)
48
+ nRetry = 0
49
+ n = x.size
50
+ xSave = x[i]
51
+ ok = 0
52
+ ratio = f.ten*f.ten*f.ten
53
+ dx = x[i].abs/ratio
54
+ dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps)
55
+ dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps)
56
+ until ok>0 do
57
+ deriv = []
58
+ nRetry += 1
59
+ if nRetry > 100
60
+ raise "Singular Jacobian matrix. No change at x[" + i.to_s + "]"
61
+ end
62
+ dx = dx*f.two
63
+ x[i] += dx
64
+ fxNew = f.values(x)
65
+ for j in 0...n do
66
+ if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then
67
+ ok += 1
68
+ deriv <<= (fxNew[j]-fx[j])/dx
69
+ else
70
+ deriv <<= f.zero
71
+ end
72
+ end
73
+ x[i] = xSave
74
+ end
75
+ deriv
76
+ end
77
+
78
+ # Computes the Jacobian of f at x. fx is the value of f at x.
79
+ def jacobian(f,fx,x)
80
+ n = x.size
81
+ dfdx = Array.new(n*n)
82
+ for i in 0...n do
83
+ df = dfdxi(f,fx,x,i)
84
+ for j in 0...n do
85
+ dfdx[j*n+i] = df[j]
86
+ end
87
+ end
88
+ dfdx
89
+ end
90
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: false
2
+ require 'bigdecimal'
3
+
4
+ #
5
+ # Solves a*x = b for x, using LU decomposition.
6
+ #
7
+ module LUSolve
8
+ module_function
9
+
10
+ # Performs LU decomposition of the n by n matrix a.
11
+ def ludecomp(a,n,zero=0,one=1)
12
+ prec = BigDecimal.limit(nil)
13
+ ps = []
14
+ scales = []
15
+ for i in 0...n do # pick up largest(abs. val.) element in each row.
16
+ ps <<= i
17
+ nrmrow = zero
18
+ ixn = i*n
19
+ for j in 0...n do
20
+ biggst = a[ixn+j].abs
21
+ nrmrow = biggst if biggst>nrmrow
22
+ end
23
+ if nrmrow>zero then
24
+ scales <<= one.div(nrmrow,prec)
25
+ else
26
+ raise "Singular matrix"
27
+ end
28
+ end
29
+ n1 = n - 1
30
+ for k in 0...n1 do # Gaussian elimination with partial pivoting.
31
+ biggst = zero;
32
+ for i in k...n do
33
+ size = a[ps[i]*n+k].abs*scales[ps[i]]
34
+ if size>biggst then
35
+ biggst = size
36
+ pividx = i
37
+ end
38
+ end
39
+ raise "Singular matrix" if biggst<=zero
40
+ if pividx!=k then
41
+ j = ps[k]
42
+ ps[k] = ps[pividx]
43
+ ps[pividx] = j
44
+ end
45
+ pivot = a[ps[k]*n+k]
46
+ for i in (k+1)...n do
47
+ psin = ps[i]*n
48
+ a[psin+k] = mult = a[psin+k].div(pivot,prec)
49
+ if mult!=zero then
50
+ pskn = ps[k]*n
51
+ for j in (k+1)...n do
52
+ a[psin+j] -= mult.mult(a[pskn+j],prec)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ raise "Singular matrix" if a[ps[n1]*n+n1] == zero
58
+ ps
59
+ end
60
+
61
+ # Solves a*x = b for x, using LU decomposition.
62
+ #
63
+ # a is a matrix, b is a constant vector, x is the solution vector.
64
+ #
65
+ # ps is the pivot, a vector which indicates the permutation of rows performed
66
+ # during LU decomposition.
67
+ def lusolve(a,b,ps,zero=0.0)
68
+ prec = BigDecimal.limit(nil)
69
+ n = ps.size
70
+ x = []
71
+ for i in 0...n do
72
+ dot = zero
73
+ psin = ps[i]*n
74
+ for j in 0...i do
75
+ dot = a[psin+j].mult(x[j],prec) + dot
76
+ end
77
+ x <<= b[ps[i]] - dot
78
+ end
79
+ (n-1).downto(0) do |i|
80
+ dot = zero
81
+ psin = ps[i]*n
82
+ for j in (i+1)...n do
83
+ dot = a[psin+j].mult(x[j],prec) + dot
84
+ end
85
+ x[i] = (x[i]-dot).div(a[psin+i],prec)
86
+ end
87
+ x
88
+ end
89
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: false
2
+ require 'bigdecimal'
3
+
4
+ #
5
+ #--
6
+ # Contents:
7
+ # sqrt(x, prec)
8
+ # sin (x, prec)
9
+ # cos (x, prec)
10
+ # atan(x, prec) Note: |x|<1, x=0.9999 may not converge.
11
+ # PI (prec)
12
+ # E (prec) == exp(1.0,prec)
13
+ #
14
+ # where:
15
+ # x ... BigDecimal number to be computed.
16
+ # |x| must be small enough to get convergence.
17
+ # prec ... Number of digits to be obtained.
18
+ #++
19
+ #
20
+ # Provides mathematical functions.
21
+ #
22
+ # Example:
23
+ #
24
+ # require "bigdecimal/math"
25
+ #
26
+ # include BigMath
27
+ #
28
+ # a = BigDecimal((PI(100)/2).to_s)
29
+ # puts sin(a,100) # => 0.99999999999999999999......e0
30
+ #
31
+ module BigMath
32
+ module_function
33
+
34
+ # call-seq:
35
+ # sqrt(decimal, numeric) -> BigDecimal
36
+ #
37
+ # Computes the square root of +decimal+ to the specified number of digits of
38
+ # precision, +numeric+.
39
+ #
40
+ # BigMath.sqrt(BigDecimal('2'), 16).to_s
41
+ # #=> "0.1414213562373095048801688724e1"
42
+ #
43
+ def sqrt(x, prec)
44
+ x.sqrt(prec)
45
+ end
46
+
47
+ # call-seq:
48
+ # sin(decimal, numeric) -> BigDecimal
49
+ #
50
+ # Computes the sine of +decimal+ to the specified number of digits of
51
+ # precision, +numeric+.
52
+ #
53
+ # If +decimal+ is Infinity or NaN, returns NaN.
54
+ #
55
+ # BigMath.sin(BigMath.PI(5)/4, 5).to_s
56
+ # #=> "0.70710678118654752440082036563292800375e0"
57
+ #
58
+ def sin(x, prec)
59
+ raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
60
+ return BigDecimal("NaN") if x.infinite? || x.nan?
61
+ n = prec + BigDecimal.double_fig
62
+ one = BigDecimal("1")
63
+ two = BigDecimal("2")
64
+ x = -x if neg = x < 0
65
+ if x > (twopi = two * BigMath.PI(prec))
66
+ if x > 30
67
+ x %= twopi
68
+ else
69
+ x -= twopi while x > twopi
70
+ end
71
+ end
72
+ x1 = x
73
+ x2 = x.mult(x,n)
74
+ sign = 1
75
+ y = x
76
+ d = y
77
+ i = one
78
+ z = one
79
+ while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
80
+ m = BigDecimal.double_fig if m < BigDecimal.double_fig
81
+ sign = -sign
82
+ x1 = x2.mult(x1,n)
83
+ i += two
84
+ z *= (i-one) * i
85
+ d = sign * x1.div(z,m)
86
+ y += d
87
+ end
88
+ neg ? -y : y
89
+ end
90
+
91
+ # call-seq:
92
+ # cos(decimal, numeric) -> BigDecimal
93
+ #
94
+ # Computes the cosine of +decimal+ to the specified number of digits of
95
+ # precision, +numeric+.
96
+ #
97
+ # If +decimal+ is Infinity or NaN, returns NaN.
98
+ #
99
+ # BigMath.cos(BigMath.PI(4), 16).to_s
100
+ # #=> "-0.999999999999999999999999999999856613163740061349e0"
101
+ #
102
+ def cos(x, prec)
103
+ raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
104
+ return BigDecimal("NaN") if x.infinite? || x.nan?
105
+ n = prec + BigDecimal.double_fig
106
+ one = BigDecimal("1")
107
+ two = BigDecimal("2")
108
+ x = -x if x < 0
109
+ if x > (twopi = two * BigMath.PI(prec))
110
+ if x > 30
111
+ x %= twopi
112
+ else
113
+ x -= twopi while x > twopi
114
+ end
115
+ end
116
+ x1 = one
117
+ x2 = x.mult(x,n)
118
+ sign = 1
119
+ y = one
120
+ d = y
121
+ i = BigDecimal("0")
122
+ z = one
123
+ while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
124
+ m = BigDecimal.double_fig if m < BigDecimal.double_fig
125
+ sign = -sign
126
+ x1 = x2.mult(x1,n)
127
+ i += two
128
+ z *= (i-one) * i
129
+ d = sign * x1.div(z,m)
130
+ y += d
131
+ end
132
+ y
133
+ end
134
+
135
+ # call-seq:
136
+ # atan(decimal, numeric) -> BigDecimal
137
+ #
138
+ # Computes the arctangent of +decimal+ to the specified number of digits of
139
+ # precision, +numeric+.
140
+ #
141
+ # If +decimal+ is NaN, returns NaN.
142
+ #
143
+ # BigMath.atan(BigDecimal('-1'), 16).to_s
144
+ # #=> "-0.785398163397448309615660845819878471907514682065e0"
145
+ #
146
+ def atan(x, prec)
147
+ raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
148
+ return BigDecimal("NaN") if x.nan?
149
+ pi = PI(prec)
150
+ x = -x if neg = x < 0
151
+ return pi.div(neg ? -2 : 2, prec) if x.infinite?
152
+ return pi / (neg ? -4 : 4) if x.round(prec) == 1
153
+ x = BigDecimal("1").div(x, prec) if inv = x > 1
154
+ x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5
155
+ n = prec + BigDecimal.double_fig
156
+ y = x
157
+ d = y
158
+ t = x
159
+ r = BigDecimal("3")
160
+ x2 = x.mult(x,n)
161
+ while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
162
+ m = BigDecimal.double_fig if m < BigDecimal.double_fig
163
+ t = -t.mult(x2,n)
164
+ d = t.div(r,m)
165
+ y += d
166
+ r += 2
167
+ end
168
+ y *= 2 if dbl
169
+ y = pi / 2 - y if inv
170
+ y = -y if neg
171
+ y
172
+ end
173
+
174
+ # call-seq:
175
+ # PI(numeric) -> BigDecimal
176
+ #
177
+ # Computes the value of pi to the specified number of digits of precision,
178
+ # +numeric+.
179
+ #
180
+ # BigMath.PI(10).to_s
181
+ # #=> "0.3141592653589793238462643388813853786957412e1"
182
+ #
183
+ def PI(prec)
184
+ raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
185
+ n = prec + BigDecimal.double_fig
186
+ zero = BigDecimal("0")
187
+ one = BigDecimal("1")
188
+ two = BigDecimal("2")
189
+
190
+ m25 = BigDecimal("-0.04")
191
+ m57121 = BigDecimal("-57121")
192
+
193
+ pi = zero
194
+
195
+ d = one
196
+ k = one
197
+ t = BigDecimal("-80")
198
+ while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
199
+ m = BigDecimal.double_fig if m < BigDecimal.double_fig
200
+ t = t*m25
201
+ d = t.div(k,m)
202
+ k = k+two
203
+ pi = pi + d
204
+ end
205
+
206
+ d = one
207
+ k = one
208
+ t = BigDecimal("956")
209
+ while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
210
+ m = BigDecimal.double_fig if m < BigDecimal.double_fig
211
+ t = t.div(m57121,n)
212
+ d = t.div(k,m)
213
+ pi = pi + d
214
+ k = k+two
215
+ end
216
+ pi
217
+ end
218
+
219
+ # call-seq:
220
+ # E(numeric) -> BigDecimal
221
+ #
222
+ # Computes e (the base of natural logarithms) to the specified number of
223
+ # digits of precision, +numeric+.
224
+ #
225
+ # BigMath.E(10).to_s
226
+ # #=> "0.271828182845904523536028752390026306410273e1"
227
+ #
228
+ def E(prec)
229
+ raise ArgumentError, "Zero or negative precision for E" if prec <= 0
230
+ BigMath.exp(1, prec)
231
+ end
232
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: false
2
+ require "bigdecimal/ludcmp"
3
+ require "bigdecimal/jacobian"
4
+
5
+ #
6
+ # newton.rb
7
+ #
8
+ # Solves the nonlinear algebraic equation system f = 0 by Newton's method.
9
+ # This program is not dependent on BigDecimal.
10
+ #
11
+ # To call:
12
+ # n = nlsolve(f,x)
13
+ # where n is the number of iterations required,
14
+ # x is the initial value vector
15
+ # f is an Object which is used to compute the values of the equations to be solved.
16
+ # It must provide the following methods:
17
+ #
18
+ # f.values(x):: returns the values of all functions at x
19
+ #
20
+ # f.zero:: returns 0.0
21
+ # f.one:: returns 1.0
22
+ # f.two:: returns 2.0
23
+ # f.ten:: returns 10.0
24
+ #
25
+ # f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
26
+ #
27
+ # On exit, x is the solution vector.
28
+ #
29
+ module Newton
30
+ include LUSolve
31
+ include Jacobian
32
+ module_function
33
+
34
+ def norm(fv,zero=0.0) # :nodoc:
35
+ s = zero
36
+ n = fv.size
37
+ for i in 0...n do
38
+ s += fv[i]*fv[i]
39
+ end
40
+ s
41
+ end
42
+
43
+ # See also Newton
44
+ def nlsolve(f,x)
45
+ nRetry = 0
46
+ n = x.size
47
+
48
+ f0 = f.values(x)
49
+ zero = f.zero
50
+ one = f.one
51
+ two = f.two
52
+ p5 = one/two
53
+ d = norm(f0,zero)
54
+ minfact = f.ten*f.ten*f.ten
55
+ minfact = one/minfact
56
+ e = f.eps
57
+ while d >= e do
58
+ nRetry += 1
59
+ # Not yet converged. => Compute Jacobian matrix
60
+ dfdx = jacobian(f,f0,x)
61
+ # Solve dfdx*dx = -f0 to estimate dx
62
+ dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero)
63
+ fact = two
64
+ xs = x.dup
65
+ begin
66
+ fact *= p5
67
+ if fact < minfact then
68
+ raise "Failed to reduce function values."
69
+ end
70
+ for i in 0...n do
71
+ x[i] = xs[i] - dx[i]*fact
72
+ end
73
+ f0 = f.values(x)
74
+ dn = norm(f0,zero)
75
+ end while(dn>=d)
76
+ d = dn
77
+ end
78
+ nRetry
79
+ end
80
+ end