appmath 0.0.1

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.
@@ -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