quaternion_c2 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +62 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/quaternion_c2.rb +10 -0
- data/lib/quaternion_c2/arithmetic.rb +75 -0
- data/lib/quaternion_c2/attributes.rb +25 -0
- data/lib/quaternion_c2/base.rb +18 -0
- data/lib/quaternion_c2/classification.rb +32 -0
- data/lib/quaternion_c2/conversion.rb +166 -0
- data/lib/quaternion_c2/equality.rb +30 -0
- data/lib/quaternion_c2/to_type.rb +108 -0
- data/lib/quaternion_c2/unary.rb +43 -0
- data/lib/quaternion_c2/units.rb +40 -0
- data/lib/quaternion_c2/utils.rb +71 -0
- data/quaternion_c2.gemspec +25 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 11a23c8bee71f2eac9cdd28569ddbd060e1c78d6
|
4
|
+
data.tar.gz: 2da47444cd5b3c7f50da7f9cb0dfc281dcd187a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3504d452e7841683a890fb01c8ca3a82ce44c9c334aa3309b50329dd17415a99d021c6d2b56cf0358a5c04d9d09b1aaf44508d030cc75072a32626d4c1a8d1eb
|
7
|
+
data.tar.gz: 6348e89acb1f9a31923c03d9ea1fac21d10d2a849d55c198fd757021ad35243dce10c411502f4ea4b6e622d331e4e66dfd4b014e46c2774d290320a77507265b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Masahiro Nomoto
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Quaternion
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'quaternion_c2'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
`Quaternion` is similar to a built-in class `Complex`.
|
18
|
+
|
19
|
+
There are several methods to create a quaternion. `Kernel.#Quaternion` accepts many patterns of arguments.
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'quaternion_c2'
|
23
|
+
|
24
|
+
q1 = Quaternion.rect(Complex(1, 2), Complex(3, 4)) #=> (1+2i+3j+4k)
|
25
|
+
q2 = Quaternion.hrect(1, -2, Rational(-3, 4), 0.56) #=> (1-2i-(3/4)*j+0.56k)
|
26
|
+
vector = Vector[1, 1, 1].normalize
|
27
|
+
q3 = Quaternion.polar(1, Math::PI / 3, vector) #=> (0.5+0.5i+0.5j+0.5k)
|
28
|
+
q4 = Quaternion(1, [2, 3, 4]) #=> (1+2i+3j+4k)
|
29
|
+
|
30
|
+
q1.rect #=> [(1+2i), (3+4i)]
|
31
|
+
q2.hrect #=> [1, -2, (-3/4), 0.56]
|
32
|
+
q3.polar #=> [1.0, Math::PI / 3, vector]
|
33
|
+
q4.real #=> 1
|
34
|
+
q4.imag #=> Vector[2, 3, 4]
|
35
|
+
|
36
|
+
Complex::I.to_q #=> (0+1i+0j+0k)
|
37
|
+
1 + 2.i + 3.j + 4.k #=> (1+2i+3j+4k)
|
38
|
+
```
|
39
|
+
|
40
|
+
`Quaternion` supports standard calculations between numeric instances.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Complex::I * Quaternion::J #=> (0+0i+0j+1k)
|
44
|
+
1 / Quaternion::I #=> ((0/1)-(1/1)*i+(0/0)*j+(0/0)*k)
|
45
|
+
Quaternion(1, 1, 1, 1) ** 6 #=> (64+0i+0j+0k)
|
46
|
+
```
|
47
|
+
|
48
|
+
## Development
|
49
|
+
|
50
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
51
|
+
|
52
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/hmmnrst/quaternion_c2.
|
57
|
+
|
58
|
+
|
59
|
+
## License
|
60
|
+
|
61
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
62
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "quaternion_c2"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require "quaternion_c2/base"
|
2
|
+
require "quaternion_c2/classification"
|
3
|
+
require "quaternion_c2/unary"
|
4
|
+
require "quaternion_c2/units"
|
5
|
+
require "quaternion_c2/equality"
|
6
|
+
require "quaternion_c2/attributes"
|
7
|
+
require "quaternion_c2/arithmetic"
|
8
|
+
require "quaternion_c2/to_type"
|
9
|
+
require "quaternion_c2/conversion"
|
10
|
+
require "quaternion_c2/utils"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'classification'
|
3
|
+
require_relative 'unary'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Basic arithmetic operators (#+, #-, #*, #/)
|
7
|
+
# and related methods
|
8
|
+
#
|
9
|
+
|
10
|
+
class Quaternion
|
11
|
+
def +(other)
|
12
|
+
if other.kind_of?(Quaternion)
|
13
|
+
__new__(@a + other.a, @b + other.b)
|
14
|
+
elsif other.kind_of?(Numeric) && other.complex?
|
15
|
+
__new__(@a + other, @b)
|
16
|
+
else
|
17
|
+
n1, n2 = other.coerce(self)
|
18
|
+
n1 + n2
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def -(other)
|
23
|
+
if other.kind_of?(Quaternion)
|
24
|
+
__new__(@a - other.a, @b - other.b)
|
25
|
+
elsif other.kind_of?(Numeric) && other.complex?
|
26
|
+
__new__(@a - other, @b)
|
27
|
+
else
|
28
|
+
n1, n2 = other.coerce(self)
|
29
|
+
n1 - n2
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def *(other)
|
34
|
+
if other.kind_of?(Quaternion)
|
35
|
+
_a = other.a
|
36
|
+
_b = other.b
|
37
|
+
__new__(@a * _a - _b.conj * @b, _b * @a + @b * _a.conj)
|
38
|
+
elsif other.kind_of?(Numeric) && other.complex?
|
39
|
+
__new__(@a * other, @b * other.conj)
|
40
|
+
else
|
41
|
+
n1, n2 = other.coerce(self)
|
42
|
+
n1 * n2
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
[:quo, :fdiv].each do |sym|
|
47
|
+
define_method(sym) do |other|
|
48
|
+
if other.kind_of?(Quaternion)
|
49
|
+
self * other.conj.send(sym, other.abs2)
|
50
|
+
elsif other.kind_of?(Numeric) && other.complex?
|
51
|
+
__new__(@a.send(sym, other), @b.send(sym, other.conj))
|
52
|
+
else
|
53
|
+
n1, n2 = other.coerce(self)
|
54
|
+
n1.send(sym, n2)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
alias / quo
|
59
|
+
undef div, %, modulo, remainder, divmod
|
60
|
+
|
61
|
+
#
|
62
|
+
# Conversion
|
63
|
+
#
|
64
|
+
|
65
|
+
def coerce(other)
|
66
|
+
if other.kind_of?(Quaternion)
|
67
|
+
[other, self]
|
68
|
+
elsif other.kind_of?(Numeric) && other.complex?
|
69
|
+
[__new__(other, 0), self]
|
70
|
+
else
|
71
|
+
raise TypeError,
|
72
|
+
"#{other.class} can't be coerced into #{self.class}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'unary'
|
2
|
+
require_relative 'equality'
|
3
|
+
|
4
|
+
class Quaternion
|
5
|
+
# defined by Numeric:
|
6
|
+
# * zero? #=> self == 0
|
7
|
+
# * nonzero? #=> zero? ? nil : self
|
8
|
+
|
9
|
+
defined_methods = public_instance_methods
|
10
|
+
|
11
|
+
if defined_methods.include?(:finite?)
|
12
|
+
def finite?
|
13
|
+
abs.finite?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined_methods.include?(:infinite?)
|
18
|
+
def infinite?
|
19
|
+
abs.infinite?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
undef positive? if defined_methods.include?(:positive?)
|
24
|
+
undef negative? if defined_methods.include?(:negative?)
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Number system classification
|
5
|
+
# (N <) Z < Q < R < C < H (< ...)
|
6
|
+
#
|
7
|
+
# * quaternion? : [H] Quaternion (*undefined*)
|
8
|
+
# * && complex? : [C] Complex
|
9
|
+
# * && real? : [R] Float, BigDecimal, Numeric
|
10
|
+
# * && rational? : [Q] Rational (*undefined*)
|
11
|
+
# * && integer? : [Z] Fixnum, Bignum, Integer
|
12
|
+
#
|
13
|
+
|
14
|
+
class Numeric
|
15
|
+
# defined:
|
16
|
+
# * integer? #=> false
|
17
|
+
# * real? #=> true
|
18
|
+
|
19
|
+
def complex?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Quaternion
|
25
|
+
def real?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def complex?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'classification'
|
3
|
+
require_relative 'unary'
|
4
|
+
require_relative 'units'
|
5
|
+
require_relative 'arithmetic'
|
6
|
+
require_relative 'to_type'
|
7
|
+
|
8
|
+
require 'matrix'
|
9
|
+
|
10
|
+
class Quaternion
|
11
|
+
#
|
12
|
+
# Constructors
|
13
|
+
#
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def rect(a, b = 0)
|
17
|
+
unless [a, b].all? { |c| c.kind_of?(Numeric) && c.complex? }
|
18
|
+
raise TypeError, 'not a complex'
|
19
|
+
end
|
20
|
+
new(a, b)
|
21
|
+
end
|
22
|
+
alias rectangular rect
|
23
|
+
|
24
|
+
def hrect(w, x = 0, y = 0, z = 0)
|
25
|
+
a = Complex.rect(w, x)
|
26
|
+
b = Complex.rect(y, z)
|
27
|
+
new(a, b)
|
28
|
+
end
|
29
|
+
alias hyperrectangular hrect
|
30
|
+
|
31
|
+
# Quaternion.polar(r) == r
|
32
|
+
# Quaternion.polar(r, theta) == Complex.polar(r, theta)
|
33
|
+
def polar(r, theta = 0, vector = Vector[1, 0, 0])
|
34
|
+
unless vector.kind_of?(Enumerable) && vector.size == 3
|
35
|
+
raise TypeError, 'not a 3-D vector'
|
36
|
+
end
|
37
|
+
unless [r, theta, *vector].all? { |a| a.kind_of?(Numeric) && a.real? }
|
38
|
+
raise TypeError, 'not a real'
|
39
|
+
end
|
40
|
+
|
41
|
+
vector = Vector[*vector] unless vector.kind_of?(Vector)
|
42
|
+
norm = vector.norm
|
43
|
+
theta *= norm
|
44
|
+
|
45
|
+
r_cos = r * Math.cos(theta)
|
46
|
+
r_sin = r * Math.sin(theta)
|
47
|
+
r_sin /= norm if norm > 0
|
48
|
+
hrect(r_cos, *(r_sin * vector))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Accessors
|
54
|
+
#
|
55
|
+
|
56
|
+
def rect
|
57
|
+
[@a, @b]
|
58
|
+
end
|
59
|
+
alias rectangular rect
|
60
|
+
|
61
|
+
def hrect
|
62
|
+
rect.flat_map(&:rect)
|
63
|
+
end
|
64
|
+
alias hyperrectangular hrect
|
65
|
+
|
66
|
+
def real
|
67
|
+
@a.real
|
68
|
+
end
|
69
|
+
alias scalar real
|
70
|
+
|
71
|
+
def imag
|
72
|
+
Vector[*hrect.drop(1)]
|
73
|
+
end
|
74
|
+
alias imaginary imag
|
75
|
+
alias vector imag
|
76
|
+
|
77
|
+
# defined in unary.rb:
|
78
|
+
# * abs
|
79
|
+
# * magnitude
|
80
|
+
|
81
|
+
def arg
|
82
|
+
r_cos = real
|
83
|
+
r_sin = imag.norm
|
84
|
+
Math.atan2(r_sin, r_cos)
|
85
|
+
end
|
86
|
+
alias angle arg
|
87
|
+
alias phase arg
|
88
|
+
|
89
|
+
def axis
|
90
|
+
v = imag
|
91
|
+
norm = v.norm
|
92
|
+
if norm.zero?
|
93
|
+
# imag[0] == +0.0 -> q = r exp(+I PI)
|
94
|
+
# imag[0] == 0/1 -> q = r exp(+I PI)
|
95
|
+
# imag[0] == -0.0 -> q = r exp(-I PI)
|
96
|
+
sign = (1.0 / imag[0] >= 0) ? 1 : -1
|
97
|
+
Vector[sign, 0, 0]
|
98
|
+
else
|
99
|
+
v / norm
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def polar
|
104
|
+
[abs, arg, axis]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Generic constructor
|
110
|
+
#
|
111
|
+
# This accepts various arguments:
|
112
|
+
# * (Numeric) -> real quaternion
|
113
|
+
# * (Numeric, Numeric) -> a+bj
|
114
|
+
# * (Numeric, Vector) -> scalar and 3-D vector
|
115
|
+
# * (Numeric, Numeric, Numeric[, Numeric]) -> w+xi+yj+zk
|
116
|
+
# and String instead of Numeric.
|
117
|
+
#
|
118
|
+
module Kernel
|
119
|
+
module_function
|
120
|
+
|
121
|
+
def Quaternion(*args)
|
122
|
+
argc = args.size
|
123
|
+
|
124
|
+
unless (1..4).cover?(argc)
|
125
|
+
raise ArgumentError,
|
126
|
+
"wrong number of arguments (given #{argc}, expected 1..4)"
|
127
|
+
end
|
128
|
+
|
129
|
+
if args.any?(&:nil?)
|
130
|
+
raise TypeError, "can't convert nil into Quaternion"
|
131
|
+
end
|
132
|
+
|
133
|
+
# convert String into Numeric (strictly)
|
134
|
+
args.collect! do |arg|
|
135
|
+
case arg
|
136
|
+
when String
|
137
|
+
Quaternion.send(:parse, arg, true)
|
138
|
+
else
|
139
|
+
arg
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
case argc
|
144
|
+
when 2
|
145
|
+
if args[1].kind_of?(Enumerable)
|
146
|
+
# scalar and 3-D vector
|
147
|
+
if args[1].size != 3
|
148
|
+
raise TypeError, "not a 3-D vector"
|
149
|
+
end
|
150
|
+
args = [args[0], *args[1]]
|
151
|
+
else
|
152
|
+
# a pair of complex numbers
|
153
|
+
return args[0] + args[1] * Quaternion::J
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# maximum four real numbers
|
158
|
+
i = Quaternion::I
|
159
|
+
j = Quaternion::J
|
160
|
+
k = Quaternion::K
|
161
|
+
zero = Quaternion.send(:new, 0, 0)
|
162
|
+
args.zip([1, i, j, k]).inject(zero) do |sum,(num,base)|
|
163
|
+
sum + num * base
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'classification'
|
3
|
+
|
4
|
+
class Quaternion
|
5
|
+
undef <=>
|
6
|
+
undef_method(*Comparable.instance_methods)
|
7
|
+
|
8
|
+
def ==(other)
|
9
|
+
if other.kind_of?(Quaternion)
|
10
|
+
@a == other.a && @b == other.b
|
11
|
+
elsif other.kind_of?(Numeric) && other.complex?
|
12
|
+
@a == other && @b == 0
|
13
|
+
else
|
14
|
+
other == self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def eql?(other)
|
19
|
+
if other.kind_of?(Quaternion)
|
20
|
+
@a.eql?(other.a) && @b.eql?(other.b)
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# q1.eql?(q2) => q1.hash == q2.hash
|
27
|
+
def hash
|
28
|
+
[@a, @b].hash
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'unary'
|
3
|
+
|
4
|
+
#
|
5
|
+
# to_xxx
|
6
|
+
#
|
7
|
+
|
8
|
+
class Quaternion
|
9
|
+
# defined by Numeric:
|
10
|
+
# * to_int #=> to_i
|
11
|
+
|
12
|
+
def to_q
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_c
|
17
|
+
unless __exact_zero__(@b)
|
18
|
+
raise RangeError, "can't convert #{self} into Complex"
|
19
|
+
end
|
20
|
+
@a
|
21
|
+
end
|
22
|
+
|
23
|
+
[:to_f, :to_r, :to_i, :rationalize].each do |sym|
|
24
|
+
define_method(sym) { |*args| to_c.send(sym, *args) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
__format__(:to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
"(#{__format__(:inspect)})"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def __format__(sym)
|
38
|
+
w, *xyz = [@a, @b].flat_map(&:rect)
|
39
|
+
sw = w.send(sym)
|
40
|
+
sx, sy, sz = xyz.zip(%w[i j k]).collect do |num,base|
|
41
|
+
if num.kind_of?(Float)
|
42
|
+
str = num.send(sym)
|
43
|
+
str[0,0] = '+' if str[0] != '-'
|
44
|
+
elsif num < 0
|
45
|
+
str = '-' + (-num).send(sym)
|
46
|
+
else
|
47
|
+
str = '+' + num.send(sym)
|
48
|
+
end
|
49
|
+
str << '*' if str[-1] !~ /\d/
|
50
|
+
str << base
|
51
|
+
end
|
52
|
+
[sw, sx, sy, sz].join
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
def parse(str, strict = false)
|
57
|
+
regexp = %r{
|
58
|
+
\A
|
59
|
+
\s*+
|
60
|
+
(?:(?'real' [+-]?+ \g'rational' ) (?![ijk]))?+
|
61
|
+
(?:(?'imag_i' \g'head_or_sign' \g'rational'?+) i )?+
|
62
|
+
(?:(?'imag_j' \g'head_or_sign' \g'rational'?+) j )?+
|
63
|
+
(?:(?'imag_k' \g'head_or_sign' \g'rational'?+) k )?+
|
64
|
+
\s*+
|
65
|
+
|\z
|
66
|
+
(?'head_or_sign' (?<=^|\s) [+-]?+ | [+-])
|
67
|
+
(?'digits' \d++ (?:#{strict ? "_" : "_++"} \d++)*+)
|
68
|
+
(?'int_or_float' (?:\g'digits')?+ (?:\. \g'digits')?+ (?<=\d) (?:e [+-]?+ \g'digits')?+)
|
69
|
+
(?'rational' \g'int_or_float' (?:/ \g'digits')?+)
|
70
|
+
}xi
|
71
|
+
|
72
|
+
match_data = regexp.match(str)
|
73
|
+
if strict && (!$'.empty? || match_data.captures[0,4].all?(&:nil?))
|
74
|
+
raise ArgumentError, "invalid value for convert(): #{str.inspect}"
|
75
|
+
end
|
76
|
+
|
77
|
+
w, x, y, z = match_data.captures[0,4].collect do |s|
|
78
|
+
case s
|
79
|
+
when %r{/} then s.to_r
|
80
|
+
when %r{[.eE]} then s.to_f
|
81
|
+
when '+', '' then 1
|
82
|
+
when '-' then -1
|
83
|
+
else s.to_i # Integer or nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
new(Complex.rect(w, x), Complex.rect(y, z))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Numeric
|
93
|
+
def to_q
|
94
|
+
Quaternion.send(:new, self, 0)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class String
|
99
|
+
def to_q
|
100
|
+
Quaternion.send(:parse, self, false)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class NilClass
|
105
|
+
def to_q
|
106
|
+
Quaternion.send(:new, 0, 0)
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Unary operations
|
5
|
+
#
|
6
|
+
|
7
|
+
class Quaternion
|
8
|
+
# defined by Numeric:
|
9
|
+
# * +@ #=> self
|
10
|
+
# * -@ #=> 0 - self
|
11
|
+
|
12
|
+
def conj
|
13
|
+
__new__(@a.conj, -@b)
|
14
|
+
end
|
15
|
+
alias conjugate conj
|
16
|
+
|
17
|
+
def abs2
|
18
|
+
@a.abs2 + @b.abs2
|
19
|
+
end
|
20
|
+
|
21
|
+
def abs
|
22
|
+
a_abs = @a.abs
|
23
|
+
b_abs = @b.abs
|
24
|
+
if __exact_zero__(a_abs)
|
25
|
+
b_abs
|
26
|
+
elsif __exact_zero__(b_abs)
|
27
|
+
a_abs
|
28
|
+
else
|
29
|
+
Math.hypot(a_abs, b_abs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
alias magnitude abs
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def __exact_zero__(x)
|
37
|
+
1 / x
|
38
|
+
rescue ZeroDivisionError
|
39
|
+
true
|
40
|
+
else
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Fundamental quaternion units
|
5
|
+
#
|
6
|
+
|
7
|
+
class Quaternion
|
8
|
+
i = Complex::I
|
9
|
+
I = new(i, 0)
|
10
|
+
J = new(0, 1)
|
11
|
+
K = new(0, i)
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Convert to an imaginary part
|
16
|
+
#
|
17
|
+
|
18
|
+
class Numeric
|
19
|
+
# defined:
|
20
|
+
# * #i #=> Complex.rect(0, self)
|
21
|
+
|
22
|
+
def j
|
23
|
+
Quaternion.send(:new, 0, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def k
|
27
|
+
Quaternion.send(:new, 0, self.i)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Complex
|
32
|
+
# undefined:
|
33
|
+
# * #i
|
34
|
+
|
35
|
+
undef k
|
36
|
+
end
|
37
|
+
|
38
|
+
class Quaternion
|
39
|
+
undef i, j, k
|
40
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'classification'
|
3
|
+
require_relative 'unary'
|
4
|
+
require_relative 'arithmetic'
|
5
|
+
require_relative 'conversion'
|
6
|
+
|
7
|
+
class Quaternion
|
8
|
+
def **(index)
|
9
|
+
if index.kind_of?(Numeric)
|
10
|
+
if __exact_zero__(index)
|
11
|
+
return __new__(1, 0)
|
12
|
+
end
|
13
|
+
|
14
|
+
# complex -> real
|
15
|
+
begin
|
16
|
+
index.to_f
|
17
|
+
rescue
|
18
|
+
else
|
19
|
+
index = index.real
|
20
|
+
end
|
21
|
+
|
22
|
+
# rational -> integer
|
23
|
+
if index.kind_of?(Rational) && index.denominator == 1
|
24
|
+
index = index.numerator
|
25
|
+
end
|
26
|
+
|
27
|
+
if index.integer?
|
28
|
+
# binary method
|
29
|
+
x = (index >= 0) ? self : 1 / self
|
30
|
+
n = index.abs
|
31
|
+
|
32
|
+
z = __new__(1, 0)
|
33
|
+
while true
|
34
|
+
n, i = n.divmod(2)
|
35
|
+
z *= x if i == 1
|
36
|
+
return z if n == 0
|
37
|
+
x *= x
|
38
|
+
end
|
39
|
+
elsif index.real?
|
40
|
+
r, theta, vector = polar
|
41
|
+
return Quaternion.polar(r ** index, theta * index, vector)
|
42
|
+
elsif index.complex? || index.kind_of?(Quaternion)
|
43
|
+
r, theta, vector = polar
|
44
|
+
q = Math.log(r) + Quaternion.hrect(0, *(theta * vector))
|
45
|
+
q *= index
|
46
|
+
return Quaternion.polar(Math.exp(q.real), 1, q.imag)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
num1, num2 = other.coerce(self)
|
51
|
+
num1 ** num2
|
52
|
+
end
|
53
|
+
|
54
|
+
def denominator
|
55
|
+
ad = @a.denominator
|
56
|
+
bd = @b.denominator
|
57
|
+
ad.lcm(bd)
|
58
|
+
end
|
59
|
+
|
60
|
+
def numerator
|
61
|
+
an = @a.numerator
|
62
|
+
bn = @b.numerator
|
63
|
+
ad = @a.denominator
|
64
|
+
bd = @b.denominator
|
65
|
+
abd = ad.lcm(bd)
|
66
|
+
__new__(an * (abd / ad), bn * (abd / bd))
|
67
|
+
end
|
68
|
+
|
69
|
+
undef step
|
70
|
+
undef ceil, floor, round, truncate
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "quaternion_c2"
|
6
|
+
spec.version = "0.1.1"
|
7
|
+
spec.authors = ["Masahiro Nomoto"]
|
8
|
+
spec.email = ["hmmnrst@users.noreply.github.com"]
|
9
|
+
|
10
|
+
spec.summary = %q{Quaternion class}
|
11
|
+
spec.description = %q{This provides a numeric class Quaternion which is similar to a built-in class Complex.}
|
12
|
+
spec.homepage = "https://github.com/hmmnrst/quaternion_c2"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quaternion_c2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masahiro Nomoto
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: This provides a numeric class Quaternion which is similar to a built-in
|
56
|
+
class Complex.
|
57
|
+
email:
|
58
|
+
- hmmnrst@users.noreply.github.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- .rspec
|
65
|
+
- .travis.yml
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/console
|
71
|
+
- bin/setup
|
72
|
+
- lib/quaternion_c2.rb
|
73
|
+
- lib/quaternion_c2/arithmetic.rb
|
74
|
+
- lib/quaternion_c2/attributes.rb
|
75
|
+
- lib/quaternion_c2/base.rb
|
76
|
+
- lib/quaternion_c2/classification.rb
|
77
|
+
- lib/quaternion_c2/conversion.rb
|
78
|
+
- lib/quaternion_c2/equality.rb
|
79
|
+
- lib/quaternion_c2/to_type.rb
|
80
|
+
- lib/quaternion_c2/unary.rb
|
81
|
+
- lib/quaternion_c2/units.rb
|
82
|
+
- lib/quaternion_c2/utils.rb
|
83
|
+
- quaternion_c2.gemspec
|
84
|
+
homepage: https://github.com/hmmnrst/quaternion_c2
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.5.0
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Quaternion class
|
108
|
+
test_files: []
|