hyper_complex 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +6 -0
- data/LICENSE +21 -0
- data/README.html +170 -0
- data/README.md +63 -0
- data/Rakefile +24 -0
- data/lib/hyper_complex.rb +834 -0
- data/lib/version.rb +5 -0
- metadata +88 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bc2ded74b42084d30a37f220acef7f64b61c86d9081b348b86a1a87a2ede8697
|
|
4
|
+
data.tar.gz: 387d3b3132215c75dd73cc7dafc0df63249783ee5b1e9bdacca445f094d1db16
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 59c192cde4da76a0875720fb3ce5ae262611ce1f5a9b8424c5693e9258bb0fbfbc94f6e3966dfb7f1a6d87a0dfbca8f4c25097ab8f205021cbe57c59885e01e7
|
|
7
|
+
data.tar.gz: 2cc6b32a325a6b74d922afd52732988b4d0c74c72223cb0f08a566f4af3e3aa0b9d5b36550a37f6dcd41c56d9e6671e458e1a9132d3aa5be4b22d33c06c313cb
|
data/.yardopts
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Boris Fifelin
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
data/README.html
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<body>
|
|
5
|
+
<h1 id="hypercomplex">HyperComplex</h1>
|
|
6
|
+
|
|
7
|
+
<p>This gem provides a <code>HyperComplex</code> class highly compatible with other numeric classes.</p>
|
|
8
|
+
|
|
9
|
+
<p>The <a href="https://en.wikipedia.org/wiki/Hypercomplex_number">hypercomplex numbers</a> form finite-dimensional
|
|
10
|
+
algebra over the real numbers. These algebras are produced by the <a
|
|
11
|
+
href="https://en.wikipedia.org/wiki/Cayley%E2%80%93Dickson_construction">Cayley–Dickson construction</a>.
|
|
12
|
+
Examples of such algebras are complex numbers, quaternions, octonions, sedenions, etc.</p>
|
|
13
|
+
|
|
14
|
+
<p>The hypercomplex number can be represented as</p>
|
|
15
|
+
<math>
|
|
16
|
+
<munderover>
|
|
17
|
+
<mi>∑</mi>
|
|
18
|
+
<mrow>
|
|
19
|
+
<mi>i</mi>
|
|
20
|
+
<mo>=</mo>
|
|
21
|
+
<mn>0</mn>
|
|
22
|
+
</mrow>
|
|
23
|
+
<mrow>
|
|
24
|
+
<msup>
|
|
25
|
+
<mn>2</mn>
|
|
26
|
+
<mi>n</mi>
|
|
27
|
+
</msup>
|
|
28
|
+
<mo>-</mo>
|
|
29
|
+
<mn>1</mn>
|
|
30
|
+
</mrow>
|
|
31
|
+
</munderover>
|
|
32
|
+
<msub>
|
|
33
|
+
<mi>a</mi>
|
|
34
|
+
<mi>i</mi>
|
|
35
|
+
</msub>
|
|
36
|
+
<msub>
|
|
37
|
+
<mi>e</mi>
|
|
38
|
+
<mi>i</mi>
|
|
39
|
+
</msub>
|
|
40
|
+
<mtext>   where  </mtext>
|
|
41
|
+
<msub>
|
|
42
|
+
<mi>a</mi>
|
|
43
|
+
<mi>i</mi>
|
|
44
|
+
</msub>
|
|
45
|
+
<mo>∈</mo>
|
|
46
|
+
<mstyle mathvariant="bold" , mathsize="big">
|
|
47
|
+
<mi>ℝ</mi>
|
|
48
|
+
</mstyle>
|
|
49
|
+
<mtext>,  </mtext>
|
|
50
|
+
<msub>
|
|
51
|
+
<mi>e</mi>
|
|
52
|
+
<mn>0</mn>
|
|
53
|
+
</msub>
|
|
54
|
+
<mo>=</mo>
|
|
55
|
+
<mn>1</mn>
|
|
56
|
+
<mtext>,  </mtext>
|
|
57
|
+
<msubsup>
|
|
58
|
+
<mi>e</mi>
|
|
59
|
+
<mn>1</mn>
|
|
60
|
+
<mn>2</mn>
|
|
61
|
+
</msubsup>
|
|
62
|
+
<mo>=</mo>
|
|
63
|
+
<mi>...</mi>
|
|
64
|
+
<mo>=</mo>
|
|
65
|
+
<msubsup>
|
|
66
|
+
<mi>e</mi>
|
|
67
|
+
<mrow>
|
|
68
|
+
<msup>
|
|
69
|
+
<mn>2</mn>
|
|
70
|
+
<mi>n</mi>
|
|
71
|
+
</msup>
|
|
72
|
+
<mo>-</mo>
|
|
73
|
+
<mn>1</mn>
|
|
74
|
+
</mrow>
|
|
75
|
+
<mn>2</mn>
|
|
76
|
+
</msubsup>
|
|
77
|
+
<mo>=</mo>
|
|
78
|
+
<mo>-</mo>
|
|
79
|
+
<mn>1</mn>
|
|
80
|
+
<mtext>, </mtext>
|
|
81
|
+
<mi>n</mi>
|
|
82
|
+
<mo>∈</mo>
|
|
83
|
+
<mstyle mathvariant="bold" , mathsize="big">
|
|
84
|
+
<mi>ℕ</mi>
|
|
85
|
+
</mstyle>
|
|
86
|
+
</math>
|
|
87
|
+
|
|
88
|
+
<p><math>
|
|
89
|
+
<mtext>The identity unit  (</mtext>
|
|
90
|
+
<msub>
|
|
91
|
+
<mi>e</mi>
|
|
92
|
+
<mn>0</mn>
|
|
93
|
+
</msub>
|
|
94
|
+
<mtext>)  and imaginary units  (</mtext>
|
|
95
|
+
<msub>
|
|
96
|
+
<mi>e</mi>
|
|
97
|
+
<mi>i</mi>
|
|
98
|
+
</msub>
|
|
99
|
+
<mtext>, </mtext>
|
|
100
|
+
<mi>i</mi>
|
|
101
|
+
<mo>≠</mo>
|
|
102
|
+
<mn>0</mn>
|
|
103
|
+
<mtext>)  form the basis for space of dimension  </mtext>
|
|
104
|
+
<msup>
|
|
105
|
+
<mn>2</mn>
|
|
106
|
+
<mi>n</mi>
|
|
107
|
+
</msup>
|
|
108
|
+
<mtext>  over  </mtext>
|
|
109
|
+
<mstyle mathvariant="bold" , mathsize="big">
|
|
110
|
+
<mi>ℝ</mi>
|
|
111
|
+
</mstyle>
|
|
112
|
+
<mtext>.</mtext>
|
|
113
|
+
</math></p>
|
|
114
|
+
|
|
115
|
+
<h2 id="requirements">Requirements</h2>
|
|
116
|
+
|
|
117
|
+
<p>Ruby >= 3.1</p>
|
|
118
|
+
|
|
119
|
+
<h2 id="installation">Installation</h2>
|
|
120
|
+
|
|
121
|
+
<p>Add this line to your application's Gemfile:</p>
|
|
122
|
+
|
|
123
|
+
<pre class="code ruby"><code class="ruby">gem 'mcalendar'</code></pre>
|
|
124
|
+
|
|
125
|
+
<p>And then execute:</p>
|
|
126
|
+
|
|
127
|
+
<pre class="code ruby"><code class="ruby">$ bundle install</code></pre>
|
|
128
|
+
|
|
129
|
+
<p>Or install it yourself as:</p>
|
|
130
|
+
|
|
131
|
+
<pre class="code ruby"><code class="ruby">$ gem install hyper_complex
|
|
132
|
+
</code></pre>
|
|
133
|
+
|
|
134
|
+
<h2 id="usage">Usage</h2>
|
|
135
|
+
|
|
136
|
+
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>hyper_complex</span><span class='tstring_end'>'</span></span>
|
|
137
|
+
|
|
138
|
+
<span class='comment'># Creation from complex numbers
|
|
139
|
+
</span><span class='id identifier rubyid_q1'>q1</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="HyperComplex.html" title="HyperComplex (class)">HyperComplex</a></span></span><span class='period'>.</span><span class='id identifier rubyid_rect'><span class='object_link'><a href="HyperComplex.html#rect-class_method" title="HyperComplex.rect (method)">rect</a></span></span><span class='lparen'>(</span><span class='lparen'>(</span><span class='int'>1</span><span class='op'>+</span><span class='imaginary'>2i</span><span class='rparen'>)</span><span class='comma'>,</span> <span class='lparen'>(</span><span class='int'>3</span><span class='op'>+</span><span class='imaginary'>4i</span><span class='rparen'>)</span><span class='rparen'>)</span> <span class='comment'>#=> HyperComplex[1, 2, 3, 4]
|
|
140
|
+
</span><span class='id identifier rubyid_q2'>q2</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="HyperComplex.html" title="HyperComplex (class)">HyperComplex</a></span></span><span class='period'>.</span><span class='id identifier rubyid_rect'><span class='object_link'><a href="HyperComplex.html#rect-class_method" title="HyperComplex.rect (method)">rect</a></span></span><span class='lparen'>(</span><span class='lparen'>(</span><span class='int'>5</span><span class='op'>+</span><span class='imaginary'>6i</span><span class='rparen'>)</span><span class='comma'>,</span> <span class='lparen'>(</span><span class='int'>7</span><span class='op'>+</span><span class='imaginary'>8i</span><span class='rparen'>)</span><span class='rparen'>)</span> <span class='comment'>#=> HyperComplex[5, 6, 7, 8]
|
|
141
|
+
</span>
|
|
142
|
+
<span class='comment'># Creation from HyperComplex numbers
|
|
143
|
+
</span><span class='id identifier rubyid_o1'>o1</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="HyperComplex.html" title="HyperComplex (class)">HyperComplex</a></span></span><span class='period'>.</span><span class='id identifier rubyid_rect'><span class='object_link'><a href="HyperComplex.html#rect-class_method" title="HyperComplex.rect (method)">rect</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_q1'>q1</span><span class='comma'>,</span> <span class='id identifier rubyid_q2'>q2</span><span class='rparen'>)</span> <span class='comment'>#=> HyperComplex[1, 2, 3, 4, 5, 6, 7, 8]
|
|
144
|
+
</span>
|
|
145
|
+
<span class='comment'># Creation from real numbers
|
|
146
|
+
</span><span class='id identifier rubyid_o2'>o2</span> <span class='op'>=</span> <span class='const'>HyperCoplex</span><span class='lbracket'>[</span><span class='int'>9</span><span class='comma'>,</span> <span class='int'>10</span><span class='comma'>,</span> <span class='int'>11</span><span class='comma'>,</span> <span class='int'>12</span><span class='comma'>,</span> <span class='int'>13</span><span class='rbracket'>]</span> <span class='comment'>#=> HyperComplex[9, 10, 11, 12, 13, 0, 0, 0]
|
|
147
|
+
</span>
|
|
148
|
+
<span class='comment'># Creation from polar form
|
|
149
|
+
</span><span class='id identifier rubyid_q3'>q3</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="HyperComplex.html" title="HyperComplex (class)">HyperComplex</a></span></span><span class='period'>.</span><span class='id identifier rubyid_polar'><span class='object_link'><a href="HyperComplex.html#polar-class_method" title="HyperComplex.polar (method)">polar</a></span></span><span class='lparen'>(</span><span class='int'>1</span><span class='comma'>,</span> <span class='const'>Math</span><span class='op'>::</span><span class='const'>PI</span><span class='op'>/</span><span class='int'>3</span><span class='comma'>,</span> <span class='const'>Vector</span><span class='lbracket'>[</span><span class='int'>1</span><span class='comma'>,</span> <span class='int'>1</span><span class='comma'>,</span> <span class='int'>1</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_normalize'>normalize</span><span class='rparen'>)</span> <span class='comment'>#=> HyperComplex[0.5, 0.5, 0.5, 0.5]
|
|
150
|
+
</span>
|
|
151
|
+
<span class='comment'># standard calculations between numeric instances
|
|
152
|
+
</span><span class='lparen'>(</span><span class='id identifier rubyid_q1'>q1</span><span class='op'>+</span><span class='id identifier rubyid_q2'>q2</span><span class='rparen'>)</span><span class='op'>*</span><span class='id identifier rubyid_o1'>o1</span> <span class='op'>/</span> <span class='const'>Complex</span><span class='op'>::</span><span class='const'>I</span> <span class='op'>-</span> <span class='int'>24</span> <span class='comment'>#=> HyperComplex[(0/1), (88/1), (-40/1), (20/1), (-80/1), (-184/1), (112/1), (-84/1)]
|
|
153
|
+
</span></code></pre>
|
|
154
|
+
|
|
155
|
+
<h2 id="contributing">Contributing</h2>
|
|
156
|
+
|
|
157
|
+
<p>Bug reports and pull requests are welcome on GitHub at <a
|
|
158
|
+
href="https://github.com/bfifelin/hyper_complex">github.com/bfifelin/hyper_complex</a>.</p>
|
|
159
|
+
|
|
160
|
+
<h2 id="license">License</h2>
|
|
161
|
+
|
|
162
|
+
<p>This project is licensed under the terms of the <a href="http://opensource.org/licenses/MIT">MIT license</a>.</p>
|
|
163
|
+
|
|
164
|
+
<h2 id="acknowledgments">Acknowledgments</h2>
|
|
165
|
+
|
|
166
|
+
<p>This gem is based on the gem <a href="https://rubygems.org/gems/quaternion_c2">quaternion_c2</a> by Masahiro
|
|
167
|
+
Nomoto.</p>
|
|
168
|
+
</body>
|
|
169
|
+
|
|
170
|
+
</html>
|
data/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# HyperComplex
|
|
2
|
+
|
|
3
|
+
This gem provides a `HyperComplex` class highly compatible with other numeric classes.
|
|
4
|
+
|
|
5
|
+
The [hypercomplex numbers](https://en.wikipedia.org/wiki/Hypercomplex_number) form finite-dimensional algebra over the real numbers. These algebras are produced by the [Cayley–Dickson construction](https://en.wikipedia.org/wiki/Cayley%E2%80%93Dickson_construction). Examples of such algebras are complex numbers, quaternions, octonions, sedenions, etc.
|
|
6
|
+
|
|
7
|
+
The hypercomplex number can be represented as
|
|
8
|
+
|
|
9
|
+
$\displaystyle\sum_{i=0}^{2^n-1} a_ie_i$ where $a_i \in \mathbf{R}$, $e_0=1$, $e_1^2= \ldots =e_{2^n-1}^2=-1$, $n \in \mathbf{N}$
|
|
10
|
+
|
|
11
|
+
The identity unit ($e_0$) and imaginary units ($e_i, i\gt0$) form the basis for space of dimension $2^n$ over $\mathbf{R}$.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
Ruby >= 3.1
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add this line to your application's Gemfile:
|
|
20
|
+
|
|
21
|
+
gem 'mcalendar'
|
|
22
|
+
|
|
23
|
+
And then execute:
|
|
24
|
+
|
|
25
|
+
$ bundle install
|
|
26
|
+
|
|
27
|
+
Or install it yourself as:
|
|
28
|
+
|
|
29
|
+
$ gem install hyper_complex
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
require 'hyper_complex'
|
|
35
|
+
|
|
36
|
+
# Creation from complex numbers
|
|
37
|
+
q1 = HyperComplex.rect((1+2i), (3+4i)) #=> HyperComplex[1, 2, 3, 4]
|
|
38
|
+
q2 = HyperComplex.rect((5+6i), (7+8i)) #=> HyperComplex[5, 6, 7, 8]
|
|
39
|
+
|
|
40
|
+
# Creation from HyperComplex numbers
|
|
41
|
+
o1 = HyperComplex.rect(q1, q2) #=> HyperComplex[1, 2, 3, 4, 5, 6, 7, 8]
|
|
42
|
+
|
|
43
|
+
# Creation from real numbers
|
|
44
|
+
o2 = HyperCoplex[9, 10, 11, 12, 13] #=> HyperComplex[9, 10, 11, 12, 13, 0, 0, 0]
|
|
45
|
+
|
|
46
|
+
# Creation from polar form
|
|
47
|
+
q3 = HyperComplex.polar(1, Math::PI/3, Vector[1, 1, 1].normalize) #=> HyperComplex[0.5, 0.5, 0.5, 0.5]
|
|
48
|
+
|
|
49
|
+
# standard calculations between numeric instances
|
|
50
|
+
(q1+q2)*o1 / Complex::I - 24 #=> HyperComplex[(0/1), (88/1), (-40/1), (20/1), (-80/1), (-184/1), (112/1), (-84/1)]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Contributing
|
|
54
|
+
|
|
55
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/bfifelin/hyper_complex.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
This project is licensed under the terms of the [MIT license](http://opensource.org/licenses/MIT).
|
|
60
|
+
|
|
61
|
+
## Acknowledgments
|
|
62
|
+
|
|
63
|
+
This gem is based on the gem [quaternion_c2](https://rubygems.org/gems/quaternion_c2) by Masahiro Nomoto.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'minitest/test_task'
|
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/lib/version.rb")
|
|
5
|
+
|
|
6
|
+
Minitest::TestTask.create(:test) do |t|
|
|
7
|
+
t.libs << 'test'
|
|
8
|
+
t.libs << 'lib'
|
|
9
|
+
t.test_globs = ['test/**/*_test.rb']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
task build: :gendoc do
|
|
13
|
+
system 'gem build hyper_complex.gemspec'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
task :gendoc do
|
|
17
|
+
system 'yardoc'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
task release: :build do
|
|
21
|
+
system "gem push hyper_complex-#{HyperComplex::VERSION}.gem"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
task default: :build
|
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'version'
|
|
4
|
+
require 'matrix'
|
|
5
|
+
|
|
6
|
+
# @private
|
|
7
|
+
class Numeric
|
|
8
|
+
def hrect = [real, imag]
|
|
9
|
+
alias hyperrectangular hrect
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# rubocop:disable Metrics/ClassLength, Lint/MissingCopEnableDirective
|
|
13
|
+
|
|
14
|
+
## Class HyperComplex
|
|
15
|
+
# Uses Cayley-Dickson construction
|
|
16
|
+
# * A subclass of +Numeric+
|
|
17
|
+
class HyperComplex < Numeric
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
@@mt = [[]]
|
|
21
|
+
|
|
22
|
+
undef_method(*Comparable.instance_methods)
|
|
23
|
+
|
|
24
|
+
def initialize(*arg) = @arv = arg.clone # rubocop:disable Lint/MissingSuper
|
|
25
|
+
|
|
26
|
+
public
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
##
|
|
30
|
+
# Creates a HyperComplex object arg_a + arg_b*e[[i]]
|
|
31
|
+
#
|
|
32
|
+
# @param arg_a [Numeric]
|
|
33
|
+
# @param arg_b [Numeric]
|
|
34
|
+
# @return [HyperComplex]
|
|
35
|
+
# @raise [ArgumentError]
|
|
36
|
+
#
|
|
37
|
+
# @example
|
|
38
|
+
# HyperComplex.rect(HyperComplex.rect(1+2i, 3+4i), HyperComplex.rect(5,6))
|
|
39
|
+
# #=> HyperComplex[1, 2, 3, 4, 5, 6, 0, 0]
|
|
40
|
+
#
|
|
41
|
+
def rect(arg_a, arg_b = 0)
|
|
42
|
+
raise ArgumentError, 'Not all arguments are Numeric' unless arg_a.is_a?(Numeric) && arg_b.is_a?(Numeric)
|
|
43
|
+
|
|
44
|
+
a = arg_a.real? ? [arg_a] : arg_a.hrect
|
|
45
|
+
b = arg_b.real? ? [arg_b] : arg_b.hrect
|
|
46
|
+
as = a.length
|
|
47
|
+
bs = b.length
|
|
48
|
+
if as > bs
|
|
49
|
+
b.concat(Array.new(as - bs, 0))
|
|
50
|
+
elsif as < bs
|
|
51
|
+
a.concat(Array.new(bs - as, 0))
|
|
52
|
+
end
|
|
53
|
+
new(*(a + b))
|
|
54
|
+
end
|
|
55
|
+
alias rectangular rect
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# Creates a HyperComplex object.
|
|
59
|
+
#
|
|
60
|
+
# @param args [Integer or Rational or Float, ...]
|
|
61
|
+
# @return [HyperComplex]
|
|
62
|
+
# @raise [ArgumentError]
|
|
63
|
+
#
|
|
64
|
+
# @example
|
|
65
|
+
# HyperComplex[1, 2, 3, 4, 5, 6] #=> HyperComplex[1, 2, 3, 4, 5, 6, 0, 0]
|
|
66
|
+
#
|
|
67
|
+
def hrect(*args)
|
|
68
|
+
begin
|
|
69
|
+
raise ArgumentError, 'Not all components are real' unless args.map(&:real?).all?
|
|
70
|
+
rescue NoMethodError
|
|
71
|
+
raise ArgumentError, 'Not all components are numeric'
|
|
72
|
+
end
|
|
73
|
+
s = args.length
|
|
74
|
+
if s < 2
|
|
75
|
+
if s.zero?
|
|
76
|
+
args = [0, 0]
|
|
77
|
+
else
|
|
78
|
+
args.push 0
|
|
79
|
+
end
|
|
80
|
+
s = 2
|
|
81
|
+
end
|
|
82
|
+
s1 = 1 << (s.bit_length - 1)
|
|
83
|
+
if s == s1
|
|
84
|
+
new(*args)
|
|
85
|
+
else
|
|
86
|
+
new(*args.concat(Array.new((s1 << 1) - s, 0)))
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
alias hyperrectangular hrect
|
|
90
|
+
alias [] hrect
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
##
|
|
95
|
+
# Fast variant of rect (only HyperComplex arguments of the same dimension are valid, no any check are performed)
|
|
96
|
+
#
|
|
97
|
+
def _rect(arg_a, arg_b) = new(*(arg_a.hrect + arg_b.hrect))
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Fast variant of hrect (no any check are performed for parameters' validity)
|
|
101
|
+
#
|
|
102
|
+
def _hrect(*arg) = new(*arg)
|
|
103
|
+
|
|
104
|
+
public
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
# Creates a HyperComplex object.
|
|
108
|
+
#
|
|
109
|
+
# @param radius [Integer or Rational or Float]
|
|
110
|
+
# @param theta [Integer or Rational or Float]
|
|
111
|
+
# @param vector [Vector or *[Integer or Rational or Float]]
|
|
112
|
+
# @return [HyperComplex]
|
|
113
|
+
# @raise [ArgumentError, TypeError]
|
|
114
|
+
#
|
|
115
|
+
# @example
|
|
116
|
+
# HyperComplex.polar(1, Math::PI / 4 , [1, 2, 3])
|
|
117
|
+
# #=> HyperComplex[-0.9794859508794878, 0.0538564706483192, 0.1077129412966384, 0.1615694119449576]
|
|
118
|
+
#
|
|
119
|
+
def polar(radius, theta, vector)
|
|
120
|
+
vs1 = vector.size + 1
|
|
121
|
+
raise TypeError, 'Vector size must be at least 3' if vs1 < 4
|
|
122
|
+
raise TypeError, 'Vector size is not equal to 2**n - 1' if vs1 != 1 << (vs1.bit_length - 1)
|
|
123
|
+
|
|
124
|
+
begin
|
|
125
|
+
raise ArgumentError, 'Not all components are real' unless [radius, theta, *vector].map(&:real?).all?
|
|
126
|
+
rescue NoMethodError
|
|
127
|
+
raise ArgumentError, 'Not all components are numeric'
|
|
128
|
+
end
|
|
129
|
+
vector = Vector[*vector] unless vector.is_a?(Vector)
|
|
130
|
+
norm = vector.norm
|
|
131
|
+
theta *= norm
|
|
132
|
+
r_cos = radius * Math.cos(theta)
|
|
133
|
+
r_sin = radius * Math.sin(theta)
|
|
134
|
+
r_sin /= norm unless norm.zero?
|
|
135
|
+
hrect(r_cos, *(r_sin * vector))
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# Creates HyperComplex basis element (identity or imaginary)
|
|
140
|
+
#
|
|
141
|
+
# @param num [Integer]
|
|
142
|
+
# @return [HyperComplex or Integer]
|
|
143
|
+
# @raise [ArgumentError]
|
|
144
|
+
#
|
|
145
|
+
# @example
|
|
146
|
+
# HyperComplex.e(5) #=> HyperComplex[0, 0, 0, 0, 0, 1, 0, 0]
|
|
147
|
+
#
|
|
148
|
+
def e(num)
|
|
149
|
+
raise ArgumentError, 'Argument must be non-negative integer' if !num.is_a?(Integer) || num.negative?
|
|
150
|
+
return 1 if num.zero?
|
|
151
|
+
|
|
152
|
+
hrect(*Array.new(num, 0), 1)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
# Creates HyperComplex zero
|
|
157
|
+
#
|
|
158
|
+
# @param num [Integer]
|
|
159
|
+
# @return [HyperComplex or Integer]
|
|
160
|
+
# @raise [ArgumentError]
|
|
161
|
+
#
|
|
162
|
+
# @example
|
|
163
|
+
# HyperComplex.zero(5) #=> HyperComplex[0, 0, 0, 0, 0, 0, 0, 0]
|
|
164
|
+
#
|
|
165
|
+
def zero(num)
|
|
166
|
+
raise ArgumentError, 'Argument must positive integer' unless num.is_a?(Integer) && num.positive?
|
|
167
|
+
return 0 if num == 1
|
|
168
|
+
|
|
169
|
+
hrect(*Array.new(num, 0))
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
##
|
|
173
|
+
# Prints the multiplication table to stdout
|
|
174
|
+
#
|
|
175
|
+
# @param dim [Integer]
|
|
176
|
+
# @raise [ArgumentError]
|
|
177
|
+
#
|
|
178
|
+
# @example
|
|
179
|
+
# HyperComplex.print_mt(8)
|
|
180
|
+
# | e0 e1 e2 e3 e4 e5 e6 e7
|
|
181
|
+
# ----+--------------------------------
|
|
182
|
+
# e0| e0 e1 e2 e3 e4 e5 e6 e7
|
|
183
|
+
# e1| e1 -e0 e3 -e2 e5 -e4 -e7 e6
|
|
184
|
+
# e2| e2 -e3 -e0 e1 e6 e7 -e4 -e5
|
|
185
|
+
# e3| e3 e2 -e1 -e0 e7 -e6 e5 -e4
|
|
186
|
+
# e4| e4 -e5 -e6 -e7 -e0 e1 e2 e3
|
|
187
|
+
# e5| e5 e4 -e7 e6 -e1 -e0 -e3 e2
|
|
188
|
+
# e6| e6 e7 e4 -e5 -e2 e3 -e0 -e1
|
|
189
|
+
# e7| e7 -e6 e5 e4 -e3 -e2 e1 -e0
|
|
190
|
+
#
|
|
191
|
+
def print_mt(dim) # rubocop:disable Metrics/AbcSize
|
|
192
|
+
table = @@mt = calc_mt(dim) if dim > @@mt.length
|
|
193
|
+
ss = dim
|
|
194
|
+
sf = ss.to_s.length + 3
|
|
195
|
+
out_s = "#{' ' * sf}|"
|
|
196
|
+
(0...ss).each { out_s << format("%#{sf}s", "e#{_1}") }
|
|
197
|
+
puts out_s
|
|
198
|
+
puts "#{'-' * sf}+#{'-' * ss * sf}"
|
|
199
|
+
out_s = format("%#{sf}s|", 'e0')
|
|
200
|
+
(0...ss).each { out_s << format("%#{sf}s", "e#{_1}") }
|
|
201
|
+
puts out_s
|
|
202
|
+
(1...ss).each do |i|
|
|
203
|
+
out_s = format("%#{sf}s|%#{sf}s", "e#{i}", "e#{i}")
|
|
204
|
+
(1...i).each do |j|
|
|
205
|
+
t = table[i][j]
|
|
206
|
+
out_s << format("%#{sf}s", (t.negative? ? '-' : ' ') + "e#{t.abs}")
|
|
207
|
+
end
|
|
208
|
+
out_s << format("%#{sf}s", '-e0')
|
|
209
|
+
((i + 1)...ss).each do |j|
|
|
210
|
+
t = table[j][i]
|
|
211
|
+
out_s << format("%#{sf}s", (t.negative? ? ' ' : '-') + "e#{t.abs}")
|
|
212
|
+
end
|
|
213
|
+
puts out_s
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
private
|
|
218
|
+
|
|
219
|
+
def calc_mt(dim) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
220
|
+
raise ArgumentError, 'Argument must be positive integer' unless dim.is_a?(Integer) && dim.positive?
|
|
221
|
+
|
|
222
|
+
raise ArgumentError, 'Argument must be equal to power of 2' unless (dim & (dim - 1)).zero?
|
|
223
|
+
|
|
224
|
+
raise "Seems to have infite loop while calculating multiplication table, dim=#{dim}" if dim <= @@mt.length
|
|
225
|
+
|
|
226
|
+
mt = Array.new(dim) { [] }
|
|
227
|
+
(1...dim).each do |i|
|
|
228
|
+
((i + 1)...dim).each do |j|
|
|
229
|
+
c = dim
|
|
230
|
+
r_and = i & j
|
|
231
|
+
res_e = (i ^ j)
|
|
232
|
+
f1 = i
|
|
233
|
+
f2 = j
|
|
234
|
+
while (c >>= 1) > 1
|
|
235
|
+
unless ((i | j) & c).zero? # (Not a & c)
|
|
236
|
+
if !(r_and & c).zero?
|
|
237
|
+
res_e = -res_e if (f2 & ~c).zero?
|
|
238
|
+
f1, f2 = f2, f1 # -d.conj*b
|
|
239
|
+
elsif (f1 & c).zero?
|
|
240
|
+
f1, f2 = f2, f1 # d*a
|
|
241
|
+
else
|
|
242
|
+
res_e = -res_e unless (f2 & ~c).zero? # b*c.conj
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
break if (f1 &= ~c).zero? || (f2 &= ~c).zero?
|
|
246
|
+
end
|
|
247
|
+
res_e = -res_e unless (r_and & 1).zero?
|
|
248
|
+
mt[j][i] = -res_e
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
mt
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def calc_mt_classic(dim)
|
|
255
|
+
raise ArgumentError, 'Argument must be positive integer' unless dim.is_a?(Integer) && dim.positive?
|
|
256
|
+
|
|
257
|
+
raise ArgumentError, 'Argument must be equal to power of 2' unless (dim & (dim - 1)).zero?
|
|
258
|
+
|
|
259
|
+
mt = Array.new(dim) { [] }
|
|
260
|
+
t_ar = Array.new(dim, 0)
|
|
261
|
+
t_ar[0] = 1
|
|
262
|
+
ar11 = [1, -1]
|
|
263
|
+
ids = Array.new(dim) { HyperComplex.e(_1) }
|
|
264
|
+
(1...dim).each do |i|
|
|
265
|
+
((i + 1)...dim).each do |j|
|
|
266
|
+
t_ar = ids[i].mul(ids[j]).hrect
|
|
267
|
+
ind = t_ar.index { ar11.include?(_1) }
|
|
268
|
+
mt[j][i] = -ind * t_ar[ind].to_i
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
mt
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
##
|
|
276
|
+
# Accessors
|
|
277
|
+
#
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
# Returns an array of two numbers.
|
|
281
|
+
#
|
|
282
|
+
# @return [[Integer or Rational or Float, Integer or Rational or Float] or [HyperComplex, HyperComplex]]
|
|
283
|
+
#
|
|
284
|
+
# @example
|
|
285
|
+
# HyperComplex[1, 2, 3, 4].rect #=> [HyperComplex[1, 2], HyperComplex[3, 4]]
|
|
286
|
+
#
|
|
287
|
+
def rect
|
|
288
|
+
ssh = @arv.length >> 1
|
|
289
|
+
return @arv if ssh == 1
|
|
290
|
+
|
|
291
|
+
[__new__(*@arv[0...(ssh)]), __new__(*@arv[ssh..])]
|
|
292
|
+
end
|
|
293
|
+
alias rectangular rect
|
|
294
|
+
|
|
295
|
+
##
|
|
296
|
+
# Returns an array of real numbers.
|
|
297
|
+
#
|
|
298
|
+
# @return [[Integer or Rational or Float, ...]]
|
|
299
|
+
#
|
|
300
|
+
# @example
|
|
301
|
+
# HyperComplex[1, 2, 3, 4].hrect #=> [1, 2, 3, 4]
|
|
302
|
+
#
|
|
303
|
+
def hrect = @arv
|
|
304
|
+
alias hyperrectangular hrect
|
|
305
|
+
alias to_a hrect
|
|
306
|
+
|
|
307
|
+
##
|
|
308
|
+
# Returns the real part.
|
|
309
|
+
#
|
|
310
|
+
# @return [Integer or Rational or Float]
|
|
311
|
+
#
|
|
312
|
+
# @example
|
|
313
|
+
# HyperComplex[1, 2, 3, 4].real #=> 1
|
|
314
|
+
#
|
|
315
|
+
def real = @arv[0]
|
|
316
|
+
alias scalar real
|
|
317
|
+
|
|
318
|
+
##
|
|
319
|
+
# Returns the imaginary part as a vector.
|
|
320
|
+
#
|
|
321
|
+
# @return [Vector]
|
|
322
|
+
#
|
|
323
|
+
# @example
|
|
324
|
+
# HyperComplex[1, 2, 3, 4].imag #=> Vector[2, 3, 4]
|
|
325
|
+
#
|
|
326
|
+
def imag = Vector[*hrect.drop(1)]
|
|
327
|
+
alias imaginary imag
|
|
328
|
+
alias vector imag
|
|
329
|
+
|
|
330
|
+
##
|
|
331
|
+
# Returns the angle part of its polar form.
|
|
332
|
+
#
|
|
333
|
+
# @return [Integer or Rational or Float]
|
|
334
|
+
#
|
|
335
|
+
# @example
|
|
336
|
+
# HyperComplex[3, 2, 2, 1].arg #=> 0.7853981633974483 (Math::PI/4)
|
|
337
|
+
#
|
|
338
|
+
def arg = Math.atan2(imag.norm, real)
|
|
339
|
+
|
|
340
|
+
alias angle arg
|
|
341
|
+
alias phase arg
|
|
342
|
+
##
|
|
343
|
+
# Returns the axis part of its polar form.
|
|
344
|
+
#
|
|
345
|
+
# @return [Vector]
|
|
346
|
+
#
|
|
347
|
+
# @example
|
|
348
|
+
# HyperComplex[6, 4, 4, 2].axis
|
|
349
|
+
# #=> Vector[0.6666666666666666, 0.6666666666666666, 0.3333333333333333]
|
|
350
|
+
#
|
|
351
|
+
def axis
|
|
352
|
+
v = imag
|
|
353
|
+
norm = v.norm
|
|
354
|
+
norm.zero? ? Vector[1, *Array.new(@arv.length - 2, 0)] : v / norm
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
##
|
|
358
|
+
# HyperComplex is not real number.
|
|
359
|
+
#
|
|
360
|
+
# @return [false]
|
|
361
|
+
#
|
|
362
|
+
# @example
|
|
363
|
+
# HyperComplex[6, 4, 4, 2].real? #=> false
|
|
364
|
+
#
|
|
365
|
+
def real? = false
|
|
366
|
+
|
|
367
|
+
##
|
|
368
|
+
# Returns true if all components are zero, otherwise returns false
|
|
369
|
+
#
|
|
370
|
+
# @return [true or false]
|
|
371
|
+
#
|
|
372
|
+
# @example
|
|
373
|
+
# HyperComplex[6, 4, 4, 2].zero? #=> false
|
|
374
|
+
#
|
|
375
|
+
def zero? = @arv.map(&:zero?).all?
|
|
376
|
+
|
|
377
|
+
##
|
|
378
|
+
# Returns true if any component is NaN, otherwise returns false
|
|
379
|
+
#
|
|
380
|
+
# @return [true or false]
|
|
381
|
+
#
|
|
382
|
+
# @example
|
|
383
|
+
# HyperComplex[6, 4, Float::NAN, 2].nan? #=> true
|
|
384
|
+
#
|
|
385
|
+
def nan? = @arv.map { |i| i.equal?(Float::NAN) }.any?
|
|
386
|
+
|
|
387
|
+
##
|
|
388
|
+
# Unary operations
|
|
389
|
+
#
|
|
390
|
+
|
|
391
|
+
# defined by Numeric:
|
|
392
|
+
# * +@ #=> self
|
|
393
|
+
|
|
394
|
+
##
|
|
395
|
+
# Returns negation of the value.
|
|
396
|
+
#
|
|
397
|
+
# @return [HyperComplex]
|
|
398
|
+
#
|
|
399
|
+
# @example
|
|
400
|
+
# -HyperComplex[6, 4, 1, 2] #=> HyperComplex[-6, -4, -1, -2]
|
|
401
|
+
#
|
|
402
|
+
def -@ = __new__(*@arv.map(&:-@))
|
|
403
|
+
|
|
404
|
+
##
|
|
405
|
+
# Returns its conjugate.
|
|
406
|
+
#
|
|
407
|
+
# @return [HyperComplex]
|
|
408
|
+
#
|
|
409
|
+
# @example
|
|
410
|
+
# HyperComplex[6, 4, 1, 2].conj #=> HyperComplex[6, -4, -1, -2]
|
|
411
|
+
#
|
|
412
|
+
def conj = __new__ @arv[0], *@arv[1..].map(&:-@)
|
|
413
|
+
alias conjugate conj
|
|
414
|
+
|
|
415
|
+
##
|
|
416
|
+
# Returns square of the absolute value.
|
|
417
|
+
#
|
|
418
|
+
# @return [Integer or Rational or Float]
|
|
419
|
+
#
|
|
420
|
+
# @example
|
|
421
|
+
# HyperComplex[2, 2, 2, 2].abs2 #=> 16
|
|
422
|
+
#
|
|
423
|
+
def abs2 = @arv.sum { _1**2 }
|
|
424
|
+
|
|
425
|
+
##
|
|
426
|
+
# Returns the absolute part of its polar form.
|
|
427
|
+
#
|
|
428
|
+
# @return [Integer or Rational or Float]
|
|
429
|
+
#
|
|
430
|
+
# @example
|
|
431
|
+
# HyperComplex[2, 2, 2, 2].abs #=> 4
|
|
432
|
+
#
|
|
433
|
+
def abs = Math.sqrt(abs2)
|
|
434
|
+
alias magnitude abs
|
|
435
|
+
|
|
436
|
+
##
|
|
437
|
+
# Returns an array; +[abs, arg, axis]+.
|
|
438
|
+
#
|
|
439
|
+
# @return [[Integer or Rational or Float, Integer or Rational or Float, Vector]]
|
|
440
|
+
#
|
|
441
|
+
# @example
|
|
442
|
+
# HyperComplex[3, 4, 4, 0].polar
|
|
443
|
+
# #=> [6.4031242374328485, 1.0831800840797905, Vector[0.7071067811865475, 0.7071067811865475, 0.0]]
|
|
444
|
+
#
|
|
445
|
+
def polar = [abs, arg, axis]
|
|
446
|
+
|
|
447
|
+
##
|
|
448
|
+
# Returns true if it equals to the other algebraically.
|
|
449
|
+
#
|
|
450
|
+
# @param other [Object]
|
|
451
|
+
# @return [true or false]
|
|
452
|
+
#
|
|
453
|
+
# @example
|
|
454
|
+
# HyperComplex[1, 2, 3] == HyperComplex[1, 2, 3, 0] #=> true
|
|
455
|
+
#
|
|
456
|
+
def ==(other)
|
|
457
|
+
sar, oar = homogenize(other)
|
|
458
|
+
sar == oar
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
##
|
|
462
|
+
# Returns true if it have the same dimension and the same elements.
|
|
463
|
+
#
|
|
464
|
+
# @param other [Object]
|
|
465
|
+
# @return [true or false]
|
|
466
|
+
#
|
|
467
|
+
# @example
|
|
468
|
+
# HyperComplex[1, 2, 3].eql?(HyperComplex[1, 2, 3, 0]) #=> true
|
|
469
|
+
# HyperComplex[1/1r, 2, 3, 4].eql?(HyperComplex[1, 2, 3, 4]) #=> false
|
|
470
|
+
#
|
|
471
|
+
def eql?(other) = @arv.eql?(other.hrect)
|
|
472
|
+
|
|
473
|
+
# defined by Numeric:
|
|
474
|
+
# * nonzero? #=> zero? ? nil : self
|
|
475
|
+
|
|
476
|
+
defined_methods = public_instance_methods
|
|
477
|
+
|
|
478
|
+
if defined_methods.include?(:finite?)
|
|
479
|
+
##
|
|
480
|
+
# Returns true if its magnitude is finite, oterwise returns false.
|
|
481
|
+
#
|
|
482
|
+
# @return [true or false]
|
|
483
|
+
#
|
|
484
|
+
# @example
|
|
485
|
+
# HyperComplex[1, 2, 3, 4].finite? #=> true
|
|
486
|
+
# HyperComplex[1, 2, Float::INFINITY, 4].finite? #=> false
|
|
487
|
+
#
|
|
488
|
+
def finite? = @arv.map(&:finite?).all?
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
if defined_methods.include?(:infinite?)
|
|
492
|
+
##
|
|
493
|
+
# Returns true if its magnitude is infinite, oterwise returns false.
|
|
494
|
+
#
|
|
495
|
+
# @return [true or false]
|
|
496
|
+
#
|
|
497
|
+
# @example
|
|
498
|
+
# HyperComplex[1, 2, 3, 4].infinite? #=> false
|
|
499
|
+
# HyperComplex[1, 2, Float::INFINITY, 4].infinite? #=> true
|
|
500
|
+
#
|
|
501
|
+
def infinite? = @arv.map(&:infinite?).any?
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
undef positive? if defined_methods.include?(:positive?)
|
|
505
|
+
undef negative? if defined_methods.include?(:negative?)
|
|
506
|
+
undef step, ceil, floor, round, truncate
|
|
507
|
+
|
|
508
|
+
##
|
|
509
|
+
# Performs addition.
|
|
510
|
+
#
|
|
511
|
+
# @param other [Numeric]
|
|
512
|
+
# @return [HyperComplex]
|
|
513
|
+
#
|
|
514
|
+
# @example
|
|
515
|
+
# HyperComplex[1, 1] + HyperComplex[0, 0, 0, 0, 1, 1] #=> HyperComplex[1, 1, 0, 0, 1, 1, 0, 0]
|
|
516
|
+
#
|
|
517
|
+
def +(other)
|
|
518
|
+
sar, oar = homogenize(other)
|
|
519
|
+
__new__(*sar.zip(oar).map(&:sum))
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
##
|
|
523
|
+
# Performs subtraction.
|
|
524
|
+
#
|
|
525
|
+
# @param other [Numeric]
|
|
526
|
+
# @return [HyperComplex]
|
|
527
|
+
#
|
|
528
|
+
# @example
|
|
529
|
+
# HyperComplex[1, 1] - HyperComplex[0, 0, 0, 0, 1, 1] #=> HyperComplex[1, 1, 0, 0, -1, -1, 0, 0]
|
|
530
|
+
#
|
|
531
|
+
def -(other)
|
|
532
|
+
sar, oar = homogenize(other)
|
|
533
|
+
__new__(*sar.zip(oar).map { |x| x.reduce(:-) })
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
##
|
|
537
|
+
# Performs multiplication (a, b)*(c, d) = (a*c - d.conj*b, d*a + b*c.conj).
|
|
538
|
+
#
|
|
539
|
+
# @param other [Numeric]
|
|
540
|
+
# @return [HyperComplex]
|
|
541
|
+
#
|
|
542
|
+
# @example
|
|
543
|
+
# HyperComplex[1, 2, 3, 4] * HyperComplex[4, 3, 2, 1] #=> HyperComplex[-12, 6, 24, 12]
|
|
544
|
+
#
|
|
545
|
+
def *(other) # rubocop:disable Metrics/AbcSize
|
|
546
|
+
sar, oar = homogenize(other)
|
|
547
|
+
ss = sar.length
|
|
548
|
+
begin
|
|
549
|
+
res = Array.new(ss, 0)
|
|
550
|
+
res[0] = sar[0] * oar[0]
|
|
551
|
+
(1...ss).each do |i|
|
|
552
|
+
res[0] -= sar[i] * oar[i]
|
|
553
|
+
res[i] += sar[0] * oar[i] + sar[i] * oar[0]
|
|
554
|
+
((i + 1)...ss).each do |j|
|
|
555
|
+
ind = @@mt[j][i]
|
|
556
|
+
if ind.positive?
|
|
557
|
+
res[ind] -= sar[i] * oar[j] - sar[j] * oar[i]
|
|
558
|
+
else
|
|
559
|
+
res[-ind] += sar[i] * oar[j] - sar[j] * oar[i]
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
rescue NoMethodError, TypeError
|
|
564
|
+
@@mt = HyperComplex.send(:calc_mt, ss)
|
|
565
|
+
retry
|
|
566
|
+
end
|
|
567
|
+
__new__(*res)
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
##
|
|
571
|
+
# Same as *, but 'classic' non-effective algorythm is used.
|
|
572
|
+
#
|
|
573
|
+
# @param other [Numeric]
|
|
574
|
+
# @return [HyperComplex]
|
|
575
|
+
#
|
|
576
|
+
# @example
|
|
577
|
+
# HyperComplex[1, 2, 3, 4].mul(HyperComplex[4, 3, 2, 1]) #=> HyperComplex[-12, 6, 24, 12]
|
|
578
|
+
#
|
|
579
|
+
def mul(other) # rubocop:disable Metrics/AbcSize
|
|
580
|
+
sar, oar = homogenize(other)
|
|
581
|
+
ss = sar.length
|
|
582
|
+
return __new__(sar[0] * oar[0] - oar[1] * sar[1], oar[1] * sar[0] + sar[1] * oar[0]) if ss == 2
|
|
583
|
+
|
|
584
|
+
a = __new__(*sar[0...(ss / 2)])
|
|
585
|
+
b = __new__(*sar[(ss / 2)..])
|
|
586
|
+
c = __new__(*oar[0...(ss / 2)])
|
|
587
|
+
d = __new__(*oar[(ss / 2)..])
|
|
588
|
+
__new__(*(a.mul(c) - d.conj.mul(b)).hrect, *(d.mul(a) + b.mul(c.conj)).hrect)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
##
|
|
592
|
+
# Inverse element. Rational arithmetic is used as possible.
|
|
593
|
+
#
|
|
594
|
+
# @return [HyperComplex]
|
|
595
|
+
# @raise [ZeroDivisionError] if all componetns are zero and not Float
|
|
596
|
+
#
|
|
597
|
+
# @example
|
|
598
|
+
# HyperComplex[1/4r, 1/4r, 1/4r, 1/4r] #=> HyperComplex[1, -1, -1, -1]
|
|
599
|
+
#
|
|
600
|
+
def inv
|
|
601
|
+
t = abs2
|
|
602
|
+
__new__(*conj.hrect.map { _1.to_r / t })
|
|
603
|
+
end
|
|
604
|
+
alias inverse inv
|
|
605
|
+
|
|
606
|
+
##
|
|
607
|
+
# Float Inverse element.
|
|
608
|
+
#
|
|
609
|
+
# @return [HyperComplex]
|
|
610
|
+
#
|
|
611
|
+
# @example
|
|
612
|
+
# HyperComplex[0.25, 0.25, 0.25, 0.25].inv #=> HyperComplex[1.0, -1.0, -1.0, -1.0]
|
|
613
|
+
# HyperComplex[0, 0, 0, 0].inv #=> HyperComplex[NaN, NaN, NaN, NaN]
|
|
614
|
+
#
|
|
615
|
+
def finv
|
|
616
|
+
t = abs2
|
|
617
|
+
__new__(*conj.hrect.map { _1.to_f / t })
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
##
|
|
621
|
+
# @!method quo(other)
|
|
622
|
+
#
|
|
623
|
+
# Performs rational as possible division.
|
|
624
|
+
#
|
|
625
|
+
# @param other [Numeric]
|
|
626
|
+
# @return [HyperComplex]
|
|
627
|
+
# @raise [ZeroDivisionError] if +other+ is zero and no conversion to float was performed.
|
|
628
|
+
#
|
|
629
|
+
# @example
|
|
630
|
+
# HyperComplex[12, -4, -2, 1] / HyperComplex[1, 24, -4, -8]
|
|
631
|
+
# #=> HyperComplex[-28/219r, -104/219r, 6/73r, 11/219r]
|
|
632
|
+
#
|
|
633
|
+
def quo(other)
|
|
634
|
+
t = other.abs2
|
|
635
|
+
self * __new__(*other.conj.hrect.map { _1.to_r / t })
|
|
636
|
+
end
|
|
637
|
+
alias / quo
|
|
638
|
+
|
|
639
|
+
##
|
|
640
|
+
# @!method fdiv(other)
|
|
641
|
+
#
|
|
642
|
+
# Performs float division.
|
|
643
|
+
#
|
|
644
|
+
# @param other [Numeric]
|
|
645
|
+
# @return [HyperComplex]
|
|
646
|
+
#
|
|
647
|
+
# @example
|
|
648
|
+
# HyperComplex[12, -4, -2, 1].fdiv(HyperComplex[1, 24, -4, -8])
|
|
649
|
+
# #=> HyperComplex[-0.1278538812785388, -0.4748858447488584, 0.0821917808219178, 0.0502283105022831]
|
|
650
|
+
# HyperComplex[12, -4, -2, 1].fdiv(HyperComplex[0, 0, 0, 0]) #=> HyperComplex[NaN, NaN, NaN, NaN]
|
|
651
|
+
def fdiv(other)
|
|
652
|
+
t = other.abs2
|
|
653
|
+
self * __new__(*other.conj.hrect.map { _1.to_f / t })
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
undef div, %, modulo, remainder, divmod
|
|
657
|
+
|
|
658
|
+
##
|
|
659
|
+
# Performs type conversion.
|
|
660
|
+
#
|
|
661
|
+
# @param other [Numeric]
|
|
662
|
+
# @return [[HyperComplex, self]]
|
|
663
|
+
# @raise [TypeError]
|
|
664
|
+
#
|
|
665
|
+
# @example
|
|
666
|
+
# HyperComplex[1, 2, 3, 4].coerce(1) #=> [HyperComplex[1, 0], HyperComplex[1, 2, 3, 4]]
|
|
667
|
+
#
|
|
668
|
+
def coerce(other) = [__new__(*other.hrect), self]
|
|
669
|
+
|
|
670
|
+
##
|
|
671
|
+
# Performs conversion to Integer.
|
|
672
|
+
#
|
|
673
|
+
# @return [Integer]
|
|
674
|
+
# @raise [RangeError]
|
|
675
|
+
#
|
|
676
|
+
# @example
|
|
677
|
+
# HyperComplex[3, 0, 0, 0].to_i #=> 3
|
|
678
|
+
#
|
|
679
|
+
def to_i
|
|
680
|
+
raise RangeError, "Can not convert #{inspect} to Integer" unless @arv[1..].map(&:zero?).all?
|
|
681
|
+
|
|
682
|
+
@arv[0].to_i
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
##
|
|
686
|
+
# Performs conversion to Float.
|
|
687
|
+
#
|
|
688
|
+
# @return [Float]
|
|
689
|
+
# @raise [RangeError]
|
|
690
|
+
#
|
|
691
|
+
# @example
|
|
692
|
+
# HyperComplex[3, 0, 0, 0].to_f #=> 3.0
|
|
693
|
+
#
|
|
694
|
+
def to_f
|
|
695
|
+
raise RangeError, "Can not convert #{inspect} to Float" unless @arv[1..].map(&:zero?).all?
|
|
696
|
+
|
|
697
|
+
@arv[0].to_f
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
##
|
|
701
|
+
# Performs conversion to Rational.
|
|
702
|
+
#
|
|
703
|
+
# @return [Rational]
|
|
704
|
+
# @raise [RangeError]
|
|
705
|
+
#
|
|
706
|
+
# @example
|
|
707
|
+
# HyperComplex[3, 0, 0, 0].to_r #=> 3/1r
|
|
708
|
+
#
|
|
709
|
+
def to_r
|
|
710
|
+
raise RangeError, "Can not convert #{inspect} to Rational" unless @arv[1..].map(&:zero?).all?
|
|
711
|
+
|
|
712
|
+
@arv[0].to_r
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
##
|
|
716
|
+
# Performs conversion to Complex.
|
|
717
|
+
#
|
|
718
|
+
# @return [Complex]
|
|
719
|
+
# @raise [RangeError]
|
|
720
|
+
#
|
|
721
|
+
# @example
|
|
722
|
+
# HyperComplex[3, 1, 0, 0].to_f #=> (3+1i)
|
|
723
|
+
#
|
|
724
|
+
def to_c
|
|
725
|
+
return Complex(@arv[0], @arv[1]) if @arv.length <= 2 || @arv[2..].map(&:zero?).all?
|
|
726
|
+
|
|
727
|
+
raise RangeError, "Can not convert #{inspect} to Complex"
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
##
|
|
731
|
+
# Performs conversion to String.
|
|
732
|
+
#
|
|
733
|
+
# @return [String]
|
|
734
|
+
#
|
|
735
|
+
# @example
|
|
736
|
+
# HyperComplex[4, 3, 2, 1].to_f #=> "HyperComplex[4, 3, 2, 1]"
|
|
737
|
+
#
|
|
738
|
+
def to_s = "HyperComplex#{@arv}"
|
|
739
|
+
alias inspect to_s
|
|
740
|
+
|
|
741
|
+
##
|
|
742
|
+
# Returns the dimension of hypercomplex number.
|
|
743
|
+
#
|
|
744
|
+
# @return [Integer]
|
|
745
|
+
#
|
|
746
|
+
# @example
|
|
747
|
+
# HyperComplex[4, 3, 2, 1].dim #=> 4
|
|
748
|
+
#
|
|
749
|
+
def dim = @arv.length
|
|
750
|
+
alias dimension dim
|
|
751
|
+
|
|
752
|
+
##
|
|
753
|
+
# Returns the index's element of hypercomplex number.
|
|
754
|
+
#
|
|
755
|
+
# @param index [Integer]
|
|
756
|
+
# @return [Integer or Rational or Float or nil]
|
|
757
|
+
#
|
|
758
|
+
# @example
|
|
759
|
+
# HyperComplex[4, 3, 2, 1].1 #=> 3
|
|
760
|
+
#
|
|
761
|
+
def [](index) = @arv[index]
|
|
762
|
+
|
|
763
|
+
##
|
|
764
|
+
# Performs exponentiation.
|
|
765
|
+
#
|
|
766
|
+
# @param other [Numeric]
|
|
767
|
+
# @return [HyperComplex]
|
|
768
|
+
#
|
|
769
|
+
# @example
|
|
770
|
+
# HyperComplex[1, 2, 3, 4]**HyperComplex[4, 3, 2, 1]
|
|
771
|
+
# #=> HyperComplex[9.648225704568818, -5.4921479890865506, -8.477947559523633, -4.2389737797618166]
|
|
772
|
+
#
|
|
773
|
+
def **(other) # rubocop:disable Metrics/AbcSize
|
|
774
|
+
unless other.is_a?(Numeric)
|
|
775
|
+
num1, num2 = other.coerce(self)
|
|
776
|
+
return num1**num2
|
|
777
|
+
end
|
|
778
|
+
if other.zero?
|
|
779
|
+
return __new__(*Array.new(dim, Float::NAN)) if zero?
|
|
780
|
+
|
|
781
|
+
return __new__(*Array.new(dim - 1, 0).unshift(1))
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
unless other.real?
|
|
785
|
+
begin
|
|
786
|
+
other.to_f
|
|
787
|
+
rescue # rubocop:disable Lint/SuppressedException,Style/RescueStandardError
|
|
788
|
+
else
|
|
789
|
+
other = other.real
|
|
790
|
+
end
|
|
791
|
+
end
|
|
792
|
+
other = other.numerator if other.is_a?(Rational) && other.denominator == 1
|
|
793
|
+
if other.integer?
|
|
794
|
+
x = other >= 0 ? self : 1.to_r / self
|
|
795
|
+
n = other.abs
|
|
796
|
+
z = 1
|
|
797
|
+
loop do
|
|
798
|
+
z *= x if n.odd?
|
|
799
|
+
n >>= 1
|
|
800
|
+
return z if n.zero?
|
|
801
|
+
|
|
802
|
+
x *= x
|
|
803
|
+
end
|
|
804
|
+
elsif other.real?
|
|
805
|
+
r, theta, vector = polar
|
|
806
|
+
HyperComplex.polar(r**other, theta * other, vector)
|
|
807
|
+
elsif other.is_a?(HyperComplex)
|
|
808
|
+
r, theta, vector = polar
|
|
809
|
+
q = HyperComplex.hrect(Math.log(r), *(theta * vector))
|
|
810
|
+
q *= other
|
|
811
|
+
HyperComplex.polar(Math.exp(q.real), 1, q.imag)
|
|
812
|
+
else
|
|
813
|
+
num1, num2 = other.coerce(self)
|
|
814
|
+
num1**num2
|
|
815
|
+
end
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
private
|
|
819
|
+
|
|
820
|
+
def homogenize(other)
|
|
821
|
+
ar = hrect
|
|
822
|
+
as = ar.length
|
|
823
|
+
br = other.hrect
|
|
824
|
+
bs = br.length
|
|
825
|
+
if as > bs
|
|
826
|
+
br.concat(Array.new(as - bs, 0))
|
|
827
|
+
elsif as < bs
|
|
828
|
+
ar.concat(Array.new(bs - as, 0))
|
|
829
|
+
end
|
|
830
|
+
[ar, br]
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
def __new__(*arg) = HyperComplex.send(:new, *arg)
|
|
834
|
+
end
|
data/lib/version.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hyper_complex
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Boris Fifelin
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-02-01 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: yard
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.9'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.9'
|
|
54
|
+
description: Hypercomplex numbers (by Cayley-Dickson construction)
|
|
55
|
+
email: bfifelin@gmail.com
|
|
56
|
+
executables: []
|
|
57
|
+
extensions: []
|
|
58
|
+
extra_rdoc_files: []
|
|
59
|
+
files:
|
|
60
|
+
- ".yardopts"
|
|
61
|
+
- LICENSE
|
|
62
|
+
- README.html
|
|
63
|
+
- README.md
|
|
64
|
+
- Rakefile
|
|
65
|
+
- lib/hyper_complex.rb
|
|
66
|
+
- lib/version.rb
|
|
67
|
+
homepage: https://github.com/bfifelin/hyper_complex
|
|
68
|
+
licenses:
|
|
69
|
+
- MIT
|
|
70
|
+
metadata: {}
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: 3.1.0
|
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
requirements: []
|
|
85
|
+
rubygems_version: 4.0.3
|
|
86
|
+
specification_version: 4
|
|
87
|
+
summary: Hypercomplex numbers
|
|
88
|
+
test_files: []
|