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