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,118 @@
1
+ =begin rdoc
2
+ ruby
3
+
4
+ Ulrich Mutze, www.ulrichmutze.de
5
+
6
+ 2008-12-09
7
+
8
+ Defines the module AppMath::Basics.
9
+
10
+ Requires files rnum and float_ext.
11
+
12
+ The intention is that a statement
13
+ require 'appmath_basics.rb'
14
+ allows us to use in the following a programming style in which the concept
15
+ of real numbers is handled in a flexibel manner so that switching from
16
+ standard type Float to arbitrary precision type R can be achieved
17
+ by a single statement of the type
18
+ R.prec = 0 ( for using Float)
19
+ or
20
+ R.prec = 100 ( for using R with 100 decimal places )
21
+ All modules and classes to be defined furtheron within the module
22
+ AppMath
23
+ will require appmath_basics.rb and will use real numbers in the manner
24
+ indicated above.
25
+ Copyright (C) 2008 Ulrich Mutze
26
+
27
+ This program is free software: you can redistribute it and/or modify
28
+ it under the terms of the GNU General Public License as published by
29
+ the Free Software Foundation, either version 3 of the License, or
30
+ (at your option) any later version.
31
+
32
+ This program is distributed in the hope that it will be useful,
33
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
34
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35
+ GNU General Public License for more details.
36
+
37
+ You should have received a copy of the GNU General Public License
38
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
39
+ =end
40
+
41
+ require File.join(File.dirname(__FILE__), 'rnum')
42
+ require File.join(File.dirname(__FILE__), 'float_ext')
43
+ #require 'rnum'
44
+ #require 'float_ext'
45
+
46
+ # A Module which collects concepts of applied mathematics and of physics.
47
+ module AppMath
48
+
49
+ # A Module which collects simple utility functions.
50
+ module Basics
51
+
52
+ # Returns the larger of the two numbers a and b.
53
+ def Basics.sup(a, b)
54
+ a <= b ? b : a
55
+ end
56
+
57
+ # Returns the smaller of the two numbers a and b.
58
+ def Basics.inf(a, b)
59
+ a < b ? a : b
60
+ end
61
+
62
+ # Returns the sign of a as a real number.
63
+ def Basics.sign(a)
64
+ if a > R.c0
65
+ R.c1
66
+ elsif a < R.c0
67
+ -R.c1
68
+ else
69
+ R.c0
70
+ end
71
+ end
72
+
73
+ # Returns 'a with the sign of b'. Programming idiom frequently used in
74
+ # the 'Numerical Recipes in C' by Press et al.
75
+ def Basics.sign2(a, b)
76
+ b >= R.c0 ? a.abs : -a.abs
77
+ end
78
+
79
+ # Returns the nearest number to x, which up to sign and power of 10
80
+ # is one of the numbers in the array c in the function
81
+ # body. For instance,
82
+ # c = [1,2,5,7.5,10]
83
+ # In a sense, here cutting is a more drastic version of rounding.
84
+ # It will be used in order to create reasonable axis subdivsion in
85
+ # graphical representation of data.
86
+ def Basics.cut(x)
87
+ fail "x is not a number" if x.nan?
88
+ fail "x is not finite" if x.infinite?
89
+ c = [R.c1,R.c2,R.c5,R.c(7.5),R.c10]
90
+ return R.c0 if x.zero?
91
+ s = Basics.sign(x)
92
+ y1 = x.abs
93
+ ylog = y1.log10
94
+ ylogInt = ylog.floor
95
+ yMantisse = ylog - ylogInt
96
+ y = R.c10 ** yMantisse
97
+ fail "Basics.cut(): !(1 <= y)" if !(R.c1 <= y)
98
+ fail "Basics.cut(): !(y < 10)" if !(y < R.c10)
99
+ i = 0
100
+ while y > c[i]
101
+ i += 1
102
+ end
103
+ cd = c.length
104
+ fail "unexpected failure in function Basics.cut" if i < 0 || i >= cd
105
+ yu = c[i] # yu is the smallest c[k] which is larger or equal to y
106
+ if i==0
107
+ yf = yu
108
+ else
109
+ fail "unexpected failure in function Basics.cut" if (i-1) < 0 || (i-1) >= cd
110
+ yl = c[i-1]
111
+ yf = yu-y <y -yl ? yu : yl # now yf is the c[k] which is nearest to y
112
+ end
113
+ return yf.ldexp(ylogInt) * s # sign and exponent are restored
114
+ end
115
+
116
+ end # Basics
117
+
118
+ end # module AppMath
@@ -0,0 +1,615 @@
1
+ =begin rdoc
2
+ ruby
3
+
4
+ Ulrich Mutze www.ulrichmutze.de
5
+
6
+ Started 2008-12-03 by modifying cpmc.h and cpmc.cpp
7
+
8
+ Defining a class C of complex numbers.
9
+
10
+ Defines the class AppMath::C.
11
+
12
+ Requires file appmath_basics.
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__), 'appmath_basics')
31
+ #require 'appmath_basics'
32
+
33
+ module AppMath
34
+
35
+ # Class of arbitrary precision complex numbers. The underlying real numbers may be Float
36
+ # or R.
37
+ class C < Numeric
38
+
39
+ attr_reader :re, :im
40
+
41
+ def initialize(*arg)
42
+ n = arg.size
43
+ case n
44
+ when 0
45
+ @re = R.c0
46
+ @im = R.c0
47
+ when 1
48
+ a0 = arg[0]
49
+ if a0.integer? || a0.real?
50
+ @re = R.c a0
51
+ @im = R.c0
52
+ elsif a0.complex?
53
+ @re = R.c a0.re
54
+ @im = R.c a0.im
55
+ else
56
+ fail "can't construct a C from this argument"
57
+ end
58
+ when 2
59
+ a0 = R.c arg[0]; a1 = R.c arg[1]
60
+ @re = a0
61
+ @im = a1
62
+ else
63
+ fail "can't construct a C from more than two arguments"
64
+ end
65
+ end
66
+
67
+ def clone; C.new(@re,@im); end
68
+
69
+ # The constant 0.
70
+ def C.zero
71
+ C.new
72
+ end
73
+
74
+ # The constant 1.
75
+ def C.one
76
+ C.new(R.c1,R.c0)
77
+ end
78
+ # The constant i
79
+ def C.i
80
+ C.new(R.c0,R.c1)
81
+ end
82
+
83
+ # Random value (sine-floor random generator).
84
+ #
85
+ # Chaotic function from the integers into the
86
+ # subset [0,1] x [0,1] of C
87
+
88
+ def C.ran(anInteger)
89
+ ai = anInteger.to_i * 2
90
+ x1 = R.ran(ai)
91
+ x2 = R.ran(ai + 1)
92
+ C.new(x1,x2)
93
+ end
94
+
95
+ # Test object.
96
+ #
97
+ # Needed for automatic tests of arithmetic relations. Intended
98
+ # to give numbers which rapidly change sign and order of
99
+ # magnitude when the argument grows regularly e.g.
100
+ # as in 1,2,3,... . However, suitibility as a random generator is
101
+ # not the focus. If the second argument is 'true', the result
102
+ # is multplied by a number << 1 in order to prevent the result
103
+ # from overloading the exponential function.
104
+
105
+ def C.tob(anInteger, small = false)
106
+ ai = anInteger.to_i * 2
107
+ x1 = R.tob(ai,small)
108
+ x2 = R.tob(ai + 1,small)
109
+ C.new(x1,x2)
110
+ end
111
+
112
+ # Unary minus operator. It returns the C-object -self.
113
+ def -@; C.new(-@re, -@im); end
114
+
115
+ # Unary plus operator. It returns the C-object self.
116
+ def +@; self; end
117
+
118
+ # (Complex) conjugation, no effect on real numbers.
119
+ # Supports the unified treatment of real and complex numbers.
120
+ def conj; C.new(@re, -@im); end
121
+
122
+ # Returns self times i.
123
+ def ti; self * C.i; end
124
+
125
+ # Returns self divided by i.
126
+ def dbi; self * C.new(0,-1); end
127
+
128
+ # Returns the absolute value of self.
129
+ def abs; @re.hypot(@im); end
130
+
131
+ # Returns the absolute value squared of self.
132
+ def abs2; @re * @re + @im * @im; end
133
+
134
+ # Returns the argument (i.e. the polar angle) of self.
135
+ def arg; @re.arg(@im); end
136
+
137
+ # Redefining coerce from Numeric.
138
+ # This allows writing 1 + C.new(137) instead of C.new(137) + 1
139
+ # or C.new(137) + R.c1.
140
+ #-- Notice that the order in the resulting array is essential for
141
+ # correct functionality.
142
+ def coerce(a)
143
+ [ C.new(a), self]
144
+ end
145
+
146
+ # The order relation is here lexicographic ordering based on the
147
+ # agreement that re is the 'first letter' and im the 'second letter'
148
+ # of 'the word'. Needed only for book-keeping purposes.
149
+ def <=> (a)
150
+ cr = @re <=> a.re
151
+ return cr unless cr.zero?
152
+ ci = @im <=> a.im
153
+ return ci unless ci.zero?
154
+ return 0
155
+ end
156
+
157
+ # Returns 'true' if self equals zero.
158
+ def zero?; @re.zero? && @im.zero?; end
159
+
160
+ # Returns 'true' if self is 'not a number' (NaN).
161
+ def nan?; @re.nan? || @im.nan?; end
162
+
163
+ # Returns 'true' if the real art or the iaginary part of self is
164
+ # infinite.
165
+ def infinite?; @re.infinite? || @im.infinite?; end
166
+
167
+ # Since R is not Fixnum or Bignum we return 'false'. In scientific
168
+ # computation there may be the need to use various types of
169
+ # 'real number types' but there should always a clear-cut distinction
170
+ # between integer types and real types.
171
+ def integer?; false; end
172
+
173
+ # Supports the unified treatment of real and complex numbers.
174
+ def real?; false; end
175
+
176
+ # Supports the unified treatment of real and complex numbers.
177
+ def complex?; true; end
178
+
179
+ # Returns the C-object self + a.
180
+ def +(a)
181
+ if a.integer? || a.real?
182
+ b = R.c a
183
+ C.new(@re + b, @im)
184
+ elsif a.complex?
185
+ C.new(@re + a.re, @im + a.im)
186
+ else
187
+ fail "cannot add this argument to a complex number"
188
+ end
189
+ end
190
+
191
+ # Returns the C-object self - a.
192
+ def -(a)
193
+ if a.integer? || a.real?
194
+ b = R.c a
195
+ C.new(@re - b, @im)
196
+ elsif a.complex?
197
+ C.new(@re - a.re, @im - a.im)
198
+ else
199
+ fail "cannot subtract this argument from a complex number"
200
+ end
201
+
202
+ end
203
+
204
+ # Returns the C-object self * a.
205
+ def *(a)
206
+ if a.integer? || a.real?
207
+ b = R.c a
208
+ C.new(@re * b, @im * b)
209
+ elsif a.complex?
210
+ C.new(@re * a.re - @im * a.im , @re * a.im + @im * a.re )
211
+ else
212
+ fail "cannot multiply a complex number with this argument"
213
+ end
214
+ end
215
+
216
+ # Returns the C-object self / a.
217
+ def /(a)
218
+ if a.integer? || a.real?
219
+ b = R.c a
220
+ C.new(@re / b, @im / b)
221
+ elsif a.complex?
222
+ r2 = a.abs2
223
+ C.new((@re * a.re + @im * a.im)/r2 , (@im * a.re - @re * a.im)/r2 )
224
+ else
225
+ fail "cannot divide a complex number by this argument"
226
+ end
227
+ end
228
+
229
+ # Exponential function.
230
+ def exp; C.new(@im.cos, @im.sin) * @re.exp; end
231
+
232
+ # Exponential function of the argument multiplied by C.i
233
+ def expi; (self * C.i).exp; end
234
+
235
+
236
+ # Natural logarithm.
237
+ def log; C.new(abs.log,arg); end
238
+
239
+ # Returns the a-th power of self. A may be integer, real, or
240
+ # complex. The result is always complex.
241
+ def **(a)
242
+ return C.nan if nan?
243
+ if a.integer?
244
+ if a.zero?
245
+ C.one
246
+ elsif a == 1
247
+ self
248
+ elsif a == -1
249
+ inv
250
+ else
251
+ b = a.abs
252
+ res = self
253
+ for i in 1...b
254
+ res *= self
255
+ end
256
+ if a < 0
257
+ res = res.inv
258
+ end
259
+ res
260
+ end
261
+ elsif a.real?
262
+ b = C.new(a)
263
+ (log * b).exp
264
+ elsif a.complex?
265
+ (log * a).exp
266
+ else
267
+ fail "Argument not acceptable as an exponent"
268
+ end
269
+ end
270
+
271
+ # Returns the zero-element which belongs to the same class than self
272
+ def to_0; C.zero; end
273
+
274
+ # Returns the unit-element which belongs to the same class than self
275
+ def to_1; C.one; end
276
+
277
+ # Returns the inverse 1/self.
278
+ def inv
279
+ C.one / self
280
+ end
281
+
282
+ # Returns 'true' iff self == C(0,0)
283
+ def zero?; @re.zero? && @im.zero?; end
284
+
285
+ # The pseudo_inverse of zero is zero, and equal to the inverse for
286
+ # all other arguments.
287
+ def pseudo_inv
288
+ zero? ? C.zero : C.one / self
289
+ end
290
+
291
+ # For the return value res we have res.int? true and (self - res).abs <= 0.5
292
+ def round(n)
293
+ u = @re.round(n)
294
+ v = @im.round(n)
295
+ C.new(u,v)
296
+ end
297
+
298
+ # Returns the square root of self.
299
+ def sqrt
300
+ self ** R.i2
301
+ end
302
+
303
+ # Returns a kind of relative distance between self and aR.
304
+ # The return value varies from 0 to 1, where 1 means maximum dissimilarity
305
+ # of the arguments.
306
+ # Such a function is needed for testing the validity of arithmetic laws,
307
+ # which, due to numerical noise, should not be expected to be fulfilled
308
+ # exactly.
309
+ def dis(aC)
310
+ a = abs
311
+ b = aC.abs
312
+ d = (self - aC).abs
313
+ s = a + b
314
+ return R.c0 if s.zero?
315
+ d1 = d/s
316
+ Basics.inf(d,d1)
317
+ end
318
+
319
+ # Conversion to String.
320
+ def to_s; "C(#{@re}, #{@im})"; end
321
+
322
+ # Printing the value together with a label
323
+ def prn(name)
324
+ puts "#{name} = " + to_s
325
+ end
326
+ #=begin
327
+
328
+ # Sine.
329
+ def sin
330
+ (expi - (-self).expi) * C.new(0,-R.i2)
331
+ end
332
+
333
+ # Cosine.
334
+ def cos
335
+ (expi + (-self).expi) * R.i2
336
+ end
337
+
338
+ # Tangent.
339
+ def tan
340
+ sin / cos
341
+ end
342
+
343
+ # Cotangent.
344
+ def cot
345
+ cos / sin
346
+ end
347
+
348
+ # Hyperbolic sine.
349
+ def sinh; (exp - (-self).exp) * R.i2; end
350
+
351
+ # Hyperbolic cosine.
352
+ def cosh; (exp + (-self).exp) * R.i2; end
353
+
354
+ # Hyperbolic tangent.
355
+ def tanh
356
+ s = exp - (-self).exp
357
+ c = exp + (-self).exp
358
+ s/c
359
+ end
360
+
361
+ # Hyperbolic cotangent.
362
+ def coth
363
+ s = exp - (-self).exp
364
+ c = exp + (-self).exp
365
+ c/s
366
+ end
367
+
368
+ # Inverse hyperbolic sine.
369
+ def asinh
370
+ ((self * self + C.one).sqrt + self).log
371
+ end
372
+
373
+ # Inverse hyperbolic cosine.
374
+ def acosh
375
+ ((self * self - C.one).sqrt + self).log
376
+ end
377
+
378
+ # Inverse hyperbolic tangent.
379
+ def atanh
380
+ ((C.one + self)/(C.one - self)).log * R.i2
381
+ end
382
+
383
+ # Inverse hyperbolic cotangent.
384
+ def acoth
385
+ ((self + C.one)/(self - C.one)).log * R.i2
386
+ end
387
+
388
+ # Inverse sine.
389
+ def asin
390
+ ti.asinh.dbi
391
+ end
392
+
393
+ # Inverse cosine.
394
+ def acos
395
+ acosh.dbi
396
+ end
397
+
398
+ # Inverse tangent.
399
+ def atan
400
+ ti.atanh.dbi
401
+ end
402
+
403
+ # Inverse cotangent.
404
+ def acot
405
+ ti.acoth.ti
406
+ end
407
+
408
+ # Consistency test for class C
409
+ # This is intended to keep the class consistent despite of modifications.
410
+ # The first argument influences the numbers which are selected for the
411
+ # test. Returned is a sum of numbers each of which should be numerical
412
+ # noise and so the result has to be << 1 if the test is to indicate
413
+ # success.
414
+ # For instance, on my system
415
+ #
416
+ # Doing C.test(n = 137, verbose = false) for R.dig = 100:
417
+ # *************************************************
418
+ # class of s is AppMath::R
419
+ # class of s is AppMath::R .
420
+ # The error sum s is 0.95701879151814897746312007872622225589589551941
421
+ # 73186692168823486932509515793972625242699350133964052E-98 .
422
+ # It should be close to 0.
423
+ # Computation time was 1.062 seconds.
424
+
425
+ def C.test(n0, verbose = false )
426
+ puts "Doing C.test(n = #{n0}, verbose = #{verbose})" +
427
+ " for R.dig = #{R.dig}:"
428
+ puts "*************************************************"
429
+ t1 = Time.now
430
+ small = true # otherwise not all inverse function tests work well
431
+ s = R.c0
432
+ puts "class of s is " + s.class.to_s
433
+ i = n0
434
+ a = C.tob(i,small)
435
+ i += 1
436
+ b = C.tob(i,small)
437
+ i += 1
438
+ c = C.tob(i,small)
439
+ i += 1
440
+
441
+ if verbose
442
+ a.prn("a")
443
+ b.prn("b")
444
+ c.prn("c")
445
+ end
446
+
447
+ r = 2 + a
448
+ l = a + 2
449
+ ds = r.dis(l)
450
+ puts "coerce 2 + a: ds = " + ds.to_s if verbose
451
+ s += ds
452
+
453
+ r = a + 1.234
454
+ l = a + R.c(1.234)
455
+ ds = r.dis(l)
456
+ puts "coerce a + float: ds = " + ds.to_s if verbose
457
+ s += ds
458
+
459
+ r = (a + b) * c
460
+ l = a * c + b * c
461
+
462
+ ds = r.dis(l)
463
+ puts "Distributive law for +: ds = " + ds.to_s if verbose
464
+ s += ds
465
+
466
+ r = (a - b) * c
467
+ l = a * c - b * c
468
+ ds = r.dis(l)
469
+ puts "Distributive law for -: ds = " + ds.to_s if verbose
470
+ s += ds
471
+
472
+ r = (a * b) * c
473
+ l = b * (c * a)
474
+ ds = r.dis(l)
475
+ puts "Multiplication: ds = " + ds.to_s if verbose
476
+ s += ds
477
+
478
+ r = (a * b) / c
479
+ l = (a / c) * b
480
+ ds = r.dis(l)
481
+ puts "Division: ds = " + ds.to_s if verbose
482
+ s += ds
483
+
484
+ r = C.one
485
+ l = a * a.inv
486
+ ds = r.dis(l)
487
+ puts "inv: ds = " + ds.to_s if verbose
488
+ s += ds
489
+
490
+ r = 1/a
491
+ l = a.inv
492
+ ds = r.dis(l)
493
+ puts "inv and 1/x: ds = " + ds.to_s if verbose
494
+ s += ds
495
+
496
+ r = b
497
+ l = -(-b)
498
+ ds = r.dis(l)
499
+ puts "Unary minus is idempotent: ds = " + ds.to_s if verbose
500
+ s += ds
501
+ x = -a
502
+ y = x + a
503
+ r = y
504
+ l = C.zero
505
+ ds = r.dis(l)
506
+ puts "Unary -: ds = " + ds.to_s if verbose
507
+ s += ds
508
+
509
+ l = a
510
+ x = a.sqrt
511
+ r = x * x
512
+ s = r.dis(l)
513
+ puts "square root: ds = " + ds.to_s if verbose
514
+ s += ds
515
+
516
+ n = 11
517
+ l = a ** n
518
+ r = a ** C.new(n)
519
+ ds = r.dis(l)
520
+ puts "power with integer exponent: ds = " + ds.to_s if verbose
521
+ s += ds
522
+
523
+ n = -7
524
+ l = a ** n
525
+ r = a ** C.new(n)
526
+ ds = r.dis(l)
527
+ puts "power with negative integer exponent: ds = " + ds.to_s if verbose
528
+ s += ds
529
+
530
+ l = -C.one
531
+ r = (C.i * R.pi).exp
532
+ ds = r.dis(l)
533
+ puts "Euler's relation: ds = " + ds.to_s if verbose
534
+ s += ds
535
+
536
+ l = a.sin * b.cos + a.cos * b.sin
537
+ r = (a + b).sin
538
+ ds = r.dis(l)
539
+ puts "Addition theorem for sin: ds = " + ds.to_s if verbose
540
+ s += ds
541
+
542
+ l = a.exp * b.exp
543
+ r = (a + b).exp
544
+ ds = r.dis(l)
545
+ puts "Addition theorem for exp: ds = " + ds.to_s if verbose
546
+ s += ds
547
+
548
+ l = b.exp
549
+ r = l.log.exp
550
+ ds = r.dis(l)
551
+ puts "exp and log: ds = " + ds.to_s if verbose
552
+ s += ds
553
+
554
+ l = c.sin
555
+ r = l.asin.sin
556
+ ds = r.dis(l)
557
+ puts "sin and asin: ds = " + ds.to_s if verbose
558
+ s += ds
559
+
560
+ l = b.cos
561
+ r = l.acos.cos
562
+ ds = r.dis(l)
563
+ puts "cos and acos: ds = " + ds.to_s if verbose
564
+ s += ds
565
+
566
+ l = a.tan
567
+ r = l.atan.tan
568
+ ds = r.dis(l)
569
+ puts "tan and atan: ds = " + ds.to_s if verbose
570
+ s += ds
571
+
572
+ l = a.cot
573
+ r = l.acot.cot
574
+ ds = r.dis(l)
575
+ puts "cot and acot: ds = " + ds.to_s if verbose
576
+ s += ds
577
+
578
+ l = c.sinh
579
+ r = l.asinh.sinh
580
+ ds = r.dis(l)
581
+ puts "sinh and asinh: ds = " + ds.to_s if verbose
582
+ s += ds
583
+
584
+ l = a.cosh
585
+ r = l.acosh.cosh
586
+ ds = r.dis(l)
587
+ puts "cosh and acosh: ds = " + ds.to_s if verbose
588
+ s += ds
589
+
590
+ l = b.tanh
591
+ r = l.atanh.tanh
592
+ ds = r.dis(l)
593
+ puts "tanh and atanh: ds = " + ds.to_s if verbose
594
+ s += ds
595
+
596
+ l = a.coth
597
+ r = l.acoth.coth
598
+ ds = r.dis(l)
599
+ puts "coth and acoth: ds = " + ds.to_s if verbose
600
+ s += ds
601
+
602
+ t2 = Time.now
603
+ puts "class of s is " + s.class.to_s + " ."
604
+ puts "The error sum s is " + s.to_s + " ."
605
+ puts "It should be close to 0."
606
+ puts "Computation time was #{t2-t1} seconds."
607
+ s
608
+ end
609
+
610
+ protected :coerce
611
+
612
+ end # class C
613
+
614
+ end # module AppMath
615
+