Class: AppMath::C

Inherits:
Numeric
  • Object
show all
Defined in:
lib/cnum.rb

Overview

Class of arbitrary precision complex numbers. The underlying real numbers may be Float or R.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*arg) ⇒ C

Returns a new instance of C.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/cnum.rb', line 41

def initialize(*arg)
  n = arg.size
  case n
  when 0
    @re = R.c0
    @im = R.c0
  when 1
    a0 = arg[0]
    if a0.integer? || a0.real?
      @re = R.c a0
      @im = R.c0
    elsif a0.complex? 
      @re = R.c a0.re
      @im = R.c a0.im
    else
      fail "can't construct a C from this argument"
    end
  when 2
    a0 = R.c arg[0]; a1 = R.c arg[1]
      @re = a0
      @im = a1
  else
     fail "can't construct a C from more than two arguments"
  end
end

Instance Attribute Details

#imObject (readonly)

Returns the value of attribute im.



39
40
41
# File 'lib/cnum.rb', line 39

def im
  @im
end

#reObject (readonly)

Returns the value of attribute re.



39
40
41
# File 'lib/cnum.rb', line 39

def re
  @re
end

Class Method Details

.iObject

The constant i



79
80
81
# File 'lib/cnum.rb', line 79

def C.i
  C.new(R.c0,R.c1)
end

.oneObject

The constant 1.



75
76
77
# File 'lib/cnum.rb', line 75

def C.one
  C.new(R.c1,R.c0)
end

.ran(anInteger) ⇒ Object

Random value (sine-floor random generator).

Chaotic function from the integers into the subset [0,1] x [0,1] of C



88
89
90
91
92
93
# File 'lib/cnum.rb', line 88

def C.ran(anInteger)
  ai = anInteger.to_i * 2
  x1 = R.ran(ai)
  x2 = R.ran(ai + 1)
  C.new(x1,x2)
end

.test(n0, verbose = false) ⇒ Object

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.


425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# File 'lib/cnum.rb', line 425

def C.test(n0, verbose = false )
  puts "Doing C.test(n = #{n0}, verbose = #{verbose})" +
    " for R.dig = #{R.dig}:"
  puts "*************************************************"
  t1 = Time.now
  small = true # otherwise not all inverse function tests work well 
  s = R.c0
  puts "class of s is " + s.class.to_s
  i = n0
  a = C.tob(i,small) 
  i += 1
  b = C.tob(i,small)
  i += 1
  c = C.tob(i,small)
  i += 1
  
  if verbose
    a.prn("a")
    b.prn("b")
    c.prn("c")
  end

  r = 2 + a
  l = a + 2
  ds = r.dis(l)
  puts "coerce 2 + a: ds = " + ds.to_s if verbose
  s += ds
  
  r =  a + 1.234
  l =  a + R.c(1.234)
  ds = r.dis(l)
  puts "coerce a + float: ds = " + ds.to_s if verbose
  s += ds

  r = (a + b) * c
  l = a * c + b * c

  ds = r.dis(l)
  puts "Distributive law for +: ds = " + ds.to_s if verbose
  s += ds
  
  r = (a - b) * c
  l = a * c - b * c
  ds = r.dis(l)
  puts "Distributive law for -: ds = " + ds.to_s if verbose
  s += ds
  
  r = (a * b) * c
  l = b * (c * a)
  ds = r.dis(l)
  puts "Multiplication: ds = " + ds.to_s if verbose
  s += ds

  r = (a * b) / c
  l = (a / c) * b
  ds = r.dis(l)
  puts "Division: ds = " + ds.to_s if verbose
  s += ds
  
  r = C.one
  l = a * a.inv
  ds = r.dis(l)
  puts "inv: ds = " + ds.to_s if verbose
  s += ds
 
  r = 1/a
  l = a.inv
  ds = r.dis(l)
  puts "inv and 1/x: ds = " + ds.to_s if verbose
  s += ds

  r = b
  l = -(-b)
  ds = r.dis(l)
  puts "Unary minus is idempotent: ds = " + ds.to_s if verbose
  s += ds
  x = -a
  y = x + a
  r = y
  l = C.zero
  ds = r.dis(l)
  puts "Unary -: ds = " + ds.to_s if verbose
  s += ds
  
  l = a
  x = a.sqrt
  r = x * x
  s = r.dis(l)
  puts "square root: ds = " + ds.to_s if verbose
  s += ds
  
  n = 11
  l = a ** n
  r = a ** C.new(n)
  ds = r.dis(l)
  puts "power with integer exponent: ds = " + ds.to_s if verbose
  s += ds
  
  n = -7
  l = a ** n
  r = a ** C.new(n)
  ds = r.dis(l)
  puts "power with negative integer exponent: ds = " + ds.to_s if verbose
  s += ds
  
  l = -C.one
  r = (C.i * R.pi).exp
  ds = r.dis(l)
  puts "Euler's relation: ds = " + ds.to_s if verbose
  s += ds

  l = a.sin * b.cos + a.cos * b.sin
  r = (a + b).sin
  ds = r.dis(l)
  puts "Addition theorem for sin: ds = " + ds.to_s if verbose
  s += ds
  
  l = a.exp * b.exp
  r = (a + b).exp
  ds = r.dis(l)
  puts "Addition theorem for exp: ds = " + ds.to_s if verbose
  s += ds

  l = b.exp
  r = l.log.exp
  ds = r.dis(l)
  puts "exp and log: ds = " + ds.to_s if verbose
  s += ds
  
  l = c.sin
  r = l.asin.sin
  ds = r.dis(l)
  puts "sin and asin: ds = " + ds.to_s if verbose
  s += ds
 
  l = b.cos
  r = l.acos.cos
  ds = r.dis(l)
  puts "cos and acos: ds = " + ds.to_s if verbose
  s += ds
  
  l = a.tan
  r = l.atan.tan
  ds = r.dis(l)
  puts "tan and atan: ds = " + ds.to_s if verbose
  s += ds

  l = a.cot
  r = l.acot.cot
  ds = r.dis(l)
  puts "cot and acot: ds = " + ds.to_s if verbose
  s += ds
  
  l = c.sinh
  r = l.asinh.sinh
  ds = r.dis(l)
  puts "sinh and asinh: ds = " + ds.to_s if verbose
  s += ds
  
  l = a.cosh
  r = l.acosh.cosh
  ds = r.dis(l)
  puts "cosh and acosh: ds = " + ds.to_s if verbose
  s += ds
  
  l = b.tanh
  r = l.atanh.tanh
  ds = r.dis(l)
  puts "tanh and atanh: ds = " + ds.to_s if verbose
  s += ds
 
  l = a.coth
  r = l.acoth.coth
  ds = r.dis(l)
  puts "coth and acoth: ds = " + ds.to_s if verbose
  s += ds
  
  t2 = Time.now
  puts "class of s is " + s.class.to_s + " ."
  puts "The error sum s is " + s.to_s + " ."
  puts "It should be close to 0."
  puts "Computation time was #{t2-t1} seconds."
  s
end

.tob(anInteger, small = false) ⇒ Object

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.



105
106
107
108
109
110
# File 'lib/cnum.rb', line 105

def C.tob(anInteger, small = false)
  ai = anInteger.to_i * 2
  x1 = R.tob(ai,small)
  x2 = R.tob(ai + 1,small)
  C.new(x1,x2)
end

.zeroObject

The constant 0.



70
71
72
# File 'lib/cnum.rb', line 70

def C.zero
  C.new
end

Instance Method Details

#*(a) ⇒ Object

Returns the C-object self * a.



205
206
207
208
209
210
211
212
213
214
# File 'lib/cnum.rb', line 205

def *(a)
  if a.integer? || a.real?
    b = R.c a
    C.new(@re * b, @im * b)
  elsif a.complex?
    C.new(@re * a.re - @im * a.im , @re * a.im + @im * a.re )
  else
    fail "cannot multiply a complex number with this argument"
  end
end

#**(a) ⇒ Object

Returns the a-th power of self. A may be integer, real, or complex. The result is always complex.



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/cnum.rb', line 241

def **(a)
  return C.nan if nan?
  if a.integer?
    if a.zero?
      C.one
    elsif a == 1
      self
    elsif a == -1
      inv
    else
      b = a.abs
      res = self
      for i in 1...b
        res *= self
      end
      if a < 0
        res = res.inv
      end
      res
    end
  elsif a.real?
    b = C.new(a)
    (log * b).exp
  elsif a.complex?
    (log * a).exp
  else
    fail "Argument not acceptable as an exponent"
  end
end

#+(a) ⇒ Object

Returns the C-object self + a.



180
181
182
183
184
185
186
187
188
189
# File 'lib/cnum.rb', line 180

def +(a)
  if a.integer? || a.real?
    b = R.c a
    C.new(@re + b, @im)
  elsif a.complex?
    C.new(@re + a.re, @im + a.im)
  else
    fail "cannot add this argument to a complex number"
  end
end

#+@Object

Unary plus operator. It returns the C-object self.



116
# File 'lib/cnum.rb', line 116

def +@; self; end

#-(a) ⇒ Object

Returns the C-object self - a.



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/cnum.rb', line 192

def -(a)
  if a.integer? || a.real?
    b = R.c a
    C.new(@re - b, @im)
  elsif a.complex?
    C.new(@re - a.re, @im - a.im)
  else
    fail "cannot subtract this argument from a complex number"
  end  
  
end

#-@Object

Unary minus operator. It returns the C-object -self.



113
# File 'lib/cnum.rb', line 113

def -@; C.new(-@re, -@im); end

#/(a) ⇒ Object

Returns the C-object self / a.



217
218
219
220
221
222
223
224
225
226
227
# File 'lib/cnum.rb', line 217

def /(a)
  if a.integer? || a.real?
    b = R.c a
    C.new(@re / b, @im / b)
  elsif a.complex?
    r2 = a.abs2
    C.new((@re * a.re + @im * a.im)/r2 , (@im * a.re - @re * a.im)/r2 )
  else
    fail "cannot divide a complex number by this argument"
  end
end

#<=>(a) ⇒ Object

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.



149
150
151
152
153
154
155
# File 'lib/cnum.rb', line 149

def <=> (a)
  cr = @re <=> a.re
  return cr unless cr.zero?
  ci = @im <=> a.im
  return ci unless ci.zero?
  return 0
end

#absObject

Returns the absolute value of self.



129
# File 'lib/cnum.rb', line 129

def abs; @re.hypot(@im); end

#abs2Object

Returns the absolute value squared of self.



132
# File 'lib/cnum.rb', line 132

def abs2; @re * @re + @im * @im; end

#acosObject

Inverse cosine.



394
395
396
# File 'lib/cnum.rb', line 394

def acos
  acosh.dbi
end

#acoshObject

Inverse hyperbolic cosine.



374
375
376
# File 'lib/cnum.rb', line 374

def acosh
  ((self * self - C.one).sqrt + self).log
end

#acotObject

Inverse cotangent.



404
405
406
# File 'lib/cnum.rb', line 404

def acot 
  ti.acoth.ti
end

#acothObject

Inverse hyperbolic cotangent.



384
385
386
# File 'lib/cnum.rb', line 384

def acoth
  ((self + C.one)/(self - C.one)).log * R.i2
end

#argObject

Returns the argument (i.e. the polar angle) of self.



135
# File 'lib/cnum.rb', line 135

def arg; @re.arg(@im); end

#asinObject

Inverse sine.



389
390
391
# File 'lib/cnum.rb', line 389

def asin
  ti.asinh.dbi
end

#asinhObject

Inverse hyperbolic sine.



369
370
371
# File 'lib/cnum.rb', line 369

def asinh
   ((self * self + C.one).sqrt + self).log
end

#atanObject

Inverse tangent.



399
400
401
# File 'lib/cnum.rb', line 399

def atan 
  ti.atanh.dbi
end

#atanhObject

Inverse hyperbolic tangent.



379
380
381
# File 'lib/cnum.rb', line 379

def atanh
  ((C.one + self)/(C.one - self)).log * R.i2
end

#cloneObject



67
# File 'lib/cnum.rb', line 67

def clone; C.new(@re,@im); end

#complex?Boolean

Supports the unified treatment of real and complex numbers.

Returns:

  • (Boolean)


177
# File 'lib/cnum.rb', line 177

def complex?; true; end

#conjObject

(Complex) conjugation, no effect on real numbers. Supports the unified treatment of real and complex numbers.



120
# File 'lib/cnum.rb', line 120

def conj; C.new(@re, -@im); end

#cosObject

Cosine.



334
335
336
# File 'lib/cnum.rb', line 334

def cos
  (expi + (-self).expi) * R.i2
end

#coshObject

Hyperbolic cosine.



352
# File 'lib/cnum.rb', line 352

def cosh; (exp + (-self).exp) * R.i2; end

#cotObject

Cotangent.



344
345
346
# File 'lib/cnum.rb', line 344

def cot
  cos / sin
end

#cothObject

Hyperbolic cotangent.



362
363
364
365
366
# File 'lib/cnum.rb', line 362

def coth
  s = exp - (-self).exp
  c = exp + (-self).exp
  c/s
end

#dbiObject

Returns self divided by i.



126
# File 'lib/cnum.rb', line 126

def dbi; self * C.new(0,-1); end

#dis(aC) ⇒ Object

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.



309
310
311
312
313
314
315
316
317
# File 'lib/cnum.rb', line 309

def dis(aC)
  a = abs
  b = aC.abs
  d = (self - aC).abs
  s = a + b
  return R.c0 if s.zero?
  d1 = d/s
  Basics.inf(d,d1)
end

#expObject

Exponential function.



230
# File 'lib/cnum.rb', line 230

def exp; C.new(@im.cos, @im.sin) * @re.exp; end

#expiObject

Exponential function of the argument multiplied by C.i



233
# File 'lib/cnum.rb', line 233

def expi; (self * C.i).exp; end

#infinite?Boolean

Returns ‘true’ if the real art or the iaginary part of self is infinite.

Returns:

  • (Boolean)


165
# File 'lib/cnum.rb', line 165

def infinite?; @re.infinite? || @im.infinite?; end

#integer?Boolean

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.

Returns:

  • (Boolean)


171
# File 'lib/cnum.rb', line 171

def integer?; false; end

#invObject

Returns the inverse 1/self.



278
279
280
# File 'lib/cnum.rb', line 278

def inv
   C.one / self
end

#logObject

Natural logarithm.



237
# File 'lib/cnum.rb', line 237

def log; C.new(abs.log,arg); end

#nan?Boolean

Returns ‘true’ if self is ‘not a number’ (NaN).

Returns:

  • (Boolean)


161
# File 'lib/cnum.rb', line 161

def nan?; @re.nan? || @im.nan?; end

#prn(name) ⇒ Object

Printing the value together with a label



323
324
325
# File 'lib/cnum.rb', line 323

def prn(name)
  puts "#{name} = " + to_s
end

#pseudo_invObject

The pseudo_inverse of zero is zero, and equal to the inverse for all other arguments.



287
288
289
# File 'lib/cnum.rb', line 287

def pseudo_inv
   zero? ? C.zero : C.one / self
end

#real?Boolean

Supports the unified treatment of real and complex numbers.

Returns:

  • (Boolean)


174
# File 'lib/cnum.rb', line 174

def real?; false; end

#round(n) ⇒ Object

For the return value res we have res.int? true and (self - res).abs <= 0.5



292
293
294
295
296
# File 'lib/cnum.rb', line 292

def round(n)
  u = @re.round(n)
  v = @im.round(n)
  C.new(u,v)
end

#sinObject

Sine.



329
330
331
# File 'lib/cnum.rb', line 329

def sin 
  (expi - (-self).expi) * C.new(0,-R.i2)
end

#sinhObject

Hyperbolic sine.



349
# File 'lib/cnum.rb', line 349

def sinh; (exp - (-self).exp) * R.i2; end

#sqrtObject

Returns the square root of self.



299
300
301
# File 'lib/cnum.rb', line 299

def sqrt
  self ** R.i2
end

#tanObject

Tangent.



339
340
341
# File 'lib/cnum.rb', line 339

def tan
  sin / cos
end

#tanhObject

Hyperbolic tangent.



355
356
357
358
359
# File 'lib/cnum.rb', line 355

def tanh
  s = exp - (-self).exp
  c = exp + (-self).exp
  s/c
end

#tiObject

Returns self times i.



123
# File 'lib/cnum.rb', line 123

def ti; self * C.i; end

#to_0Object

Returns the zero-element which belongs to the same class than self



272
# File 'lib/cnum.rb', line 272

def to_0; C.zero; end

#to_1Object

Returns the unit-element which belongs to the same class than self



275
# File 'lib/cnum.rb', line 275

def to_1; C.one; end

#to_sObject

Conversion to String.



320
# File 'lib/cnum.rb', line 320

def to_s; "C(#{@re}, #{@im})"; end

#zero?Boolean

Returns ‘true’ iff self == C(0,0)

Returns:

  • (Boolean)


158
# File 'lib/cnum.rb', line 158

def zero?; @re.zero? && @im.zero?; end