appmath 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,223 @@
1
+ =begin rdoc
2
+ ruby
3
+
4
+ Ulrich Mutze, www.ulrichmutze.de
5
+
6
+ 2008-12-07
7
+
8
+ Extends the class Float.
9
+
10
+ Requires no file.
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
+ # Usage:
29
+ # require 'float_ext.rb'
30
+ # x = 1.37; y = 0.123
31
+ # phi = x.arg(y)
32
+ # r = x.hypot(y)
33
+ # dx = r * phi.cos - x
34
+ # dy = r * phi.sin - y
35
+ #
36
+ # Defining all functons of module Math as methods of class Float.
37
+ # The meaning of functions frexp and ldexp has been changed from
38
+ # referring to exponent 2 to exponent 10.
39
+ # Notice that code that worked without a statement
40
+ # require 'float_ext.rb'
41
+ # will work in an identical manner with this statement added.
42
+ # Only if this is true, extending the functionaliy of standard types is
43
+ # considered acceptable.
44
+ # A few functions, not to be found in module Math have been added.
45
+ # These are methods which are defined in R and thus should be available
46
+ # in Float in order to make R and Float strictly replacable in all code
47
+ # that loads float_ext.rb and rnum.rb.
48
+ # These new functions are:
49
+ # inv, pseudo_inv, conj, cot, coth, acot, acoth, arg,
50
+ # clone, dis, integer?, real?, complex?
51
+ # Notice also, that for R.prec = 0, the real-number generating functions
52
+ # R.ran, R.tob, R.c, R.i, R.pi, R.e
53
+ # R.c0, R.c1, ..., R.c10, R.i2, ... R.i10
54
+ # that normally return R-numbers are forced to return Floats so that it
55
+ # is simple to write programs in such a manner that all real numbers
56
+ # switch their type from Float to R and vice versa.
57
+ # The class R is coded in a manner that it makes no use of the present
58
+ # extension of Float, although this would have allowed some code
59
+ # reduction.
60
+
61
+ class Float
62
+
63
+ def sqrt; Math.sqrt(self); end
64
+
65
+ # x.hypot(y) is an efficient and accurate representation of the
66
+ # square root of x*x + y*y.
67
+ def hypot(y); Math.hypot(self,y); end
68
+
69
+ # It is convenient to have inversion (the multiplicative analogon
70
+ # of the unary - operation) as a member function.
71
+ def inv; 1.0/self; end
72
+
73
+ # pseudo inverse, which always exists
74
+ def pseudo_inv; zero? ? 0.0 : 1.0/self; end
75
+
76
+ # (Complex) conjugation, no effect on real numbers.
77
+ # Supports the unified treatment of real and complex numbers.
78
+ def conj; self; end
79
+
80
+ def exp; Math.exp(self); end
81
+ # method log (unlike function Math.log) is defined in a mathematically
82
+ # reasonable manner also for negative arguments.
83
+ def log; Math.log(abs); end
84
+
85
+ # method log10 (unlike function Math.log10) is defined in a
86
+ # mathematically reasonable manner also for negative arguments.
87
+ def log10; Math.log10(abs); end
88
+
89
+ def sin; Math.sin(self); end
90
+ def sinh; Math.sinh(self); end
91
+ def asin; Math.asin(self); end
92
+ def asinh; Math.asinh(self); end
93
+
94
+ def cos; Math.cos(self); end
95
+ def cosh; Math.cosh(self); end
96
+ def acos; Math.acos(self); end
97
+ def acosh; Math.acosh(self); end
98
+
99
+ def tan; Math.tan(self); end
100
+ def tanh; Math.tanh(self); end
101
+ def atan; Math.atan(self); end
102
+ def atan2(x); Math.atan2(self,x); end
103
+
104
+ # The name 'arg' stands for 'agument', which is the name for the
105
+ # polar angle of a complex number preferred in the mahematical
106
+ # literature. So for a complex number z = u + iv we have
107
+ # argument(z) = u.arg(v)
108
+ # Notice also the representation of the absolute value of z:
109
+ # |z| = u.hypot(v)
110
+ # The functions hypot and arg (or atan2) thus prepare the introduction
111
+ # of complex numbers.
112
+ def arg(y); Math.atan2(y,self); end
113
+
114
+ def atanh; Math.atanh(self); end
115
+
116
+ # All functions having 'cot' in their name deal with the trigonometric
117
+ # or hyperbolic cotangent. These functions are so directly related to
118
+ # the corresponding 'tan'-functions that they are not included in the
119
+ # Math and BigMath modules. It is sometimes useful to have them, though.
120
+ def cot; Math.cos(self)/Math.sin(self); end
121
+ def coth; Math.cosh(self)/Math.sinh(self); end
122
+ def acot; Math.atan(1./self); end
123
+ def acoth; 0.5 * Math.log( ((self + 1.0)/(self - 1.0)).abs ) end
124
+
125
+ # error function
126
+ def erf; Math.erf(self); end
127
+
128
+ # complemetary error function
129
+ def erfc; Math.erfc(self) ; end
130
+
131
+ # Warning! x.frexp differs from Math.frexp(x).
132
+ # We need exponent 10 not 2,
133
+ # so we don't use:
134
+ # def frexp; Math.frexp(self);end
135
+ def frexp
136
+ # puts "arg of frex="+to_s
137
+ if zero?
138
+ [ 0.0, 0]
139
+ elsif self > 0.0
140
+ y = Math.log10(self)
141
+ yf = y.floor
142
+ yfrac = y - yf
143
+ [ 10.0 ** yfrac, yf.to_i]
144
+ else
145
+ y = Math.log10(-self)
146
+ yf = y.floor
147
+ yfrac = y - yf
148
+ [ - 10.0 ** yfrac, yf.to_i]
149
+ end
150
+ end
151
+
152
+ # Warning! x.ldexp differs from Math.ldexp(x).
153
+ # We need exponent 10 not 2,
154
+ # so we don't use:
155
+ # def ldexp(n); Math.ldexp(self,n);end
156
+ def ldexp(n); self * (10.0 ** n.to_i); end
157
+
158
+ # One should have this!
159
+ def clone
160
+ res = 0.0; res += self; res
161
+ end
162
+
163
+ # For all Float x, y we have 0 <= x.dis(y) <= 1 and x.dis(x) = 0
164
+ # It is a kind of relative distance which should be in the order
165
+ # of magnitude of the smalles positive representable number
166
+ # if x and y are known to differ only by numerical noise.
167
+ def dis(x)
168
+ xf = x.to_f
169
+ a = abs
170
+ b = xf.abs
171
+ d = (self - xf).abs
172
+ s = a + b
173
+ return 0.0 if s.zero?
174
+ d1 = d/s
175
+ d < d1 ? d : d1
176
+ end
177
+
178
+ # Since Float is not Fixnum or Bignum we return 'false'. In scientific
179
+ # computation there may be the need to use various types of 'real number
180
+ # types' but there should be always a clear-cut distinction between
181
+ # integer types and real types.
182
+ def integer?; false; end
183
+
184
+ # Although there may be technical variants in representing real numbers,
185
+ # these all should answer this question with 'yes' since they all
186
+ # model mathematical real numbers.
187
+ def real?; true; end
188
+
189
+ # Not complex, since no second dimesion (filling a plane) is provided.
190
+ def complex?; false; end
191
+
192
+ # Providing the neutral element of addition via a method.
193
+ def to_0; 0.0; end
194
+
195
+ # Providing the neutral element of multiplication via a method.
196
+ def to_1; 1.0; end
197
+
198
+ # Print. Output to console, together with a name which is given by the
199
+ # argument.
200
+ def prn(name)
201
+ puts " #{name} = " + to_s
202
+ end
203
+
204
+ # Returns a real number, the significand of which has not more
205
+ # than n digits. Notice that there is also a function round
206
+ # which takes no argument and which returns an integer number.
207
+ # This function replaces function Float#round.
208
+ def round(*arg)
209
+ n = arg.size
210
+ case n
211
+ when 0
212
+ (self + 0.5).floor # output is integer
213
+ when 1
214
+ m = arg[0].to_i
215
+ x = frexp
216
+ y = x[0].ldexp(m)
217
+ (y + 0.5).floor.to_f.ldexp(x[1] - m)
218
+ else
219
+ fail "needs 0 or 1 arguments"
220
+ end
221
+ end
222
+
223
+ end # class Float
@@ -0,0 +1,415 @@
1
+ =begin rdoc
2
+ ruby
3
+
4
+ Ulrich Mutze, www.urichmutze.de
5
+
6
+ 2008-12-07
7
+
8
+ Defines classes AppMath::Graph, AppMath::Graphs,
9
+ AppMath::[Sin, Cos, ArcSin, Ex, Log, ArcTan, ArcCos, ArcCot,
10
+ Erf, Erfc]
11
+
12
+ Requires files interval and tk
13
+
14
+ Copyright (C) 2008 Ulrich Mutze
15
+
16
+ This program is free software: you can redistribute it and/or modify
17
+ it under the terms of the GNU General Public License as published by
18
+ the Free Software Foundation, either version 3 of the License, or
19
+ (at your option) any later version.
20
+
21
+ This program is distributed in the hope that it will be useful,
22
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ GNU General Public License for more details.
25
+
26
+ You should have received a copy of the GNU General Public License
27
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
28
+ =end
29
+
30
+ require File.join(File.dirname(__FILE__), 'interval')
31
+ require 'tk'
32
+ #require 'interval'
33
+
34
+
35
+ module AppMath
36
+
37
+ =begin rdoc
38
+ class for representing a 'window to the real world'
39
+ With the x-direction there is associated a real interval, and with
40
+ the y-directon there is also associated a real interval. With these intervals
41
+ there are associated integer numbers width and height, which govern the
42
+ 'pixelation' of these intervals.
43
+ =end
44
+ class Graph
45
+
46
+ attr :fac, true
47
+ attr :bgr_color
48
+ attr :n_div, true
49
+ attr :grid_color, true
50
+ attr :text_color, true
51
+
52
+ # Setting the background color.
53
+ def bgr_color=(color)
54
+ @@bgr = color
55
+ clear(@@bgr)
56
+ end
57
+
58
+ # Specifes the parent window, pixel sizes in x- and y-directons, and
59
+ # the intervals of x-values and y-alues associated with the
60
+ # graphical window.
61
+ def initialize(parent,nx,ny,iv_x,iv_y)
62
+ @fac = R.c 1.04
63
+ @bgr_color = 'lightgreen'
64
+ @n_div = 5
65
+ @grid_color = 'darkgray'
66
+ @text_color = 'black'
67
+
68
+ @parent = parent
69
+ @w = nx
70
+ @h = ny
71
+ set_size_x!(iv_x)
72
+ set_size_y!(iv_y)
73
+ # sets @a, @b, @c, @d, @ivx, @ivy
74
+ col = @bgr_color
75
+ @canvas = TkCanvas.new(parent){
76
+ width nx
77
+ height ny
78
+ background col
79
+ }
80
+ @canvas.pack
81
+ end
82
+
83
+ # Returns the interval of x-values.
84
+ def ivx; @ivx; end
85
+
86
+ # Returns the interval of y-values.
87
+ def ivy; @ivy; end
88
+
89
+ # Clearing the graphical window by painting it uniformly
90
+ # with a given color.
91
+ def clear(color=@bgr_color)
92
+ magic = 2 # not expected to need this
93
+ TkcRectangle.new(@canvas, 0, 0, @w + magic, @h + magic,
94
+ 'width'=> 0, 'fill' => color)
95
+ end
96
+
97
+ # Setting vertical lines associated with an array of x-values
98
+ # (given as the first argument) in a color (given as the second
99
+ # argument).
100
+ def grid_x(anArray, color=@grid_color)
101
+ anArray.each{ |x|
102
+ pa = ipos(x,@ivy.low)
103
+ pb = ipos(x,@ivy.upp)
104
+ line = TkcLine.new(@canvas,pa[0],pa[1],pb[0],pb[1])
105
+ line.fill(color)
106
+ }
107
+ end
108
+
109
+ # Setting horizontal lines associated with an array of y-values
110
+ # (given as the first argument) in a color (given as the second
111
+ # argument).
112
+ def grid_y(anArray, color=@grid_color)
113
+ anArray.each{ |y|
114
+ pa = ipos(@ivx.low,y)
115
+ pb = ipos(@ivx.upp,y)
116
+ line = TkcLine.new(@canvas,pa[0],pa[1],pb[0],pb[1])
117
+ line.fill(color)
118
+ }
119
+ end
120
+
121
+ # Drawing a polygon which is determined by an array of
122
+ # x-values and an array of y-values (if these don't have equal
123
+ # length, the smaller of he two sizes is active) in a given
124
+ # color. If the hird argument is true, the intervals of x-values
125
+ # and y-values get adjusted in a manner that the whole polygon can
126
+ # be represented and that the coordinate ranges can be described
127
+ # by simple numbers in the style as numerical ranges for printed
128
+ # diagrams are typically selected.
129
+
130
+ def draw(arr_x, arr_y, color, auto_adjust = true)
131
+ # clear(@bgr_color)
132
+ nx = arr_x.length
133
+ ny = arr_y.length
134
+ n = Basics.inf(nx,ny)
135
+ if auto_adjust
136
+ ivx_e = Iv.from_array(arr_x) * @fac # the values to be shown
137
+ # should stay a bit apat from the rim of the diagram
138
+ ivy_e = Iv.from_array(arr_y) * @fac
139
+ divx = ivx_e.axis_division(@n_div)
140
+ divy = ivy_e.axis_division(@n_div)
141
+ d_x = divx[0]
142
+ d_y = divy[0]
143
+ v_x = divx[1]
144
+ v_y = divy[1]
145
+ ivxNew = Iv.new(v_x.first, v_x.last)
146
+ ivyNew = Iv.new(v_y.first, v_y.last)
147
+ set_size_x!(ivxNew)
148
+ set_size_y!(ivyNew)
149
+ grid_x(v_x, grid_color)
150
+ grid_y(v_y, grid_color)
151
+ end
152
+
153
+ for i in 1...n
154
+ xa = arr_x[i-1]
155
+ xb = arr_x[i]
156
+ ya = arr_y[i-1]
157
+ yb = arr_y[i]
158
+ pa = ipos(xa,ya)
159
+ pb = ipos(xb,yb)
160
+ line = TkcLine.new(@canvas,pa[0],pa[1],pb[0],pb[1])
161
+ line.fill(color)
162
+ end
163
+ if @bgr_color != @text_color # only then writing the text works, and
164
+ # so we have a method to avoid writing text
165
+ # some magic numbers here
166
+ t_x = (@w * 0.1).to_i
167
+ t_y = (@h * 0.1).to_i
168
+ t_w = (@w * 0.75).to_i
169
+ t_h = (@h * 0.02).to_i
170
+ t_h = Basics.sup(t_h,10)
171
+
172
+ font_text = "LucidaConsole "+t_h.to_s
173
+
174
+ xl = @ivx.low.to_s
175
+ xu = @ivx.upp.to_s
176
+ yl = @ivy.low.to_s
177
+ yu = @ivy.upp.to_s
178
+
179
+ x_text=xl + " < x < " + xu + " , dx_grid = " + d_x.to_s
180
+ y_text=yl + " < y < " + yu + " , dy_grid = " + d_y.to_s
181
+ full_text = x_text + "\n\n" + y_text
182
+
183
+ txt=TkcText.new(@canvas,t_x,t_y){
184
+ anchor "nw"
185
+ width t_w
186
+ text full_text
187
+ font font_text
188
+ }
189
+ txt.fill(@text_color)
190
+ end
191
+ end
192
+
193
+ # Drawing a polygon which represents function f. The x-values
194
+ # to be used for creating this polygon are determined by
195
+ # interval @ivx and the number n of points. f is assumed to be
196
+ # a 'function object' in the sense that it dfines a method 'at'
197
+ # so that f.at(x) is what one normally would write as f(x). So
198
+ # 'at' replaces C++'s operator(). Of course, the last argument
199
+ # allows auto-scaling of the y-range.
200
+
201
+ def draw_func(f, n, color = 'red', auto_adjust_y = true)
202
+ arr_x = @ivx.to_array(n)
203
+ arr_y = Array.new(n, 0.0)
204
+ for i in 0...n
205
+ xi = arr_x[i]
206
+ yi = f.at(xi)
207
+ arr_y[i] = yi.to_f
208
+ end
209
+ draw(arr_x, arr_y, color, auto_adjust_y)
210
+ end
211
+
212
+ # integer (pixel-wise) position associated with a point
213
+ # (x,y) in x,y-space. Notice that this is given by a simple
214
+ # (and efficient) formula if the auxiliar quantities
215
+ # @a, @b, @c, @d got their value by means of the next two functions.
216
+
217
+ def ipos x, y
218
+ [@a + @b * x, @c + @d * y]
219
+ end
220
+
221
+ # Setting the active interval of x-values and the
222
+ # auxiliar quantities @a and @b.
223
+ def set_size_x!(aIv)
224
+ @ivx = aIv
225
+ @b = (@w - 1.0)/@ivx.size
226
+ @a = -@b * @ivx.low
227
+ end
228
+
229
+ # Setting the active interval of y-values and the
230
+ # auxiliar quantities @c and @d.
231
+ def set_size_y!(aIv)
232
+ @ivy = aIv
233
+ @d = (1.0 - @h)/@ivy.size
234
+ @c = -@d * @ivy.upp
235
+ end
236
+
237
+ end # class Graph
238
+
239
+ # The code
240
+ # root = TkRoot.new{ title 'Test of ...'}
241
+ # g = Graphs.new(root,3,2,1000,700)
242
+ # generates a 2 times 3 matrix of Graph-objects
243
+ # g.at(0,0), g.at(0,1), g.at(0,2)
244
+ # g.at(1,0), g.at(1,1), g.at(1,2)
245
+ # which together make up a rectangle of 1000 pixels in x-direction
246
+ # and 700 pixels in y-direction. Notice that the matrix is a grid
247
+ # with 3 items in x-direction and 2 items in y-direction.
248
+ # Since these items are of class Graph, they can be used for graphical
249
+ # representations of arrays and of functions without needing support fom
250
+ # the present class Graphs.
251
+ class Graphs
252
+
253
+ attr_reader :px,:py,:mx,:my,:grs
254
+
255
+ # Generating a (py,px)-matrix of Graph-objects, in an rectangular area
256
+ # which is a (ny,nx)-matrix of pixels.
257
+ def initialize(parent,px,py,nx,ny)
258
+ zero = R.c0
259
+ one = R.c1
260
+ @px = px
261
+ @py = py
262
+ @mx = nx / @px
263
+ @my = ny / @py
264
+ ivx = Iv.new(zero,one)
265
+ ivy = Iv.new(zero,one)
266
+ @grs = Array.new
267
+ py.times{ |r|
268
+ gr = Array.new
269
+ px.times{ |c|
270
+ fr = TkFrame.new(parent).grid('row' => r, 'column' => c)
271
+ gr << Graph.new(fr,@mx,@my,ivx,ivy)
272
+ }
273
+ @grs << gr
274
+ }
275
+ end
276
+
277
+ # Access function. For a Graphs-object g, one has the Graph-objects
278
+ # g.at(i,j), 0 <= i < p.py, 0 <= j < g.px .
279
+ # All integer values are allowed and these then are understood
280
+ # periodically.
281
+ def at(i,j)
282
+ return @grs[i.to_i%@py][j.to_i%@px]
283
+ end
284
+
285
+ end
286
+
287
+ # Function objects for testing Graph#draw_func
288
+ # with functions for which the precision of function evaluation
289
+ # is enforced. This enforced precison is set with the creation of the
290
+ # function object.
291
+
292
+ class Sin
293
+
294
+ def initialize(n = R.prec)
295
+ @precMem = R.prec
296
+ @precLoc = n
297
+ end
298
+
299
+ def at(x)
300
+ R.prec = @precLoc
301
+ y = R.c(x).sin
302
+ R.prec = @precMem; y
303
+ end
304
+ end
305
+
306
+ class Cos
307
+ def initialize(n = R.prec)
308
+ @precMem = R.prec
309
+ @precLoc = n
310
+ end
311
+ def at(x)
312
+ R.prec = @precLoc
313
+ y = R.c(x).cos
314
+ R.prec = @precMem; y
315
+ end
316
+ end
317
+
318
+ class ArcSin
319
+ def initialize(n = R.prec)
320
+ @precMem = R.prec
321
+ @precLoc = n
322
+ end
323
+ def at(x)
324
+ R.prec = @precLoc
325
+ y = R.c(x).asin
326
+ R.prec = @precMem; y
327
+ end
328
+ end
329
+
330
+ class Exp
331
+ def initialize(n = R.prec)
332
+ @precMem = R.prec
333
+ @precLoc = n
334
+ end
335
+ def at(x)
336
+ R.prec = @precLoc
337
+ y = R.c(x).exp
338
+ R.prec = @precMem; y
339
+ end
340
+ end
341
+
342
+ class Log
343
+ def initialize(n = R.prec)
344
+ @precMem = R.prec
345
+ @precLoc = n
346
+ end
347
+ def at(x)
348
+ R.prec = @precLoc
349
+ y = R.c(x).log
350
+ R.prec = @precMem; y
351
+ end
352
+ end
353
+
354
+ class ArcTan
355
+ def initialize(n = R.prec)
356
+ @precMem = R.prec
357
+ @precLoc = n
358
+ end
359
+ def at(x)
360
+ R.prec = @precLoc
361
+ y = R.c(x).atan
362
+ R.prec = @precMem; y
363
+ end
364
+ end
365
+
366
+ class ArcCos
367
+ def initialize(n = R.prec)
368
+ @precMem = R.prec
369
+ @precLoc = n
370
+ end
371
+ def at(x)
372
+ R.prec = @precLoc
373
+ y = R.c(x).acos
374
+ R.prec = @precMem; y
375
+ end
376
+ end
377
+
378
+ class ArcCot
379
+ def initialize(n = R.prec)
380
+ @precMem = R.prec
381
+ @precLoc = n
382
+ end
383
+ def at(x)
384
+ R.prec = @precLoc
385
+ y = R.c(x).acot
386
+ R.prec = @precMem; y
387
+ end
388
+ end
389
+
390
+ class Erf
391
+ def initialize(n = R.prec)
392
+ @precMem = R.prec
393
+ @precLoc = n
394
+ end
395
+ def at(x)
396
+ R.prec = @precLoc
397
+ y = R.c(x).erf
398
+ R.prec = @precMem; y
399
+ end
400
+ end
401
+
402
+ class Erfc
403
+ def initialize(n = R.prec)
404
+ @precMem = R.prec
405
+ @precLoc = n
406
+ end
407
+ def at(x)
408
+ R.prec = @precLoc
409
+ y = R.c(x).erfc
410
+ R.prec = @precMem; y
411
+ end
412
+ end
413
+
414
+ end # AppMath
415
+