Class: HyperComplex
- Defined in:
- lib/hyper_complex.rb,
lib/version.rb
Overview
Class HyperComplex Uses Cayley-Dickson construction
-
A subclass of
Numeric
Constant Summary collapse
- VERSION =
'1.0.3'
Class Method Summary collapse
-
.[] ⇒ HyperComplex
Creates a HyperComplex object.
-
.e(num) ⇒ HyperComplex or Integer
Creates HyperComplex basis element (identity or imaginary).
-
.hrect(*args) ⇒ HyperComplex
Creates a HyperComplex object.
-
.hyperrectangular ⇒ HyperComplex
Creates a HyperComplex object.
-
.polar(radius, theta, vector) ⇒ HyperComplex
Creates a HyperComplex object.
-
.print_mt(dim) ⇒ Object
Prints the multiplication table to stdout.
-
.rect(arg_a, arg_b = 0) ⇒ HyperComplex
Creates a HyperComplex object arg_a + arg_b*e[].
-
.rectangular ⇒ HyperComplex
Creates a HyperComplex object arg_a + arg_b*e[].
-
.zero(num) ⇒ HyperComplex or Integer
Creates HyperComplex zero.
Instance Method Summary collapse
-
#*(other) ⇒ HyperComplex
Performs multiplication (a, b)*(c, d) = (a*c - d.conj*b, d*a + b*c.conj).
-
#**(other) ⇒ HyperComplex
Performs exponentiation.
-
#+(other) ⇒ HyperComplex
Performs addition.
-
#-(other) ⇒ HyperComplex
Performs subtraction.
-
#-@ ⇒ HyperComplex
Returns negation of the value.
-
#==(other) ⇒ true or false
Returns true if it equals to the other algebraically.
-
#[](index) ⇒ Integer or Rational or Float or nil
Returns the index’s element of hypercomplex number.
-
#abs ⇒ Integer or Rational or Float
(also: #magnitude)
Returns the absolute part of its polar form.
-
#abs2 ⇒ Integer or Rational or Float
Returns square of the absolute value.
-
#arg ⇒ Integer or Rational or Float
(also: #angle, #phase)
Returns the angle part of its polar form.
-
#axis ⇒ Vector
Returns the axis part of its polar form.
-
#coerce(other) ⇒ [HyperComplex, self]
Performs type conversion.
-
#conj ⇒ HyperComplex
(also: #conjugate)
Returns its conjugate.
-
#dim ⇒ Integer
(also: #dimension)
Returns the dimension of hypercomplex number.
-
#eql?(other) ⇒ true or false
Returns true if it have the same dimension and the same elements.
-
#fdiv(other) ⇒ HyperComplex
Performs float division.
-
#finite? ⇒ true or false
Returns true if its magnitude is finite, oterwise returns false.
-
#finv ⇒ HyperComplex
Float Inverse element.
-
#hrect ⇒ [Integer or Rational or Float, ...]
(also: #hyperrectangular, #to_a)
Returns an array of real numbers.
-
#imag ⇒ Vector
(also: #imaginary, #vector)
Returns the imaginary part as a vector.
-
#infinite? ⇒ true or false
Returns true if its magnitude is infinite, oterwise returns false.
-
#inv ⇒ HyperComplex
(also: #inverse)
Inverse element.
-
#mul(other) ⇒ HyperComplex
Same as *, but ‘classic’ non-effective algorythm is used.
-
#nan? ⇒ true or false
Returns true if any component is NaN, otherwise returns false.
-
#polar ⇒ [Integer or Rational or Float, Integer or Rational or Float, Vector]
Returns an array; [abs, arg, axis].
-
#quo(other) ⇒ HyperComplex
(also: #/)
Performs rational as possible division.
-
#real ⇒ Integer or Rational or Float
(also: #scalar)
Returns the real part.
-
#real? ⇒ false
HyperComplex is not real number.
-
#rect ⇒ [Integer or Rational or Float, Integer or Rational or Float] or [HyperComplex, HyperComplex]
(also: #rectangular)
Returns an array of two numbers.
-
#to_c ⇒ Complex
Performs conversion to Complex.
-
#to_f ⇒ Float
Performs conversion to Float.
-
#to_i ⇒ Integer
Performs conversion to Integer.
-
#to_r ⇒ Rational
Performs conversion to Rational.
-
#to_s ⇒ String
(also: #inspect)
Performs conversion to String.
-
#zero? ⇒ true or false
Returns true if all components are zero, otherwise returns false.
Class Method Details
.[] ⇒ HyperComplex
Creates a HyperComplex object.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/hyper_complex.rb', line 90 def hrect(*args) begin raise ArgumentError, 'Not all components are real' unless args.map(&:real?).all? rescue NoMethodError raise ArgumentError, 'Not all components are numeric' end s = args.length if s < 2 if s.zero? args = [0, 0] else args.push 0 end s = 2 end s1 = 1 << (s.bit_length - 1) if s == s1 new(*args) else new(*args.concat(Array.new((s1 << 1) - s, 0))) end end |
.e(num) ⇒ HyperComplex or Integer
Creates HyperComplex basis element (identity or imaginary)
148 149 150 151 152 153 |
# File 'lib/hyper_complex.rb', line 148 def e(num) raise ArgumentError, 'Argument must be non-negative integer' if !num.is_a?(Integer) || num.negative? return 1 if num.zero? hrect(*Array.new(num, 0), 1) end |
.hrect(*args) ⇒ HyperComplex
Creates a HyperComplex object.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/hyper_complex.rb', line 67 def hrect(*args) begin raise ArgumentError, 'Not all components are real' unless args.map(&:real?).all? rescue NoMethodError raise ArgumentError, 'Not all components are numeric' end s = args.length if s < 2 if s.zero? args = [0, 0] else args.push 0 end s = 2 end s1 = 1 << (s.bit_length - 1) if s == s1 new(*args) else new(*args.concat(Array.new((s1 << 1) - s, 0))) end end |
.hyperrectangular ⇒ HyperComplex
Creates a HyperComplex object.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/hyper_complex.rb', line 89 def hrect(*args) begin raise ArgumentError, 'Not all components are real' unless args.map(&:real?).all? rescue NoMethodError raise ArgumentError, 'Not all components are numeric' end s = args.length if s < 2 if s.zero? args = [0, 0] else args.push 0 end s = 2 end s1 = 1 << (s.bit_length - 1) if s == s1 new(*args) else new(*args.concat(Array.new((s1 << 1) - s, 0))) end end |
.polar(radius, theta, vector) ⇒ HyperComplex
Creates a HyperComplex object.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/hyper_complex.rb', line 119 def polar(radius, theta, vector) vs1 = vector.size + 1 raise TypeError, 'Vector size must be at least 3' if vs1 < 4 raise TypeError, 'Vector size is not equal to 2**n - 1' if vs1 != 1 << (vs1.bit_length - 1) begin raise ArgumentError, 'Not all components are real' unless [radius, theta, *vector].map(&:real?).all? rescue NoMethodError raise ArgumentError, 'Not all components are numeric' end vector = Vector[*vector] unless vector.is_a?(Vector) norm = vector.norm theta *= norm r_cos = radius * Math.cos(theta) r_sin = radius * Math.sin(theta) r_sin /= norm unless norm.zero? hrect(r_cos, *(r_sin * vector)) end |
.print_mt(dim) ⇒ Object
Prints the multiplication table to stdout
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/hyper_complex.rb', line 191 def print_mt(dim) table = @@mt = calc_mt(dim) if dim > @@mt.length ss = dim sf = ss.to_s.length + 3 out_s = "#{' ' * sf}|" (0...ss).each { out_s << format("%#{sf}s", "e#{_1}") } puts out_s puts "#{'-' * sf}+#{'-' * ss * sf}" out_s = format("%#{sf}s|", 'e0') (0...ss).each { out_s << format("%#{sf}s", "e#{_1}") } puts out_s (1...ss).each do |i| out_s = format("%#{sf}s|%#{sf}s", "e#{i}", "e#{i}") (1...i).each do |j| t = table[i][j] out_s << format("%#{sf}s", (t.negative? ? '-' : ' ') + "e#{t.abs}") end out_s << format("%#{sf}s", '-e0') ((i + 1)...ss).each do |j| t = table[j][i] out_s << format("%#{sf}s", (t.negative? ? ' ' : '-') + "e#{t.abs}") end puts out_s end end |
.rect(arg_a, arg_b = 0) ⇒ HyperComplex
Creates a HyperComplex object arg_a + arg_b*e[]
41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/hyper_complex.rb', line 41 def rect(arg_a, arg_b = 0) raise ArgumentError, 'Not all arguments are Numeric' unless arg_a.is_a?(Numeric) && arg_b.is_a?(Numeric) a = arg_a.real? ? [arg_a] : arg_a.hrect b = arg_b.real? ? [arg_b] : arg_b.hrect as = a.length bs = b.length if as > bs b.concat(Array.new(as - bs, 0)) elsif as < bs a.concat(Array.new(bs - as, 0)) end new(*(a + b)) end |
.rectangular ⇒ HyperComplex
Creates a HyperComplex object arg_a + arg_b*e[]
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/hyper_complex.rb', line 55 def rect(arg_a, arg_b = 0) raise ArgumentError, 'Not all arguments are Numeric' unless arg_a.is_a?(Numeric) && arg_b.is_a?(Numeric) a = arg_a.real? ? [arg_a] : arg_a.hrect b = arg_b.real? ? [arg_b] : arg_b.hrect as = a.length bs = b.length if as > bs b.concat(Array.new(as - bs, 0)) elsif as < bs a.concat(Array.new(bs - as, 0)) end new(*(a + b)) end |
.zero(num) ⇒ HyperComplex or Integer
Creates HyperComplex zero
165 166 167 168 169 170 |
# File 'lib/hyper_complex.rb', line 165 def zero(num) raise ArgumentError, 'Argument must positive integer' unless num.is_a?(Integer) && num.positive? return 0 if num == 1 hrect(*Array.new(num, 0)) end |
Instance Method Details
#*(other) ⇒ HyperComplex
Performs multiplication (a, b)*(c, d) = (a*c - d.conj*b, d*a + b*c.conj).
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 |
# File 'lib/hyper_complex.rb', line 545 def *(other) # rubocop:disable Metrics/AbcSize sar, oar = homogenize(other) ss = sar.length begin res = Array.new(ss, 0) res[0] = sar[0] * oar[0] (1...ss).each do |i| res[0] -= sar[i] * oar[i] res[i] += sar[0] * oar[i] + sar[i] * oar[0] ((i + 1)...ss).each do |j| ind = @@mt[j][i] if ind.positive? res[ind] -= sar[i] * oar[j] - sar[j] * oar[i] else res[-ind] += sar[i] * oar[j] - sar[j] * oar[i] end end end rescue NoMethodError, TypeError @@mt = HyperComplex.send(:calc_mt, ss) retry end __new__(*res) end |
#**(other) ⇒ HyperComplex
Performs exponentiation.
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 |
# File 'lib/hyper_complex.rb', line 774 def **(other) # rubocop:disable Metrics/AbcSize raise ArgumentError, 'Argument must be Numeric' unless other.is_a?(Numeric) if other.zero? return __new__(*Array.new(dim, Float::NAN)) if zero? return __new__(*Array.new(dim - 1, 0).unshift(1)) end unless other.real? begin other.to_f rescue # rubocop:disable Lint/SuppressedException,Style/RescueStandardError else other = other.real end end other = other.numerator if other.is_a?(Rational) && other.denominator == 1 if other.integer? x = other >= 0 ? self : 1.to_r / self n = other.abs z = 1 loop do z *= x if n.odd? n >>= 1 return z if n.zero? x *= x end elsif other.real? r, theta, vector = polar HyperComplex.polar(r**other, theta * other, vector) elsif other.is_a?(HyperComplex) r, theta, vector = polar q = HyperComplex.hrect(Math.log(r), *(theta * vector)) q *= other HyperComplex.polar(Math.exp(q.real), 1, q.imag) else num2, num1 = coerce(other) num1**num2 end end |
#+(other) ⇒ HyperComplex
Performs addition.
517 518 519 520 |
# File 'lib/hyper_complex.rb', line 517 def +(other) sar, oar = homogenize(other) __new__(*sar.zip(oar).map(&:sum)) end |
#-(other) ⇒ HyperComplex
Performs subtraction.
531 532 533 534 |
# File 'lib/hyper_complex.rb', line 531 def -(other) sar, oar = homogenize(other) __new__(*sar.zip(oar).map { |x| x.reduce(:-) }) end |
#-@ ⇒ HyperComplex
Returns negation of the value.
402 |
# File 'lib/hyper_complex.rb', line 402 def -@ = __new__(*@arv.map(&:-@)) |
#==(other) ⇒ true or false
Returns true if it equals to the other algebraically.
456 457 458 459 |
# File 'lib/hyper_complex.rb', line 456 def ==(other) sar, oar = homogenize(other) sar == oar end |
#[](index) ⇒ Integer or Rational or Float or nil
Returns the index’s element of hypercomplex number.
761 |
# File 'lib/hyper_complex.rb', line 761 def [](index) = @arv[index] |
#abs ⇒ Integer or Rational or Float Also known as: magnitude
Returns the absolute part of its polar form.
433 |
# File 'lib/hyper_complex.rb', line 433 def abs = Math.sqrt(abs2) |
#abs2 ⇒ Integer or Rational or Float
Returns square of the absolute value.
423 |
# File 'lib/hyper_complex.rb', line 423 def abs2 = @arv.sum { _1**2 } |
#arg ⇒ Integer or Rational or Float Also known as: angle, phase
Returns the angle part of its polar form.
338 |
# File 'lib/hyper_complex.rb', line 338 def arg = Math.atan2(imag.norm, real) |
#axis ⇒ Vector
Returns the axis part of its polar form.
351 352 353 354 355 |
# File 'lib/hyper_complex.rb', line 351 def axis v = imag norm = v.norm norm.zero? ? Vector[1, *Array.new(@arv.length - 2, 0)] : v / norm end |
#coerce(other) ⇒ [HyperComplex, self]
Performs type conversion.
668 |
# File 'lib/hyper_complex.rb', line 668 def coerce(other) = [__new__(*other.hrect), self] |
#conj ⇒ HyperComplex Also known as: conjugate
Returns its conjugate.
412 |
# File 'lib/hyper_complex.rb', line 412 def conj = __new__ @arv[0], *@arv[1..].map(&:-@) |
#dim ⇒ Integer Also known as: dimension
Returns the dimension of hypercomplex number.
749 |
# File 'lib/hyper_complex.rb', line 749 def dim = @arv.length |
#eql?(other) ⇒ true or false
Returns true if it have the same dimension and the same elements.
471 |
# File 'lib/hyper_complex.rb', line 471 def eql?(other) = @arv.eql?(other.hrect) |
#fdiv(other) ⇒ HyperComplex
Performs float division.
651 652 653 654 |
# File 'lib/hyper_complex.rb', line 651 def fdiv(other) t = other.abs2 self * __new__(*other.conj.hrect.map { _1.to_f / t }) end |
#finite? ⇒ true or false
Returns true if its magnitude is finite, oterwise returns false.
488 |
# File 'lib/hyper_complex.rb', line 488 def finite? = @arv.map(&:finite?).all? |
#finv ⇒ HyperComplex
Float Inverse element.
615 616 617 618 |
# File 'lib/hyper_complex.rb', line 615 def finv t = abs2 __new__(*conj.hrect.map { _1.to_f / t }) end |
#hrect ⇒ [Integer or Rational or Float, ...] Also known as: hyperrectangular, to_a
Returns an array of real numbers.
303 |
# File 'lib/hyper_complex.rb', line 303 def hrect = @arv |
#imag ⇒ Vector Also known as: imaginary, vector
Returns the imaginary part as a vector.
326 |
# File 'lib/hyper_complex.rb', line 326 def imag = Vector[*hrect.drop(1)] |
#infinite? ⇒ true or false
Returns true if its magnitude is infinite, oterwise returns false.
501 |
# File 'lib/hyper_complex.rb', line 501 def infinite? = @arv.map(&:infinite?).any? |
#inv ⇒ HyperComplex Also known as: inverse
Inverse element. Rational arithmetic is used as possible.
600 601 602 603 |
# File 'lib/hyper_complex.rb', line 600 def inv t = abs2 __new__(*conj.hrect.map { _1.to_r / t }) end |
#mul(other) ⇒ HyperComplex
Same as *, but ‘classic’ non-effective algorythm is used.
579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/hyper_complex.rb', line 579 def mul(other) sar, oar = homogenize(other) ss = sar.length return __new__(sar[0] * oar[0] - oar[1] * sar[1], oar[1] * sar[0] + sar[1] * oar[0]) if ss == 2 a = __new__(*sar[0...(ss / 2)]) b = __new__(*sar[(ss / 2)..]) c = __new__(*oar[0...(ss / 2)]) d = __new__(*oar[(ss / 2)..]) __new__(*(a.mul(c) - d.conj.mul(b)).hrect, *(d.mul(a) + b.mul(c.conj)).hrect) end |
#nan? ⇒ true or false
Returns true if any component is NaN, otherwise returns false
385 |
# File 'lib/hyper_complex.rb', line 385 def nan? = @arv.map { |i| i.equal?(Float::NAN) }.any? |
#polar ⇒ [Integer or Rational or Float, Integer or Rational or Float, Vector]
Returns an array; [abs, arg, axis].
445 |
# File 'lib/hyper_complex.rb', line 445 def polar = [abs, arg, axis] |
#quo(other) ⇒ HyperComplex Also known as: /
Performs rational as possible division.
633 634 635 636 |
# File 'lib/hyper_complex.rb', line 633 def quo(other) t = other.abs2 self * __new__(*other.conj.hrect.map { _1.to_r / t }) end |
#real ⇒ Integer or Rational or Float Also known as: scalar
Returns the real part.
315 |
# File 'lib/hyper_complex.rb', line 315 def real = @arv[0] |
#real? ⇒ false
HyperComplex is not real number.
365 |
# File 'lib/hyper_complex.rb', line 365 def real? = false |
#rect ⇒ [Integer or Rational or Float, Integer or Rational or Float] or [HyperComplex, HyperComplex] Also known as: rectangular
Returns an array of two numbers.
287 288 289 290 291 292 |
# File 'lib/hyper_complex.rb', line 287 def rect ssh = @arv.length >> 1 return @arv if ssh == 1 [__new__(*@arv[0...(ssh)]), __new__(*@arv[ssh..])] end |
#to_c ⇒ Complex
Performs conversion to Complex.
724 725 726 727 728 |
# File 'lib/hyper_complex.rb', line 724 def to_c return Complex(@arv[0], @arv[1]) if @arv.length <= 2 || @arv[2..].map(&:zero?).all? raise RangeError, "Can not convert #{inspect} to Complex" end |
#to_f ⇒ Float
Performs conversion to Float.
694 695 696 697 698 |
# File 'lib/hyper_complex.rb', line 694 def to_f raise RangeError, "Can not convert #{inspect} to Float" unless @arv[1..].map(&:zero?).all? @arv[0].to_f end |
#to_i ⇒ Integer
Performs conversion to Integer.
679 680 681 682 683 |
# File 'lib/hyper_complex.rb', line 679 def to_i raise RangeError, "Can not convert #{inspect} to Integer" unless @arv[1..].map(&:zero?).all? @arv[0].to_i end |
#to_r ⇒ Rational
Performs conversion to Rational.
709 710 711 712 713 |
# File 'lib/hyper_complex.rb', line 709 def to_r raise RangeError, "Can not convert #{inspect} to Rational" unless @arv[1..].map(&:zero?).all? @arv[0].to_r end |
#to_s ⇒ String Also known as: inspect
Performs conversion to String.
738 |
# File 'lib/hyper_complex.rb', line 738 def to_s = "HyperComplex#{@arv}" |
#zero? ⇒ true or false
Returns true if all components are zero, otherwise returns false
375 |
# File 'lib/hyper_complex.rb', line 375 def zero? = @arv.map(&:zero?).all? |