appmath 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ =begin rdoc
2
+ ruby
3
+
4
+ Ulrich Mutze, www.ulrichmutze.de
5
+
6
+ Started 2008-12-09
7
+
8
+ Defines class AppMath::Ran.
9
+
10
+ Requires file interval.
11
+
12
+ Copyright (C) 2008 Ulrich Mutze
13
+
14
+ This program is free software: you can redistribute it and/or modify
15
+ it under the terms of the GNU General Public License as published by
16
+ the Free Software Foundation, either version 3 of the License, or
17
+ (at your option) any later version.
18
+
19
+ This program is distributed in the hope that it will be useful,
20
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ GNU General Public License for more details.
23
+
24
+ You should have received a copy of the GNU General Public License
25
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
26
+ =end
27
+
28
+ require File.join(File.dirname(__FILE__), 'interval')
29
+ #require 'interval'
30
+
31
+ module AppMath
32
+
33
+ =begin rdoc
34
+ Class of random generators.
35
+ The real numbers delivered by different random generators are intended
36
+ to be statistically independent. Each random generator has its own
37
+ interval within which the output values are uniformly distributed.
38
+ =end
39
+
40
+ class Ran
41
+
42
+ attr_reader :iv
43
+ @@seed = 137
44
+ @@swing = 133216711
45
+
46
+ # The arguments have to determine an interval in which the random values
47
+ # lie. This is named @iv. If there is no argument we have @iv = [0,1].
48
+ # If there is one argument, it has to be an instance of Iv and then
49
+ # becomes @iv. If there are two arguments they need to be convertible to
50
+ # R and then become the boundaries of @iv.
51
+ def initialize(*arg)
52
+ n = arg.size
53
+ case n
54
+ when 0
55
+ @iv = Iv.new(0,1)
56
+ when 1
57
+ a0 = arg[0]
58
+ fail "argument must be an Iv" unless a0.class == Iv
59
+ @iv = a0
60
+ when 2
61
+ a0 = arg[0]; a1 = arg[1]
62
+ @iv = Iv.new(a0,a1)
63
+ else
64
+ fail "0,1,or 2 arguments needed, not #{n}"
65
+ end
66
+ x = R.ran(@@seed)
67
+ i = (@@swing * x).to_i
68
+ @local_seed = i
69
+ @@seed += 1
70
+ end
71
+
72
+ # Returns a point (value) from interval @iv by
73
+ # 'random selection'. The underlying mechanism is the
74
+ # sine-floor generator defined in method R.ran.
75
+ def ran
76
+ p = R.ran(@local_seed)
77
+ @local_seed += 1
78
+ @iv.put(p)
79
+ end
80
+
81
+ # Returns the local seed for control purposes. Should not be needed.
82
+ def local_seed
83
+ @local_seed
84
+ end
85
+
86
+ end # class Ran
87
+
88
+ end # AppMath
@@ -0,0 +1,1648 @@
1
+ =begin rdoc
2
+ ruby
3
+
4
+ Ulrich Mutze, www.ulrichmutze.de
5
+
6
+ Started 2008-11-01 as experimentation with class BigDecimal.
7
+
8
+ Defines class AppMath::R.
9
+
10
+ Requires files bigdecimal and bigdecimal/math.
11
+
12
+ Copyright (C) 2008 Ulrich Mutze
13
+
14
+ This program is free software: you can redistribute it and/or modify
15
+ it under the terms of the GNU General Public License as published by
16
+ the Free Software Foundation, either version 3 of the License, or
17
+ (at your option) any later version.
18
+
19
+ This program is distributed in the hope that it will be useful,
20
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ GNU General Public License for more details.
23
+
24
+ You should have received a copy of the GNU General Public License
25
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
26
+ =end
27
+
28
+ # Applied mathematics. Emphasis is on precision-independent coding.
29
+ module AppMath
30
+
31
+ require 'bigdecimal'
32
+ require 'bigdecimal/math'
33
+ include BigMath
34
+
35
+ =begin rdoc
36
+ Class of real numbers which implements arbitrary precision arithmetic
37
+ together with the standard elementary transcendental functions.
38
+
39
+ Implementation is based on classes BigDecimal and module BigMath
40
+ created by Shigeo Kobayashi.
41
+
42
+ The term 'precision' is here used in one of its technical meanings:
43
+ here it is the number of decimal digits used for representing the
44
+ 'significand' of a number.
45
+ Recall that each real number can be represented uniquely as
46
+ sign * significand * 10^exponent
47
+ where
48
+ sign = +/-
49
+ significand = 0.d_1 d_2 d_3 ...., where the d_1, d_2, are
50
+ decimal digits in 0,1,2,3,4,5,6,7,8,9, but d_1 != 0
51
+ exponent is an integer number
52
+ For any natural number n, a real number 'of precision n' is one
53
+ for which the digits d_i vanish for all i > n.
54
+
55
+ Notice that specifying the precision defines an infinite subset of
56
+ the rational numbers. The infinity is due to the infinity of choices
57
+ for the exponent. In the class BigDecimal, there is the restriction
58
+ that the exponent be a Fixnum (and not a Bignum) so that there is
59
+ a well-defined large but finite number of possible exponents in
60
+ BigDecimal and so there is a well-defined finite set of BigDecimals
61
+ for any specified value of the precision.
62
+
63
+ Unlike class BigDecimal, the present class R (recall the mathematical
64
+ standard symbols N, Z, Q, R, C, H for natural, integer, rational,
65
+ real, complex, quaternionic numbers) treats precision not as a
66
+ parameter of the arithmetic operations but as a class variable:
67
+ The computational representation of the real number world as a whole
68
+ depends on the precision parameter. Class R makes it easy to
69
+ write programs in a 'precision independent style':
70
+ With a single statement, e. g.
71
+ R.prec = 100
72
+ one selects the precision of all numbers (here as 100 decimal places),
73
+ and all arithmetic operations and transcendental functions take the
74
+ required precision automaticaly int account. There is an important
75
+ point here: Obviously the exact product of two numbers of precsion p
76
+ is normally a number of precision 2p. Class BigDecimal allows to
77
+ obtain the exact value of a product if the precision parameter of
78
+ multiplication remains unspecified (or is specified as 0).
79
+ This exact arithmetic, however, gives longer and longer numbers and
80
+ slower and slower execution in virtually all algorithms.
81
+ Thus cutting all arithmetic results back to precission p is essential
82
+ for algorithms of real analysis to remain executable in a practical sense.
83
+
84
+ Since arbitrary precision arithmetic, not only for very large precision,
85
+ is considerably slower than Float arithmetic (I found a factor 3.6
86
+ for a singular value decomposition of a 20 times 20 matrix between
87
+ precision 17 computation time and Float computation time. Precision 100
88
+ led to a computation time of 5.39 s, which was 2.7 times longer than
89
+ for precision 17.) it is desirable to allow switching to Float arithmetic
90
+ with the same syntax:
91
+ R.prec = 0
92
+ and
93
+ R.prec = "float"
94
+ both have this effect. In order for this to work, one has to introduce
95
+ real numbers into the program by a simple specific syntax relying on R:
96
+ Instead of
97
+ x = 1.456e-6; y = 7.7; z = 1000.0; n = 128
98
+ a R-based program may use the conventional mehod 'new' and write
99
+ x = R.new("1.456E-6"); y = R.new(7.7); z = R.new(1000); n = 128
100
+ Instead, we also may use the class method c ('c' for 'converter') and
101
+ write (with or without argument brackets)
102
+ x = R.c 1.456e-6; y = R.c(7.7); z = R.c 1000; n = 128
103
+ Written in this form, the variables x,y,z refer to R-objects for precision
104
+ set as a positive integer, and to Float-objects as a consequence of the
105
+ statement
106
+ R.prec = "float"
107
+ Assume that the program continues as
108
+ u = (x.sin + y.exp) * z
109
+ This works for R-objects since R implements as methods all the functions
110
+ which module Math povides as module functions. When, however, we have the
111
+ statement R.prec = "float" in action, the variables x and y refer to
112
+ Float objects. Then, the terms x.sin and y.exp are not defined, unless we
113
+ prevented the problem via
114
+ require 'float_ext'
115
+ which adds to the interface of Float all the methods of R so that any program
116
+ hat makes sense for R, also makes sense for Float. (This is a harmless
117
+ change of Float that breaks no code which relied on the unmodified
118
+ class Float. If Float has been modified already for some reason, one has to
119
+ make sure that this extension does not conflict with the previous one.)
120
+ Instead of setting precision for the whole program, we may vary it for
121
+ direct comparison by code like this:
122
+
123
+ for i in 0..8
124
+ R.prec = 20*i
125
+ # some computation, e. g.
126
+ t1 = Time.now
127
+ x = [R.c2, R.i2, R.i(0.33333),R.ran(137), R.tob(i), R.pi]
128
+ x.each{ |x|
129
+ y = x.sin**2 + x.cos**2 - 1
130
+ puts "x = #{x.round(6)}, y = #{y}"
131
+ }
132
+ t2 = Time.now
133
+ puts "computation time was #{t2 - t1} for precision #{R.prec}"
134
+ end
135
+
136
+ Here all precision independent creation modes for real numbers are
137
+ exemplified:
138
+ R.c2 equivalent to the yet known R.c(2), hence 2
139
+ R.i2 inverse of 2, i.e. 0.5
140
+ R.i(0.33333) inverse of 0.33333 i.e. approximately 3
141
+ R.ran(137) sine-floor random generator invoked for argument 137
142
+ R.tob(i) test object generator invoked for agument i
143
+ R.pi number pi = 3.14159... (also R.e = 2.71828...)
144
+
145
+ Since the method 'coerce', which determines the meaning of binary
146
+ operator expressions, is defined appropriately in R, one may for a
147
+ R-object r write 1/r instead of R.c1/r and r + 1 instead of
148
+ r + R.c1. This allows us to avoid the appearance of R in all formulas
149
+ except those where real variables are initialized, as in the part
150
+ x = [R.c2, R.i2, R.i(0.33333),R.ran(137), R.tob(3), R.pi]
151
+ of our example computation.
152
+
153
+ Class R is truly consitent with Ruby's numerical concepts: R is derived
154
+ from Ruby class Numeric so that R, Float, Fixnum, and Bignum have a common
155
+ superclass. Notice that class BigDecimal cannot have this property since
156
+ most of its functions need the precision parameter as input.
157
+ Further, R implements as member funcions all functions which are
158
+ defined in Ruby module Math and thus form the core of Ruby's mathematics.
159
+ It would therefore be natural to integrate class R into the BigMath
160
+ module. It would also be natural to include float_ext.rb since this would
161
+ enable the 'precision-independent coding style' scetched above within
162
+ all number-oriented Ruby projects. One would then have a framework in
163
+ which valuable algorithms such as singular value decomposition of matrices
164
+ could be coded with permanent validity. I achieved to transform
165
+ real matrix SVD code from 'Numerical Recipes in C' by Press et al.
166
+ to precision-independent Ruby code and did not succeed so far with the
167
+ same task for complex matrices.
168
+ Inspecting existing sources for "Algorithm 358" shows how far we are away
169
+ fom presenting algorithms of typical complexity in a form that is ready
170
+ for usage at arbitrary precision. In order for this task to make sense,
171
+ one has to have complex numbers, vectors and matrices in
172
+ precision-independent Ruby style. As far as I can see it, the many
173
+ mathematical tool boxes provided by the Ruby community don't contain the
174
+ tools which are needed here. The Ruby language makes it pure fun, however, to
175
+ build these tools according to ones needs, and I did it with the results
176
+ available from www.ulrichmutze.de. I think that it is extremely important
177
+ to have an agreed framework for coding mathematical and physical algorithms.
178
+ Only then one can hope that people will start to add their work and use
179
+ the work of others. So far, I have collected all the algoritms that I ever
180
+ had the opportunity to use in my CPM class system, which is written in
181
+ C++, using a programming style that I call C+-.
182
+ This is presented on my website.
183
+
184
+ Having now a tiny part of the CPM system carried over (not at all
185
+ verbatim) to Ruby, I expect that a Ruby version of the whole system would
186
+ be much smaller and more compact. I'm quite sure that I'll lack enthusiasm
187
+ (and even power) to create such an extended algorithm collection in Ruby
188
+ myself.
189
+ Would it not be great, if any algorithm (e.g. eigen-vectors/values of
190
+ matrices, FFT, min and root finding) that finds its way into some Ruby
191
+ library, would be written in a way that allows it to be executed in
192
+ arbitrary precision, even if Float precision is the preferred option?
193
+ The present class R and also the other classes to be collected in the
194
+ present module AppMath, should add to the experience which is needed to
195
+ come up with a working method to achieve this goal.
196
+
197
+ We know from the beginning that in BigDecimal and hence in R the
198
+ exponents of numbers are restricted to the size which can be represented
199
+ by a Fixnum. Although computations in physics and engineering are far
200
+ from coming close to this limit, schematically applied mathematical
201
+ functions easily transcend it:
202
+
203
+ R.prec = 60
204
+ a = R.c 2.2
205
+ for i in 0...9
206
+ a = a.exp
207
+ puts "a = " + a.to_s
208
+ end
209
+
210
+ yields
211
+
212
+ a = 0.902501349943412092647177716688866402972021659669817926079804E1
213
+ a = 0.830832663077249493655084378868900432568369546441921929731276E4
214
+ a = 0.182141787499134800567191386180195980368655533517234407096707E3609
215
+ a = 0.0
216
+ a = 0.1E1
217
+ a = 0.271828182845904523536028747135266249775724709369995957496696E1
218
+ a = 0.151542622414792641897604302726299119055285485368561397691404E2
219
+ a = 0.381427910476022059220921959409820357102394053622666607552533E7
220
+ a = 0.233150439900719546228968991101213766633201742896351361434871E1656521
221
+
222
+ where only the first three results are correct and the following ones are
223
+ determined by the overfow behavior of Fixnum.
224
+
225
+
226
+ Motivation and rational
227
+ -----------------------
228
+
229
+ Experimentation with class R provided many experiences which sharpened my
230
+ long-standing diffuse ideas concerning a coding framework for mathematics.
231
+
232
+ In mathematics we have 'two worlds': the discrete one and the 'continuous'
233
+ one. For the first world, Ruby is fit without modifications. Its automatic
234
+ switch from Fixnum representation of integer numbers to a Bignum
235
+ representation eliminates all limitations to the coding of discrete
236
+ mathematics problems for which a coding approach is reasonable from a
237
+ logical point of view.
238
+
239
+ The 'continuous world' is the one for which the real numbers lay the ground.
240
+ The prime example of this world is 'real analysis' for which the
241
+ concept of convergence is considered central. When a computationally
242
+ oriented scientist describes to a pure mathematician his experience that
243
+ all mathematically and physically relevant structures seem to have natural
244
+ codable counterparts, this pure mathematician will probably admit that
245
+ this holds for the trivial part of the story, but he will probably
246
+ insist that all deeper questions, those concerning convergence,
247
+ closedness, and completeness are a priori outside the scope of numerical
248
+ methods.
249
+
250
+ According to my understanding, this mathematician's point of view is
251
+ misleading. It is, however, suggested by what mathematicians actually do.
252
+ For them, it is natural, when having to work with a number the square of
253
+ which is known to be 2, to 'construct' this number as a limit of
254
+ objects (e.g. finite decimal fractions) with the intention to use this
255
+ 'exact solution of the equation x^2 = 2' in further constructions and
256
+ deductions within the framework of real analysis.
257
+
258
+ For a computationally oriented scientist an alterative view is more
259
+ natural and more promising: Do everything (i.e solving x^2 = 2, and the
260
+ further constructions and deductions mentioned above)
261
+ with finite precision and consider this 'whole thing' (and not only x)
262
+ as a function of this precision.
263
+ When we consider the behavior for growing precision of the
264
+ 'whole thing' we have a single limit (if we need to consider a
265
+ limit at all), and the question never arises whether two limits
266
+ are allowed to be interchanged. Such questions are typically not
267
+ easy and a major part of the technical scills of mathematicians
268
+ is devoted to them. I'm quite sure that I spent more than a year of my
269
+ live struggling with such questions.
270
+
271
+ To have a realistic example, let us consider that the 'whole thing',
272
+ mentioned above, is the task to produce all tables, figures, diagrams,
273
+ and animations that will go into a presentation or publication. Then it is
274
+ natural to consider a large structured Ruby program as the means to
275
+ perform this task. (My experience is restricted to C++ programs instead
276
+ of Ruby programs for this situation.)
277
+ Let us assume that all data that normally would be
278
+ represented as Float objects, now are represented as R objects.
279
+ As discussed already, this means that e.g. instead of
280
+ x = 2.0
281
+ we hat to write
282
+ x = R.c(2.0)
283
+ or
284
+ x = R.c2
285
+ or we had to add to the statement
286
+ x = 2.0
287
+ the conversion statement
288
+ x = R.c(x)
289
+ No modification or conversion would be needed for the integer numbers!
290
+ Now consider having an initial statement
291
+
292
+ R.prec = "float"
293
+
294
+ in the main program flow.
295
+ By executing the program we get curves in diagrams, moving particles
296
+ in animations, etc. If some of these curves are more jagged than expected,
297
+ or some table values turn out to be NaN or Infinite we may try
298
+ R.prec = 40
299
+ and
300
+ R.prec = 80
301
+ ...
302
+ If the results stabilize somewhere, practitioners are sure that
303
+ 'the result is now independent of numerical errors'.
304
+ Of course, the mathematician's objection that behavior for a few finite
305
+ values says nothing about the limit, also applies here.
306
+ But we are in a very comfortable position to cope with this objection:
307
+ Since we deal with the solution of a task, we are allowed to assume that
308
+ we know the experience-based conventions and 'best practices' concerning
309
+ solutions of tasks in the problem field under consideration. So, the
310
+ judgement whether the behavior of the solution as a function of precision
311
+ supports a specific conclusion or not has a firm basis when
312
+ the context is taken into account. It is an illusionary hope that
313
+ some abstract framework such as 'Mathematics' could replace the guidance
314
+ provided by problem-field related knowledge and experience.
315
+
316
+ Let me finally describe an experience which suggested to me the
317
+ concept of precision as a parameter of whole task (project) instead
318
+ of a parameter of individual arithmetic operations:
319
+
320
+ In an industrial project I had to assess the influence of lens aberrations
321
+ to the efficiency of coupling laser light into the core of an optical
322
+ fiber.
323
+ Although the situation is clearly a wave-optical one, the idea was to test
324
+ whether a ray-tracing simulation would reproduce the few available
325
+ measured data.
326
+ In case of success one would rely on ray-tracing for unexpensive
327
+ optimization and tolerance analysis.
328
+ At those times, in my company all computation was done
329
+ in FORTRAN and floating point number representation was by 4 bytes
330
+ (just as float in C).
331
+ The simulation reproduced the measured curve not really but it
332
+ wiggled arround it in a remarkably symmetrical manner. Just at this time
333
+ our FORTRAN compiler was upgraded to provide 8-byte numbers (corresonding
334
+ to C's double). What I had suspected turned out to be true:
335
+ the ray-trace simulation now reproduced the measurements with magical
336
+ precision.
337
+ Congratulations to the optical lab for having got such highly consistent
338
+ data!
339
+ Soon I turned to programming in C and enjoyed many advantages over
340
+ FORTRAN, but one paradise was lost: There was no longer a meaningful
341
+ comparison between 4 byte and 8 byte precision since the available C-compiler
342
+ worked with 8 bytes internally in both cases. When later we got the type
343
+ long double in additon, this was an disappointment since it was identical to
344
+ double for the MS-compiler and only 10 bytes (?) for GNU. So my desire was to
345
+ have a C++ compiler which implemented, and consistenly used
346
+ float: 4 bytes
347
+ double: 8 bytes
348
+ long double: 16 bytes
349
+ I then would write all my programs with a floating point type named R
350
+ which gets its real meaning in a flexible manner by a statement like
351
+ typedef long double R;
352
+ I was rather sure for a long time that I would never meet a practical
353
+ situation in which a simulation would show objectionable numerical
354
+ errors when done with 16 byte numbers. I had to learn, however, that this
355
+ was a silly idea: Let us consider a system of polyspherical elastic
356
+ particles (the shape of each particle is a union of overlapping spheres)
357
+ which are placed in a mirror-symmetrical container and let initial
358
+ positions (placements) and velocities be arranged symmetrically
359
+ (with respect to the same mirror).
360
+ Then the exact motion can be seen to preserve this symmetry.
361
+ However, each simulation will loose this symmetry after a few particle to
362
+ particle collisions. (The particles start to rotate after collisions,
363
+ which influences further collisions much more than for mono-spherical
364
+ particles.) Increasing the number of bytes per number will only allow a
365
+ few more symmetrical collisions, and the computation time needed to see
366
+ these will increase.
367
+ Of coarse, this deviation from symmetry is not objectionable from a
368
+ 'real world' point of view.
369
+ It teaches the positive fact that tolerances for making the particles,
370
+ placing and boosting them, are unrealisticly tight for realizing a
371
+ symmetrical motion of the system. This shows that there are
372
+ computational tasks in which not all computed system properties
373
+ will stabilize with increasing computational precision. With class R
374
+ such computational phenomena are within the scope of Ruby programming!
375
+
376
+ Preferred command line for documentation:
377
+ rdoc -a -N -S --op doc_r r.rb
378
+ =end
379
+
380
+ class R < Numeric
381
+
382
+ attr_reader :g
383
+
384
+ # number of stored decimals
385
+ @@prec = 40
386
+ # value of number pi = 3.14159...
387
+ @@pi = BigDecimal("0.3141592653589793238462643383279502884197E1")
388
+ # value of Euler's number e = 2.718281828...
389
+ @@e = BigDecimal("0.2718281828459045235360287471352662497757E1")
390
+ # value of log10(e)
391
+ @@lge = BigDecimal("0.4342944819032518276511289189166050822944E0")
392
+ # value of log(10)
393
+ @@ln10 = BigDecimal("0.2302585092994045684017991454684364207601E1")
394
+
395
+ # Setting precision, i. e. the number of digits.
396
+ #
397
+ # If the anInteger.to_i is an integer > 0, this number will
398
+ # be set as the class variable @@prec.
399
+ # All other input will set @@prec to 0.
400
+ # Usage:
401
+ # R.prec = 100
402
+ # or
403
+ # R.prec = "float"
404
+ # A class state @@prec == 0 lets all generating methods create
405
+ # Float-objects instead of R-objects. Calling methods of
406
+ # R-objects is undefined in this class state. This is behavior is
407
+ # reasonable since all real numbers will be created as Floats for
408
+ # this setting of precision so that all method calls will
409
+ # automatically be invoked by Float-objects instead of R-objects.
410
+ # For this to work one needs
411
+ # require 'float_ext'
412
+ # and to use R.c instead of R.new.
413
+
414
+ def R.prec=(anInteger)
415
+ if anInteger.kind_of?(Numeric)
416
+ ai=anInteger.to_i # for robustness
417
+ ai = 0 unless ai.class == Fixnum
418
+ else
419
+ ai = 0
420
+ end
421
+ @@prec = ai > 0 ? ai : 0
422
+ if @@prec > 0
423
+ @@pi = BigMath::PI(@@prec)
424
+ @@e = BigMath::E(@@prec)
425
+ ln5 = BigMath.log(BigDecimal("5"),@@prec)
426
+ ln2 = BigMath.log(BigDecimal("2"),@@prec)
427
+ @@ln10 = ln5.add(ln2,@@prec)
428
+ @@lge = BigDecimal("1").div(@@ln10,@@prec)
429
+ end
430
+ end
431
+
432
+ # Returns the number of digits.
433
+ # Usage:
434
+ # n = R.prec
435
+ def R.prec
436
+ return @@prec
437
+ end
438
+
439
+ # Changes the number of digits by adding the argument to the present
440
+ # value. The argument may be negative too, so that one easily sets the
441
+ # value back by means of the same function.
442
+ def R.add_prec(anInteger)
443
+ ai = anInteger.to_i
444
+ @@prec += ai
445
+ end
446
+
447
+ # Only for @@prec > 0, the function works, otherwise it fails.
448
+ # The argument is assumed to be either a BigDecimal, or any object x for
449
+ # which x.to_s determines a BigDecimal bd via bd = BigDecimal(x.to_s).
450
+ # This allows to input number-related character strings and numerical
451
+ # literals, and numberical variables in a variety of styles:
452
+ # i = 10000000
453
+ # j = 123456789
454
+ # a = R.new(i * j)
455
+ # b = R.new(1e-23)
456
+ # c = R.new("-117.07e99")
457
+ # d = R.new(1e66)
458
+ # e = R.new("1E666")
459
+ # The most universal mehod for generating large numbers is the one from
460
+ # String objects as in the definition of the variables c and e above.
461
+ # According to the design of BigDecimal, the exponent (given by the digits
462
+ # after the 'E') has to be representable by a Fixnum.
463
+
464
+ def initialize(aStringOrNumber="0.0")
465
+ fail "R.new makes sense only for @@prec > 0" unless @@prec > 0
466
+ if aStringOrNumber.kind_of?(BigDecimal)
467
+ @g = aStringOrNumber # in order to take the reuts from BigDecimal
468
+ # arithmetic directly, speeds programs up efficiently
469
+ else
470
+ x = BigDecimal(aStringOrNumber.to_s)
471
+ fail "Unable to define a BigDecimal from the argument" unless
472
+ x.kind_of?(BigDecimal)
473
+ @g = x
474
+ end
475
+ end
476
+
477
+ =begin
478
+ #old version, by a factor 3.65 slower in a typical test examle
479
+ def initialize(aStringOrNumber="0.0")
480
+ fail "R.new makes sense only for @@prec > 0" unless @@prec > 0
481
+ if aStringOrNumber.kind_of?(BigDecimal)
482
+ x = aStringOrNumber
483
+ else
484
+ x = BigDecimal(aStringOrNumber.to_s)
485
+ end
486
+ fail "Unable to define a BigDecimal from the argument" unless
487
+ x.kind_of?(BigDecimal)
488
+ if x.finite?
489
+ sp = x.split
490
+ res = sp[0] == -1 ? "-0." : "0."
491
+ core = sp[1].slice(0...@@prec)
492
+ res += core
493
+ res += "E"
494
+ res += sp[3].to_s
495
+ @g = BigDecimal(res)
496
+ else
497
+ @g = x
498
+ end
499
+ end
500
+ =end
501
+
502
+ # Number generator which yiels a Float for R.prec ==0 and a R else.
503
+ # This is the recommended method for introducing real numbers in
504
+ # programs since, when followed consequently, the whole program
505
+ # can be switched from Float precission to arbitrary precision
506
+ # by a single R.prec= statement. Usage:
507
+ # x = R.c("3.45E-45"); y = R.c 2; z = R.c(1.25e13); u = R.c "40.1e-1"
508
+ def R.c(aNumber)
509
+ if @@prec < 1 # yield a Float
510
+ nf = aNumber.to_f
511
+ if nf.class == Float
512
+ nf
513
+ else
514
+ nff = aNumber.to_s.to_f
515
+ if nff.class == Float
516
+ nff
517
+ else
518
+ fail "Unable to define a Float from the argument"
519
+ end
520
+ end
521
+ else # yield a R with the correct number of digits
522
+ R.new(aNumber)
523
+ end
524
+ end
525
+
526
+ # The clone has the required number of digits.
527
+ def clone; R.new(self); end
528
+
529
+ # The constant 0.
530
+ def R.zero
531
+ return 0.0 if @@prec < 1
532
+ R.new
533
+ end
534
+ # The constant 1.
535
+ def R.one
536
+ return 1.0 if @@prec < 1
537
+ R.new("1.0")
538
+ end
539
+ # The constant 2.
540
+ def R.two
541
+ return 2.0 if @@prec < 1
542
+ R.new("2.0")
543
+ end
544
+ # The constant 10.
545
+ def R.ten
546
+ return 10.0 if @@prec < 1
547
+ R.new("10.0")
548
+ end
549
+ # The constant 0.
550
+ def R.c0
551
+ return 0.0 if @@prec < 1
552
+ R.new
553
+ end
554
+ # The constant 1.
555
+ def R.c1
556
+ return 1.0 if @@prec < 1
557
+ R.new("1.0")
558
+ end
559
+ # The constant 2.
560
+ def R.c2
561
+ return 2.0 if @@prec < 1
562
+ R.new("2.0")
563
+ end
564
+ # The constant 3.
565
+ def R.c3
566
+ return 3.0 if @@prec < 1
567
+ R.new("3.0")
568
+ end
569
+ # The constant 4.
570
+ def R.c4
571
+ return 4.0 if @@prec < 1
572
+ R.new("4.0")
573
+ end
574
+ # The constant 5.
575
+ def R.c5
576
+ return 5.0 if @@prec < 1
577
+ R.new("5.0")
578
+ end
579
+ # The constant 6.
580
+ def R.c6
581
+ return 6.0 if @@prec < 1
582
+ R.new("6.0")
583
+ end
584
+ # The constant 7.
585
+ def R.c7
586
+ return 7.0 if @@prec < 1
587
+ R.new("7.0")
588
+ end
589
+ # The constant 8.
590
+ def R.c8
591
+ return 8.0 if @@prec < 1
592
+ R.new("8.0")
593
+ end
594
+ # The constant 9.
595
+ def R.c9
596
+ return 9.0 if @@prec < 1
597
+ R.new("9.0")
598
+ end
599
+ # The constant 10.
600
+ def R.c10
601
+ return 10.0 if @@prec < 1
602
+ R.new("10.0")
603
+ end
604
+
605
+ # The constant pi=3.14159...
606
+ def R.pi
607
+ return Math::PI if @@prec < 1
608
+ R.new(@@pi)
609
+ end
610
+ # The constant e = 2.718281828...
611
+ def R.e
612
+ return Math::E if @@prec < 1
613
+ R.new(@@e)
614
+ end
615
+ # The constant log10(e) = 0.43429448...
616
+ def R.lge
617
+ return Math::log10(Math::E) if @@prec < 1
618
+ R.new(@@lge)
619
+ end
620
+ # The constant log(10) = 2.30258...
621
+ def R.ln10
622
+ return Math::log(10.0) if @@prec < 1
623
+ R.new(@@ln10)
624
+ end
625
+
626
+ # The constant 1/2 (inverse 2)
627
+ def R.i2
628
+ return 0.5 if @@prec < 1
629
+ R.new("0.5")
630
+ end
631
+ # The constant 1/3.
632
+ def R.i3
633
+ R.c1/R.c3
634
+ end
635
+ # The constant 1/4.
636
+ def R.i4
637
+ return 0.25 if @@prec < 1
638
+ R.new("0.25")
639
+ end
640
+ # The constant 1/5.
641
+ def R.i5
642
+ return 0.2 if @@prec < 1
643
+ R.new("0.2")
644
+ end
645
+ # The constant 1/6.
646
+ def R.i6; R.c1/R.c6; end
647
+ # The constant 1/7.
648
+ def R.i7; R.c1/R.c7; end
649
+ # The constant 1/8.
650
+ def R.i8
651
+ return 0.125 if @@prec < 1
652
+ R.new("0.125")
653
+ end
654
+ # The constant 1/9.
655
+ def R.i9; R.c1/R.c9; end
656
+ # The constant 1/10.
657
+ def R.i10
658
+ return 0.1 if @@prec < 1
659
+ R.new("0.1")
660
+ end
661
+ # The constant NaN, not a number.
662
+ def R.nan
663
+ return 0.0/0.0 if @@prec < 1
664
+ R.new(BigDecimal("NaN"))
665
+ end
666
+
667
+ # Returns a two-component array containing the normalized
668
+ # fraction (an R) and the exponent (a Fixnum) of self.
669
+ # The exponent refers always to radix 10.
670
+ def frexp # member functions will be called only in situations in
671
+ # which @@prec > 0 is guarantied
672
+ n = @g.exponent
673
+ fac = BigDecimal("10").power(-n)
674
+ prel = [@g.mult(fac, @@prec), n]
675
+ [R.new(prel[0]),prel[1]]
676
+ end
677
+
678
+ # Adds the argument to the exponent of self.
679
+ # If self is a normalized fraction (and thus has exponent 0)
680
+ # the resulting number has an exponent given by the argument.
681
+ # This is the situation to which the functions name 'load exponent'
682
+ # refers.
683
+ def ldexp(anInteger)
684
+ ai=anInteger.to_i
685
+ self * (R.c10 ** ai)
686
+ end
687
+
688
+ # Random value (sine-floor random generator).
689
+ #
690
+ # Chaotic function from the integers into the
691
+ # unit interval [0,1] of R
692
+ # Argument condition: R.new(anInteger) defined
693
+ # Although the main intent is to use the function for integer
694
+ # argments, this is not essential.
695
+
696
+ def R.ran(anInteger)
697
+ if @@prec < 1 # no further usage of R
698
+ x = anInteger
699
+ y = 1e6 * Math::sin(x)
700
+ return y - y.floor
701
+ else
702
+ shift = 6
703
+ fac = R.one.ldexp(shift)
704
+ R.add_prec(shift)
705
+ x = R.c anInteger
706
+ y = fac * x.sin
707
+ res = (y - y.floor).round(@@prec - shift)
708
+ R.add_prec(-shift)
709
+ res
710
+ end
711
+ end
712
+
713
+ # Test object.
714
+ #
715
+ # Needed for automatic tests of arithmetic relations. Intended
716
+ # to give numbers which rapidly change sign and order of
717
+ # magnitude when the argument grows regularly e.g.
718
+ # as in 1,2,3,... . However, suitibility as a random generator is
719
+ # not the focus. If the second argument is 'true', the result
720
+ # is multplied by a number << 1 in order to prevent the result
721
+ # from overloading the exponential function.
722
+
723
+ def R.tob(anInteger, small = false)
724
+ small_a = small || @@prec<=0
725
+ ai=anInteger.to_i
726
+ mag_num1 = 7 # 'magic numbers'
727
+ mag_num2 = 11
728
+ mag_num3 = 17
729
+ mag_num4 = small_a ? 0.0423 : 1.7501
730
+ mag_num5 = small_a ? 0.13 : 0.65432
731
+ r1 = ai % mag_num1
732
+ r2 = ai % mag_num2
733
+ r3 = ai % mag_num3
734
+ y=(-r1 - r2 + r3) * mag_num4 + mag_num5
735
+ if @@prec < 1
736
+ res = Math::exp(y) * (ran(ai) - 0.5)
737
+ else
738
+ res = R.new(y).exp * (ran(ai) - R.i2)
739
+ end
740
+ end
741
+
742
+ # Returns the inverse of aNumber as a type controled by @@prec.
743
+ def R.i(aNumber)
744
+ if @@prec < 1
745
+ 1.0/aNumber.to_f
746
+ else
747
+ R.c1/R.new(aNumber)
748
+ end
749
+ end
750
+
751
+ # Unary minus operator. It returns the R-object -self.
752
+ def -@; R.new(-@g); end
753
+
754
+ # Unary plus operator. It returns the R-object self.
755
+ def +@; R.new(@g); end
756
+
757
+ # (Complex) conjugation, no effect on real numbers.
758
+ # Supports the unified treatment of real and complex numbers.
759
+ def conj; self; end
760
+
761
+ # Redefining coerce from Numeric.
762
+ # This allows writing 1 + R.new(137) instead of R.new(137) + 1
763
+ # or R.new(137) + R.c1.
764
+ #-- Notice that the order in the resulting array is essential for
765
+ # correct functionality.
766
+ def coerce(a)
767
+ [ R.new(a), self]
768
+ end
769
+
770
+ # Adjust argument a so that its data type fits @@prec
771
+
772
+ def R.aa(a)
773
+ if a.class == R
774
+ a.g
775
+ else
776
+ BigDecimal(a.to_s)
777
+ end
778
+ end
779
+
780
+ # The basic order relation.
781
+ def <=>(a)
782
+ @g <=> R.aa(a)
783
+ end
784
+
785
+ # Returns 'true' if self equals zero.
786
+ def zero?; @g.zero?; end
787
+
788
+ # Returns 'true' if self is 'not a number' (NaN).
789
+ def nan?; @g.nan?; end
790
+
791
+ def infinite?; @g.infinite?; end
792
+
793
+ # Since R is not Fixnum or Bignum we return 'false'. In scientific
794
+ # computation there may be the need to use various types of 'real number
795
+ # types' but there should be always a clear-cut distinction between
796
+ # integer types and real types.
797
+ def integer?; false; end
798
+
799
+ # Supports the unified treatment of real and complex numbers.
800
+ def real?; true; end
801
+
802
+ # Supports the unified treatment of real and complex numbers.
803
+ def complex?; false; end
804
+
805
+ # Returns the R-object self + aR.
806
+ def +(aR)
807
+ R.new(@g.add(R.aa(aR),@@prec))
808
+ end
809
+
810
+ # Returns the R-object self - aR.
811
+ def -(aR)
812
+ R.new(@g.sub(R.aa(aR),@@prec))
813
+ end
814
+
815
+ # Returns the R-object self * aR.
816
+ def *(aR)
817
+ R.new(@g.mult(R.aa(aR),@@prec))
818
+ end
819
+
820
+ # Returns the R-object self / aR.
821
+ def /(aR)
822
+ R.new(@g.div(R.aa(aR),@@prec))
823
+ end
824
+
825
+ # Usual modulo division.
826
+ def %(aR)
827
+ R.new(@g%R.aa(aR))
828
+ end
829
+
830
+ # Returns the a-th power of self, if a is an integer argument,
831
+ # and the a-th power of self.abs for real, non-integer a.
832
+ # We put 0**a = 0 for all non-integer a.
833
+ #-- If a is integer, the functionality is as given by classes Float and
834
+ # BigDecimal. For non-integer a, we take the freedom do make the definition 0
835
+ # where mathematics suggests complex (e.g. (-1)**0.5) or infinite ( e.g. 0**-1)
836
+ # results.
837
+ def **(a)
838
+ return R.nan if nan?
839
+ if a.class == Fixnum || a.class == Bignum
840
+ return R.new(@g.power(a))
841
+ end
842
+ a = R.new(a) if a.class != R
843
+ R.nan if a.nan?
844
+ x = abs
845
+ if x.zero?
846
+ R.c0
847
+ else
848
+ (x.log * a).exp
849
+ end
850
+ end
851
+
852
+ # Returns the zero-element which belongs to the same class than self
853
+ def to_0; R.c0; end
854
+
855
+ # Returns the unit-element which belongs to the same class than self
856
+ def to_1; R.c1; end
857
+
858
+ # Returns the inverse 1/self.
859
+ def inv
860
+ return R.nan if nan?
861
+ R.new(BigDecimal("1").div(@g,@@prec))
862
+ end
863
+
864
+ # The pseudo inverse is always defined: the pseudo inverse of 0 is 0.
865
+ def pseudo_inv
866
+ return R.c0 if zero?
867
+ inv
868
+ end
869
+
870
+ # If the method gets no argument we return the 'nearest integer':
871
+ # For the return value res we have
872
+ # res.int? == true
873
+ # and
874
+ # (self - res).abs <= 0.5
875
+ # For an integer argument we return
876
+ # a real number, the significand of which has not more
877
+ # than n digits. Notice that there is also a function.
878
+ def round(*arg)
879
+ n = arg.size
880
+ case n
881
+ when 0
882
+ (self + 0.5).floor.to_i # here we ask for an integer output
883
+ # notice that R#round maps to R
884
+ when 1
885
+ m = arg[0].to_i
886
+ x = frexp
887
+ y = x[0].ldexp(m)
888
+ (y + 0.5).floor.ldexp(x[1] - m)
889
+ else
890
+ fail "needs 0 or 1 arguments"
891
+ end
892
+ end
893
+
894
+ # Returns the square root of self.
895
+ def sqrt
896
+ return R.nan if nan?
897
+ R.new(@g.sqrt(@@prec))
898
+ end
899
+
900
+ # Returns the absolute value of self.
901
+ def abs; R.new(@g.abs); end
902
+
903
+ # Returns the square of the absolute value of self.
904
+ def abs2; self * self; end
905
+
906
+ # Returns a kind of relative distance between self and aR.
907
+ # The return value varies from 0 to 1, where 1 means maximum dissimilarity
908
+ # of the arguments.
909
+ # Such a function is needed for testing the validity of arithmetic laws,
910
+ # which, due to numerical noise, should not be expected to be fulfilled
911
+ # exactly.
912
+ def dis(aR)
913
+ aR = R.aa(aR)
914
+ a = self.abs
915
+ b = aR.abs
916
+ d = (self - aR).abs
917
+ s = a + b
918
+ return R.c0 if s.zero?
919
+ d1 = d/s
920
+ d < d1 ? d : d1
921
+ end
922
+
923
+ # Returns the floor value of self (the largest integer R <= self).
924
+ def floor; R.new(@g.floor); end
925
+
926
+ # Returns the ceil value of self (the smallest integer R >= self).
927
+ def ceil; R.new(@g.ceil); end
928
+
929
+ # Conversion to String.
930
+ def to_s; @g.to_s; end
931
+
932
+ # Printing the value together with a label
933
+ def prn(name)
934
+ puts "#{name} = " + to_s
935
+ end
936
+
937
+ # Conversion to integer.
938
+ def to_i; @g.to_i; end
939
+
940
+ # Conversion to integer.
941
+ def to_int; @g.to_int; end
942
+
943
+ # Conversion to double.
944
+ def to_f
945
+ @g.to_f
946
+ end
947
+
948
+ # Auxliar version of arcsin ( not public).
949
+ #
950
+ # Definition in terms of a power series. The convergence becomes very bad when
951
+ # self.abs becomes close to 1. The present function will actually be used for
952
+ # defining atan.
953
+
954
+ def asin_aux
955
+ return R.nan if @g.infinite? || @g.nan?
956
+ one=BigDecimal("1")
957
+ raise ArgumentError, "@g.abs must be <= 1.0" if @g.abs>one
958
+ n = @@prec + BigDecimal.double_fig
959
+ y = @g
960
+ d = y
961
+ t = @g
962
+ n1 = one
963
+ n2 = BigDecimal("2")
964
+ n3 = BigDecimal("3")
965
+ x2 = @g.mult(@g,n)
966
+ while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
967
+ m = BigDecimal.double_fig if m < BigDecimal.double_fig
968
+ t = t.mult(x2,n)*n1/n2
969
+ d = t.div(n3,m)
970
+ y += d
971
+ n1 += 2
972
+ n2 += 2
973
+ n3 += 2
974
+ end
975
+ R.new(y)
976
+ end
977
+
978
+ # Auxliar version of arctan ( not public).
979
+ def atan_aux
980
+ a = @g.abs
981
+ if a < BigDecimal("1.618")
982
+ x = self / (R.c1+self*self).sqrt # x.abs < 1/1.618 = 0.618
983
+ y = x.asin_aux
984
+ else
985
+ rec = BigDecimal("1").div(@g,@@prec) # rec.abs < 1/1.618 = 0.618
986
+ y = R.pi * R.i2 - R.new(BigMath.atan(rec,@@prec))
987
+ end
988
+ end
989
+
990
+ # Argument, i.e. polar angle phi of point (x=self,y), -pi < phi <= pi.
991
+ #
992
+ # This is the basic tool for defining asin, acos, atan, acot.
993
+ # Notice x.arg(y) == y.atan2(x) with function atan2 to be defined next.
994
+ def arg(y)
995
+ a=(self*self+y*y).sqrt
996
+ res = R.c2*(y/(a+self)).atan_aux
997
+ res -= R.pi * 2 if res > R.pi
998
+ res
999
+ end
1000
+
1001
+ # The value y.atan2(x) is the polar angle of point (x,y) and corresponds
1002
+ # to Math.atan2(y,x), where the squeere order of arguments is the same
1003
+ # as in the (poor) formula atan(y/x).
1004
+ def atan2(x); x.arg(self); end
1005
+
1006
+ # Returns he hypotenuse of a right-angled triangle with
1007
+ # sides x = self and y.
1008
+ def hypot(y); (self * self + y * y).sqrt; end
1009
+
1010
+ # Inverse sine.
1011
+ def asin
1012
+ y = self
1013
+ x = (R.c1 - y * y).sqrt
1014
+ x.arg(y)
1015
+ end
1016
+
1017
+ # Inverse cosine.
1018
+ def acos
1019
+ x = self
1020
+ y = (R.c1 - x * x).sqrt
1021
+ x.arg(y)
1022
+ end
1023
+
1024
+ # Inverse tangent.
1025
+ def atan
1026
+ cosine = (R.c1 + self * self).sqrt.inv
1027
+ sine = cosine * self
1028
+ cosine.arg(sine)
1029
+ end
1030
+
1031
+ # Inverse cotangent.
1032
+ def acot
1033
+ a = inv
1034
+ a.atan
1035
+ end
1036
+
1037
+ # Sine.
1038
+ #
1039
+ # This reduces computation of the sine of any angle
1040
+ # to computation of sine or cosine of a angle less than pi/4
1041
+ # and thus less than 1. This speeds up the convergence of the
1042
+ # sine or cosine power series.
1043
+
1044
+ def sin
1045
+ return R.nan if @g.infinite? || @g.nan?
1046
+ sign=1
1047
+ if @g >= BigDecimal("0")
1048
+ x=@g
1049
+ else
1050
+ x=-@g
1051
+ sign=-1
1052
+ end
1053
+ twoPi=@@pi*BigDecimal("2")
1054
+ piHalf=@@pi*BigDecimal("0.5")
1055
+ piQuart=@@pi*BigDecimal("0.25")
1056
+ res=x.divmod(twoPi)
1057
+ phi=res[1]
1058
+ res2=phi.divmod(piHalf)
1059
+ qn=res2[0] # number of quadrant 0,1,2,3
1060
+ alpha=res2[1] # angle in quadrant
1061
+ if qn==0
1062
+ # first quadrant
1063
+ if alpha<=piQuart
1064
+ r=R.new(BigMath.sin(alpha,@@prec))
1065
+ else
1066
+ r=R.new(BigMath.cos(piHalf-alpha,@@prec))
1067
+ end
1068
+ elsif qn==1
1069
+ # second quadrant
1070
+ if alpha<=piQuart
1071
+ r=R.new(BigMath.cos(alpha,@@prec))
1072
+ else
1073
+ r=R.new(BigMath.sin(piHalf-alpha,@@prec))
1074
+ end
1075
+ elsif qn==2
1076
+ # third quadrant
1077
+ if alpha<=piQuart
1078
+ r=-R.new(BigMath.sin(alpha,@@prec))
1079
+ else
1080
+ r=-R.new(BigMath.cos(piHalf-alpha,@@prec))
1081
+ end
1082
+ elsif qn==3
1083
+ # fourth quadrant
1084
+ if alpha<=piQuart
1085
+ r=-R.new(BigMath.cos(alpha,@@prec))
1086
+ else
1087
+ r=-R.new(BigMath.sin(piHalf-alpha,@@prec))
1088
+ end
1089
+ else
1090
+ puts "error in R.sin, not assumed to happen"
1091
+ end
1092
+ sign == -1 ? -r : r
1093
+ end
1094
+
1095
+ # Cosine.
1096
+ def cos
1097
+ piHalf=R.pi*R.new("0.5")
1098
+ (self+piHalf).sin
1099
+ end
1100
+
1101
+ # Tangent.
1102
+ def tan
1103
+ self.sin/self.cos
1104
+ end
1105
+
1106
+ # Cotangent.
1107
+ def cot
1108
+ self.cos/self.sin
1109
+ end
1110
+
1111
+ # Power of 10: exp10(x) = 10**x.
1112
+ # Auxiliar function for the implementation of exp.
1113
+ #-- For decimal numbers this can efficiently be reduced to
1114
+ # smaller numbers
1115
+ def exp10
1116
+ return R.nan if @g.infinite? || @g.nan?
1117
+ n = @g.floor
1118
+ f = @g - n
1119
+ x = BigMath.exp(f.mult(@@ln10,@@prec),@@prec)
1120
+ newExp=n.to_i # newExp has to be a Fixnum in order that
1121
+ # the next line defines a BigDecimal. This looks like a missed
1122
+ # opportunity: numbers that accept Bignums as exponents should
1123
+ # be not much more difficult to implement.
1124
+ p10=BigDecimal("1.0E"+newExp.to_s) # a power of 10, generated on the
1125
+ # level of symbols, not by computation
1126
+ y = x.mult(p10,@@prec)
1127
+ R.new(y)
1128
+ end
1129
+
1130
+ # Exponential function.
1131
+ def exp
1132
+ (self * R.lge).exp10
1133
+ end
1134
+
1135
+ # Hyperbolic sine.
1136
+ def sinh; (self.exp - (-self).exp) * R.i2; end
1137
+
1138
+ # Hyperbolic cosine.
1139
+ def cosh; (self.exp + (-self).exp) * R.i2; end
1140
+
1141
+ # Hyperbolic tangent.
1142
+ def tanh
1143
+ s = self.exp - (-self).exp
1144
+ c = self.exp + (-self).exp
1145
+ s/c
1146
+ end
1147
+
1148
+ # Hyperbolic cotangent.
1149
+ def coth
1150
+ s = self.exp - (-self).exp
1151
+ c = self.exp + (-self).exp
1152
+ c/s
1153
+ end
1154
+
1155
+ # Logarithm to base 10.
1156
+ #
1157
+ # Makes use of knowing the decimal exponent
1158
+ # for a reduction to an actually small argument.
1159
+ # We understand log10(x) as log10(|x|), which is
1160
+ # natural in the real domain.
1161
+
1162
+ def log10
1163
+ return R.nan if nan?
1164
+ return R.nan if @g.infinite? || @g.nan?
1165
+ sp=@g.split
1166
+ exponent=sp[3]
1167
+ s="0."+sp[1]
1168
+ x=BigDecimal(s)
1169
+ if x.zero?
1170
+ fail "zero argument of log"
1171
+ end
1172
+ x=BigMath.log(x.abs,@@prec)
1173
+ y=BigDecimal(exponent.to_s)
1174
+ R.new(x) * R.new(@@lge) + R.new(y)
1175
+ end
1176
+
1177
+ # Natural logarithm.
1178
+ def log
1179
+ return R.nan if nan?
1180
+ r1=self.log10
1181
+ r2=R.e.log10
1182
+ r1/r2
1183
+ end
1184
+
1185
+ # Inverse hyperbolic sine.
1186
+ def asinh
1187
+ ((self * self + R.c1).sqrt + self).log
1188
+ end
1189
+
1190
+ # Inverse hyperbolic cosine.
1191
+ def acosh
1192
+ ((self * self - R.c1).sqrt + self).abs.log
1193
+ end
1194
+
1195
+ # Inverse hyperbolic tangent.
1196
+ def atanh
1197
+ ((R.c1 + self)/(R.c1 - self)).abs.log * R.i2
1198
+ end
1199
+
1200
+ # Inverse hyperbolic cotangent.
1201
+ def acoth
1202
+ ((self + R.c1)/(self - R.c1)).abs.log * R.i2
1203
+ end
1204
+
1205
+ # Returns the value of erfc for large arguments according to the
1206
+ # asymptotic formla 7.1.23 on p. 298 of Abramowitz Stegun.
1207
+ # From the sum over m = 1, 2, ... we take the terms up to m = 4.
1208
+ def erfc_asy
1209
+ z = self *self
1210
+ z2 = z * R.c2
1211
+ yp = z2.clone
1212
+ s = R.c1 - R.c1/yp
1213
+ yp *= z2
1214
+ s += R.c3/yp
1215
+ yp *= z2
1216
+ s -= R.new("15")/yp
1217
+ yp *= z2
1218
+ s += R.new("105")/yp
1219
+ yp *= z2
1220
+ s -= R.new("945")/yp
1221
+ (-z).exp * s/R.pi.sqrt
1222
+ end
1223
+
1224
+ # Returns the value obtained from evaluating the power series of erf
1225
+ # at x = self. Here it is assumed, that x < 9 which is sufficient since
1226
+ # for larger values we have a asymptotic formula. If we had to use larger
1227
+ # x's, the 'magic number' 33 in the code had to be increased, which would
1228
+ # slow down the computation.
1229
+ #-- Source: Abramowitz Stegun p. 297, formula 7.1.5 .
1230
+ def erf_ps
1231
+ return R.nan if @g.infinite? || @g.nan?
1232
+ x = self
1233
+ n = @@prec + BigDecimal.double_fig
1234
+ x2 = -x * x
1235
+ y = R.c1
1236
+ i = R.c0
1237
+ b = R.c1
1238
+ c = R.c1
1239
+ a = R.c0
1240
+ n_add = 33
1241
+ R.add_prec(n_add)
1242
+ while a.frexp[1] > y.frexp[1] - n
1243
+ i += R.c1
1244
+ b *= x2/i
1245
+ c += R.c2
1246
+ a = b/c
1247
+ y += a
1248
+ end
1249
+ R.add_prec(-n_add)
1250
+ res = y * R.c2 * x / R.pi.sqrt
1251
+ end
1252
+
1253
+ # Returns the error function evaluated at x=self.
1254
+ def erf
1255
+ if self < R.c0
1256
+ x = abs
1257
+ sig = - R.c1
1258
+ else
1259
+ x = self
1260
+ sig = R.c1
1261
+ end
1262
+ if x > R.c9
1263
+ return (R.c1 - x.erfc_asy)*sig
1264
+ else
1265
+ return x.erf_ps * sig
1266
+ end
1267
+ end
1268
+
1269
+ # Returns the complementary error function erfc evaluated at x=self.
1270
+ def erfc
1271
+ if self > R.c9
1272
+ erfc_asy
1273
+ else
1274
+ R.c1 - erf
1275
+ end
1276
+ end
1277
+
1278
+ # Consistency test for class R
1279
+ # This is intended to keep the class consistent despite of modifications.
1280
+ # The first argument influences the numbers which are selected for the
1281
+ # test. Returned is a sum of numbers each of which should be numerical
1282
+ # noise and so the result has to be << 1 if the test is to indicate
1283
+ # success.
1284
+ # For istance, on my system
1285
+ # R.prec = 100; R.test(137)
1286
+ # produces
1287
+ # The error sum is 0.1654782936431420775338085739363521532600906524358
1288
+ # 495733897874347055126769599726793415224324363846749E-29 .
1289
+ # Computation time was 0.853 seconds.
1290
+
1291
+ def R.test(n0, verbose = false )
1292
+ puts "Doing R.test(#{n0}, #{verbose}) for R.prec = #{@@prec}:"
1293
+ puts "*************************************************"
1294
+ require 'float_ext'
1295
+ t1 = Time.now
1296
+ small = R.prec <= 0
1297
+ s = R.c0
1298
+ puts "class of s is " + s.class.to_s
1299
+ i = n0
1300
+ a = R.tob(i)
1301
+ i += 1
1302
+ b = R.tob(i)
1303
+ i += 1
1304
+ c = R.tob(i)
1305
+ i += 1
1306
+
1307
+ r = 2 + a
1308
+ l = R.c2 + a
1309
+ ds = r.dis(l)
1310
+ puts "coerce 2 + a: ds = " + ds.to_s if verbose
1311
+ s += ds
1312
+
1313
+ r = a + 1.234
1314
+ l = a + R.c(1.234)
1315
+ ds = r.dis(l)
1316
+ puts "coerce a + float: ds = " + ds.to_s if verbose
1317
+ s += ds
1318
+
1319
+ ai = a.round
1320
+ bool_val = ai.integer?
1321
+
1322
+ ds = bool_val ? 0 : 1
1323
+ puts "rounding gives integer type: ds = " + ds.to_s if verbose
1324
+ s += ds
1325
+
1326
+ diff = (a - ai).abs
1327
+ bool_val = diff <= 0.5
1328
+ ds = bool_val ? 0 : 1
1329
+ puts "rounding is accurate: ds = " + ds.to_s if verbose
1330
+ s += ds
1331
+
1332
+ r = (a + b) * c
1333
+ l = a * c + b * c
1334
+
1335
+ ds = r.dis(l)
1336
+ puts "Distributive law for +: ds = " + ds.to_s if verbose
1337
+ s += ds
1338
+
1339
+ r = (a - b) * c
1340
+ l = a * c - b * c
1341
+ ds = r.dis(l)
1342
+ puts "Distributive law for -: ds = " + ds.to_s if verbose
1343
+ s += ds
1344
+
1345
+ r = (a * b) * c
1346
+ l = b * (c * a)
1347
+ ds = r.dis(l)
1348
+ puts "Multiplication: ds = " + ds.to_s if verbose
1349
+ s += ds
1350
+
1351
+ a = R.tob(i)
1352
+ i += 1
1353
+ b = R.tob(i)
1354
+ i += 1
1355
+ c = R.tob(i)
1356
+ i += 1
1357
+
1358
+ r = (a * b) / c
1359
+ l = (a / c) * b
1360
+ ds = r.dis(l)
1361
+ puts "Division: ds = " + ds.to_s if verbose
1362
+ s += ds
1363
+
1364
+ x = R.c0/R.c0
1365
+ y = x.nan?
1366
+ ds = y ? 0 : 1
1367
+ puts "0/0: ds = " + ds.to_s if verbose
1368
+ s += ds
1369
+
1370
+ r = R.c1
1371
+ l = a * a.inv
1372
+ ds = r.dis(l)
1373
+ puts "inv: ds = " + ds.to_s if verbose
1374
+ s += ds
1375
+
1376
+
1377
+ r = 1/a
1378
+ l = a.inv
1379
+ ds = r.dis(l)
1380
+ puts "inv and 1/x: ds = " + ds.to_s if verbose
1381
+ s += ds
1382
+
1383
+ r = b
1384
+ l = -(-b)
1385
+ ds = r.dis(l)
1386
+ puts "Unary minus is idempotent: ds = " + ds.to_s if verbose
1387
+ s += ds
1388
+
1389
+ x = -a
1390
+ y = x + a
1391
+ r = y
1392
+ l = R.c0
1393
+ ds = r.dis(l)
1394
+ puts "Unary -: ds = " + ds.to_s if verbose
1395
+ s += ds
1396
+
1397
+ l = a.sin * b.cos + a.cos * b.sin
1398
+ r = (a + b).sin
1399
+ ds = r.dis(l)
1400
+ puts "Addition theorem for sin: ds = " + ds.to_s if verbose
1401
+ s += ds
1402
+
1403
+ l = a.sin ** 2 + a.cos ** 2
1404
+ r = R.c1
1405
+ ds = r.dis(l)
1406
+ puts "sin^2 + cos^2: ds = " + ds.to_s if verbose
1407
+ s += ds
1408
+
1409
+ x = a.sin
1410
+ y = a.cos
1411
+ l = x.hypot(y)
1412
+ r = R.c1
1413
+ ds = r.dis(l)
1414
+ puts "hypot: ds = " + ds.to_s if verbose
1415
+ s += ds
1416
+
1417
+ phi = (R.ran(i) - R.i2) * R.pi * 2
1418
+ x = phi.cos
1419
+ y = phi.sin
1420
+ r = phi
1421
+ l = x.arg(y)
1422
+ ds = r.dis(l)
1423
+ puts "arg: ds = " + ds.to_s if verbose
1424
+ s += ds
1425
+
1426
+ l = a.exp * b.exp
1427
+ r = (a + b).exp
1428
+ ds = r.dis(l)
1429
+ puts "Addition theorem for exp: ds = " + ds.to_s if verbose
1430
+ s += ds
1431
+
1432
+ l = b
1433
+ r = b.exp.log
1434
+ ds = r.dis(l)
1435
+ puts "exp and log: ds = " + ds.to_s if verbose
1436
+ s += ds
1437
+
1438
+ x = c.abs
1439
+ l = x
1440
+ r = x.log.exp
1441
+ ds = r.dis(l)
1442
+ puts "log and exp: ds = " + ds.to_s if verbose
1443
+ s += ds
1444
+
1445
+ i +=1
1446
+ a = R.tob(i)
1447
+ l = a.sin
1448
+ r = l.asin.sin
1449
+ ds = r.dis(l)
1450
+ puts "asin and sin: ds = " + ds.to_s if verbose
1451
+ s += ds
1452
+
1453
+ i +=1
1454
+ a = R.tob(i)
1455
+ l = a.cos
1456
+ r = l.acos.cos
1457
+ ds = r.dis(l)
1458
+ puts "acos and cos: ds = " + ds.to_s if verbose
1459
+ s += ds
1460
+
1461
+ i +=1
1462
+ a = R.tob(i)
1463
+ l = a.tan
1464
+ r = l.atan.tan
1465
+ ds = r.dis(l)
1466
+ puts "atan and tan: ds = " + ds.to_s if verbose
1467
+ s += ds
1468
+
1469
+ i +=1
1470
+ a = R.tob(i)
1471
+ l = a.cot
1472
+ r = l.acot.cot
1473
+ ds = r.dis(l)
1474
+ puts "acot and cot: ds = " + ds.to_s if verbose
1475
+ s += ds
1476
+
1477
+ i +=1
1478
+ a = R.tob(i,true) # smaller version, in order
1479
+ # not to overload function exp
1480
+ l = a.sinh
1481
+ r = l.asinh.sinh
1482
+ ds = r.dis(l)
1483
+ puts "asinh and sinh: ds = " + ds.to_s if verbose
1484
+ s += ds
1485
+
1486
+ i +=1
1487
+ a = R.tob(i,true)
1488
+ l = a.cosh
1489
+ r = l.acosh.cosh
1490
+ ds = r.dis(l)
1491
+ puts "acosh and cosh: ds = " + ds.to_s if verbose
1492
+ s += ds
1493
+
1494
+ i +=1
1495
+ a = R.tob(i,true)
1496
+ l = a.tanh
1497
+ r = l.atanh.tanh
1498
+ ds = r.dis(l)
1499
+ puts "atanh and tanh: ds = " + ds.to_s if verbose
1500
+ s += ds
1501
+
1502
+ i +=1
1503
+ a = R.tob(i,true)
1504
+ l = a.coth
1505
+ r = l.acoth.coth
1506
+ ds = r.dis(l)
1507
+ puts "acoth and coth: ds = " + ds.to_s if verbose
1508
+ s += ds
1509
+
1510
+ i += 1
1511
+ a = R.tob(i,true)
1512
+ i += 1
1513
+ b = R.tob(i,true)
1514
+ i += 1
1515
+ c = R.tob(i,true)
1516
+ i += 1
1517
+
1518
+ ap = a.abs
1519
+ l = (ap ** b) ** c
1520
+ r = ap ** (b * c)
1521
+ ds = r.dis(l)
1522
+ "general power: ds = " + ds.to_s if verbose
1523
+ s += ds
1524
+
1525
+ l = (ap ** b) * (ap ** c)
1526
+ r = ap ** (b + c)
1527
+ ds = r.dis(l)
1528
+ puts "general power, addition theorem: ds = " + ds.to_s if verbose
1529
+ s += ds
1530
+
1531
+ x=(a.abs+b.abs+c.abs)
1532
+ l = x.sqrt
1533
+ r = x ** 0.5
1534
+ ds = r.dis(l)
1535
+ puts "square root as power: ds = " + ds.to_s if verbose
1536
+ s += ds
1537
+
1538
+ bi = i % 11 -6
1539
+ ci = i % 7 - 3
1540
+ bi = 7 if bi.zero?
1541
+ ci = 3 if ci.zero?
1542
+ # avoid trivial 0
1543
+ l = (a ** bi) ** ci
1544
+ r = a ** (bi * ci)
1545
+ puts "bi = " + bi.to_s + " ci = " + ci.to_s if verbose
1546
+ ds = r.dis(l)
1547
+ puts "integer power: ds = " + ds.to_s if verbose
1548
+ s += ds
1549
+
1550
+ r = b
1551
+ l = b.clone
1552
+ ds = r.dis(l)
1553
+ puts "cloning: ds = " + ds.to_s if verbose
1554
+ s += ds
1555
+ ds = (l == r ? 0.0 : 1.0)
1556
+ puts "cloning and ==: ds = " + ds.to_s if verbose
1557
+ x = a
1558
+ y = b
1559
+ p, q = x.divmod(y)
1560
+ l = x
1561
+ r = y * p + q
1562
+ ds = r.dis(l)
1563
+ puts "divmod 1: ds = " + ds.to_s if verbose
1564
+ s += ds
1565
+
1566
+ x = a
1567
+ y = -b
1568
+ p, q = x.divmod(y)
1569
+ l = x
1570
+ r = y * p + q
1571
+ ds = r.dis(l)
1572
+ puts "divmod 2: ds = " + ds.to_s if verbose
1573
+ s += ds
1574
+
1575
+ x = b
1576
+ y = a
1577
+ p, q = x.divmod(y)
1578
+ l = x
1579
+ r = y * p + q
1580
+ ds = r.dis(l)
1581
+ puts "divmod 3: ds = " + ds.to_s if verbose
1582
+ s += ds
1583
+
1584
+ x = b
1585
+ y = -a
1586
+ p, q = x.divmod(y)
1587
+ l = x
1588
+ r = y * p + q
1589
+ ds = r.dis(l)
1590
+ puts "divmod 4: ds = " + ds.to_s if verbose
1591
+ s += ds
1592
+
1593
+ x, y = a.frexp
1594
+ l = a
1595
+ r = x.ldexp(y)
1596
+ ds = r.dis(l)
1597
+ puts "frexp and ldexp: ds = " + ds.to_s if verbose
1598
+ s += ds
1599
+
1600
+ x1 = R.c 1100000
1601
+ x2 = R.c "1100000 with comment which will be ignored"
1602
+ x3 = R.c(1200000.12)
1603
+ x4 = R.c("1200000.12")
1604
+ x5 = R.c("34567.89001953125e2")
1605
+ x6 = R.c("345.6789001953125E4")
1606
+
1607
+ l = x1
1608
+ r = x2
1609
+ ds = r.dis(l)
1610
+ puts "input 1: ds = " + ds.to_s if verbose
1611
+ s += ds
1612
+
1613
+ l = x3
1614
+ r = x4
1615
+ ds = r.dis(l)
1616
+ puts "input 2: ds = " + ds.to_s if verbose
1617
+ s += ds
1618
+
1619
+ l = x5
1620
+ r = x6
1621
+ ds = r.dis(l)
1622
+ puts "input 3: ds = " + ds.to_s if verbose
1623
+ s += ds
1624
+
1625
+ x = R.c9
1626
+ h = R.c(1e-6)
1627
+ fp = (x + h).erf
1628
+ fm = (x - h).erf
1629
+ l = (fp - fm)/(h * 2)
1630
+ r = (-x*x).exp*R.c2/R.pi.sqrt
1631
+ ds = r.dis(l)
1632
+ puts "erf derivative : ds = " + ds.to_s if verbose
1633
+ s += ds
1634
+
1635
+ t2 = Time.now
1636
+ puts "class of s is " + s.class.to_s + " ."
1637
+ puts "The error sum s is " + s.to_s + " ."
1638
+ puts "It should be close to 0."
1639
+ puts "Computation time was #{t2-t1} seconds."
1640
+ s
1641
+ end
1642
+
1643
+ protected :coerce, :asin_aux, :atan_aux, :exp10,
1644
+ :erfc_asy, :erf_ps
1645
+
1646
+ end # class R
1647
+
1648
+ end # module AppMath