| Class | AppMath::C |
| In: |
cnum.rb
|
| Parent: | Numeric |
Class of complex numbers
| im | [R] | |
| re | [R] |
# File cnum.rb, line 23
23: def initialize(*arg)
24: n = arg.size
25: case n
26: when 0
27: @re = R.c0
28: @im = R.c0
29: when 1
30: a0 = arg[0]
31: if a0.integer? || a0.real?
32: @re = R.c a0
33: @im = R.c0
34: elsif a0.complex?
35: @re = R.c a0.re
36: @im = R.c a0.im
37: else
38: fail "can't construct a C from this argument"
39: end
40: when 2
41: a0 = R.c arg[0]; a1 = R.c arg[1]
42: @re = a0
43: @im = a1
44: else
45: fail "can't construct a C from more than two arguments"
46: end
47: end
Consistency test for class C This is intended to keep the class consistent despite of modifications. The first argument influences the numbers which are selected for the test. Returned is a sum of numbers each of which should be numerical noise and so the result has to be << 1 if the test is to indicate success. For instance, on my system
Doing C.test(n = 137, verbose = false) for R.dig = 100: ************************************************* class of s is AppMath::R class of s is AppMath::R . The error sum s is 0.95701879151814897746312007872622225589589551941 73186692168823486932509515793972625242699350133964052E-98 . It should be close to 0. Computation time was 1.062 seconds.
# File cnum.rb, line 407
407: def C.test(n0, verbose = false )
408: puts "Doing C.test(n = #{n0}, verbose = #{verbose})" +
409: " for R.dig = #{R.dig}:"
410: puts "*************************************************"
411: t1 = Time.now
412: small = true # otherwise not all inverse function tests work well
413: s = R.c0
414: puts "class of s is " + s.class.to_s
415: i = n0
416: a = C.tob(i,small)
417: i += 1
418: b = C.tob(i,small)
419: i += 1
420: c = C.tob(i,small)
421: i += 1
422:
423: if verbose
424: a.prn("a")
425: b.prn("b")
426: c.prn("c")
427: end
428:
429: r = 2 + a
430: l = a + 2
431: ds = r.dis(l)
432: puts "coerce 2 + a: ds = " + ds.to_s if verbose
433: s += ds
434:
435: r = a + 1.234
436: l = a + R.c(1.234)
437: ds = r.dis(l)
438: puts "coerce a + float: ds = " + ds.to_s if verbose
439: s += ds
440:
441: r = (a + b) * c
442: l = a * c + b * c
443:
444: ds = r.dis(l)
445: puts "Distributive law for +: ds = " + ds.to_s if verbose
446: s += ds
447:
448: r = (a - b) * c
449: l = a * c - b * c
450: ds = r.dis(l)
451: puts "Distributive law for -: ds = " + ds.to_s if verbose
452: s += ds
453:
454: r = (a * b) * c
455: l = b * (c * a)
456: ds = r.dis(l)
457: puts "Multiplication: ds = " + ds.to_s if verbose
458: s += ds
459:
460: r = (a * b) / c
461: l = (a / c) * b
462: ds = r.dis(l)
463: puts "Division: ds = " + ds.to_s if verbose
464: s += ds
465:
466: r = C.one
467: l = a * a.inv
468: ds = r.dis(l)
469: puts "inv: ds = " + ds.to_s if verbose
470: s += ds
471:
472: r = 1/a
473: l = a.inv
474: ds = r.dis(l)
475: puts "inv and 1/x: ds = " + ds.to_s if verbose
476: s += ds
477:
478: r = b
479: l = -(-b)
480: ds = r.dis(l)
481: puts "Unary minus is idempotent: ds = " + ds.to_s if verbose
482: s += ds
483: x = -a
484: y = x + a
485: r = y
486: l = C.zero
487: ds = r.dis(l)
488: puts "Unary -: ds = " + ds.to_s if verbose
489: s += ds
490:
491: l = a
492: x = a.sqrt
493: r = x * x
494: s = r.dis(l)
495: puts "square root: ds = " + ds.to_s if verbose
496: s += ds
497:
498: n = 11
499: l = a ** n
500: r = a ** C.new(n)
501: ds = r.dis(l)
502: puts "power with integer exponent: ds = " + ds.to_s if verbose
503: s += ds
504:
505: n = -7
506: l = a ** n
507: r = a ** C.new(n)
508: ds = r.dis(l)
509: puts "power with negative integer exponent: ds = " + ds.to_s if verbose
510: s += ds
511:
512: l = -C.one
513: r = (C.i * R.pi).exp
514: ds = r.dis(l)
515: puts "Euler's relation: ds = " + ds.to_s if verbose
516: s += ds
517:
518: l = a.sin * b.cos + a.cos * b.sin
519: r = (a + b).sin
520: ds = r.dis(l)
521: puts "Addition theorem for sin: ds = " + ds.to_s if verbose
522: s += ds
523:
524: l = a.exp * b.exp
525: r = (a + b).exp
526: ds = r.dis(l)
527: puts "Addition theorem for exp: ds = " + ds.to_s if verbose
528: s += ds
529:
530: l = b.exp
531: r = l.log.exp
532: ds = r.dis(l)
533: puts "exp and log: ds = " + ds.to_s if verbose
534: s += ds
535:
536: l = c.sin
537: r = l.asin.sin
538: ds = r.dis(l)
539: puts "sin and asin: ds = " + ds.to_s if verbose
540: s += ds
541:
542: l = b.cos
543: r = l.acos.cos
544: ds = r.dis(l)
545: puts "cos and acos: ds = " + ds.to_s if verbose
546: s += ds
547:
548: l = a.tan
549: r = l.atan.tan
550: ds = r.dis(l)
551: puts "tan and atan: ds = " + ds.to_s if verbose
552: s += ds
553:
554: l = a.cot
555: r = l.acot.cot
556: ds = r.dis(l)
557: puts "cot and acot: ds = " + ds.to_s if verbose
558: s += ds
559:
560: l = c.sinh
561: r = l.asinh.sinh
562: ds = r.dis(l)
563: puts "sinh and asinh: ds = " + ds.to_s if verbose
564: s += ds
565:
566: l = a.cosh
567: r = l.acosh.cosh
568: ds = r.dis(l)
569: puts "cosh and acosh: ds = " + ds.to_s if verbose
570: s += ds
571:
572: l = b.tanh
573: r = l.atanh.tanh
574: ds = r.dis(l)
575: puts "tanh and atanh: ds = " + ds.to_s if verbose
576: s += ds
577:
578: l = a.coth
579: r = l.acoth.coth
580: ds = r.dis(l)
581: puts "coth and acoth: ds = " + ds.to_s if verbose
582: s += ds
583:
584: t2 = Time.now
585: puts "class of s is " + s.class.to_s + " ."
586: puts "The error sum s is " + s.to_s + " ."
587: puts "It should be close to 0."
588: puts "Computation time was #{t2-t1} seconds."
589: s
590: end
Test object.
Needed for automatic tests of arithmetic relations. Intended to give numbers which rapidly change sign and order of magnitude when the argument grows regularly e.g. as in 1,2,3,… . However, suitibility as a random generator is not the focus. If the second argument is ‘true’, the result is multplied by a number << 1 in order to prevent the result from overloading the exponential function.
# File cnum.rb, line 87
87: def C.tob(anInteger, small = false)
88: ai = anInteger.to_i * 2
89: x1 = R.tob(ai,small)
90: x2 = R.tob(ai + 1,small)
91: C.new(x1,x2)
92: end
Returns the C-object self * a.
# File cnum.rb, line 187
187: def *(a)
188: if a.integer? || a.real?
189: b = R.c a
190: C.new(@re * b, @im * b)
191: elsif a.complex?
192: C.new(@re * a.re - @im * a.im , @re * a.im + @im * a.re )
193: else
194: fail "cannot multiply a complex number with this argument"
195: end
196: end
Returns the a-th power of self. A may be integer, real, or complex. The result is always complex.
# File cnum.rb, line 223
223: def **(a)
224: return C.nan if nan?
225: if a.integer?
226: if a.zero?
227: C.one
228: elsif a == 1
229: self
230: elsif a == -1
231: inv
232: else
233: b = a.abs
234: res = self
235: for i in 1...b
236: res *= self
237: end
238: if a < 0
239: res = res.inv
240: end
241: res
242: end
243: elsif a.real?
244: b = C.new(a)
245: (log * b).exp
246: elsif a.complex?
247: (log * a).exp
248: else
249: fail "Argument not acceptable as an exponent"
250: end
251: end
Returns the C-object self - a.
# File cnum.rb, line 174
174: def -(a)
175: if a.integer? || a.real?
176: b = R.c a
177: C.new(@re - b, @im)
178: elsif a.complex?
179: C.new(@re - a.re, @im - a.im)
180: else
181: fail "cannot subtract this argument from a complex number"
182: end
183:
184: end
Returns the C-object self / a.
# File cnum.rb, line 199
199: def /(a)
200: if a.integer? || a.real?
201: b = R.c a
202: C.new(@re / b, @im / b)
203: elsif a.complex?
204: r2 = a.abs2
205: C.new((@re * a.re + @im * a.im)/r2 , (@im * a.re - @re * a.im)/r2 )
206: else
207: fail "cannot divide a complex number by this argument"
208: end
209: end
The order relation is here lexicographic ordering based on the agreement that re is the ‘first letter’ and im the ‘second letter’ of ‘the word’. Needed only for book-keeping purposes.
# File cnum.rb, line 131
131: def <=> (a)
132: cr = @re <=> a.re
133: return cr unless cr.zero?
134: ci = @im <=> a.im
135: return ci unless ci.zero?
136: return 0
137: end
Returns the absolute value squared of self.
# File cnum.rb, line 114
114: def abs2; @re * @re + @im * @im; end
Inverse hyperbolic cosine.
# File cnum.rb, line 356
356: def acosh
357: ((self * self - C.one).sqrt + self).log
358: end
Inverse hyperbolic cotangent.
# File cnum.rb, line 366
366: def acoth
367: ((self + C.one)/(self - C.one)).log * R.i2
368: end
Returns the argument (i.e. the polar angle) of self.
# File cnum.rb, line 117
117: def arg; @re.arg(@im); end
Inverse hyperbolic sine.
# File cnum.rb, line 351
351: def asinh
352: ((self * self + C.one).sqrt + self).log
353: end
Inverse hyperbolic tangent.
# File cnum.rb, line 361
361: def atanh
362: ((C.one + self)/(C.one - self)).log * R.i2
363: end
Supports the unified treatment of real and complex numbers.
# File cnum.rb, line 159
159: def complex?; true; end
(Complex) conjugation, no effect on real numbers. Supports the unified treatment of real and complex numbers.
# File cnum.rb, line 102
102: def conj; C.new(@re, -@im); end
Hyperbolic cotangent.
# File cnum.rb, line 344
344: def coth
345: s = exp - (-self).exp
346: c = exp + (-self).exp
347: c/s
348: end
Returns a kind of relative distance between self and aR. The return value varies from 0 to 1, where 1 means maximum dissimilarity of the arguments. Such a function is needed for testing the validity of arithmetic laws, which, due to numerical noise, should not be expected to be fulfilled exactly.
# File cnum.rb, line 291
291: def dis(aC)
292: a = abs
293: b = aC.abs
294: d = (self - aC).abs
295: s = a + b
296: return R.c0 if s.zero?
297: d1 = d/s
298: Basics.inf(d,d1)
299: end
Returns ‘true’ if the real art or the iaginary part of self is infinite.
# File cnum.rb, line 147
147: def infinite?; @re.infinite? || @im.infinite?; end
Since R is not Fixnum or Bignum we return ‘false’. In scientific computation there may be the need to use various types of ‘real number types’ but there should always a clear-cut distinction between integer types and real types.
# File cnum.rb, line 153
153: def integer?; false; end
Returns ‘true’ if self is ‘not a number’ (NaN).
# File cnum.rb, line 143
143: def nan?; @re.nan? || @im.nan?; end
Printing the value together with a label
# File cnum.rb, line 305
305: def prn(name)
306: puts "#{name} = " + to_s
307: end
Supports the unified treatment of real and complex numbers.
# File cnum.rb, line 156
156: def real?; false; end
Hyperbolic tangent.
# File cnum.rb, line 337
337: def tanh
338: s = exp - (-self).exp
339: c = exp + (-self).exp
340: s/c
341: end
Returns the unit-element which belongs to the same class than self
# File cnum.rb, line 257
257: def to_1; C.one; end
Redefining coerce from Numeric. This allows writing 1 + C.new(137) instead of C.new(137) + 1 or C.new(137) + R.c1.
# File cnum.rb, line 124
124: def coerce(a)
125: [ C.new(a), self]
126: end