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,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
+