stick 1.2.0
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.
- data/CHANGES +7 -0
- data/COPYING +344 -0
- data/README +110 -0
- data/lib/stick/constants.rb +3 -0
- data/lib/stick/constants/cgs.rb +151 -0
- data/lib/stick/constants/mks.rb +158 -0
- data/lib/stick/constants/number.rb +33 -0
- data/lib/stick/constants/typeless_cgs.rb +141 -0
- data/lib/stick/constants/typeless_mks.rb +142 -0
- data/lib/stick/currency.rb +8 -0
- data/lib/stick/mapcar.rb +61 -0
- data/lib/stick/matrix.rb +1022 -0
- data/lib/stick/quaternion.rb +562 -0
- data/lib/stick/times.rb +441 -0
- data/lib/stick/units.rb +112 -0
- data/lib/stick/units/base.rb +980 -0
- data/lib/stick/units/currency.rb +159 -0
- data/lib/stick/units/data/binary/base.rb +4 -0
- data/lib/stick/units/data/cex.rb +5 -0
- data/lib/stick/units/data/currency-default.rb +5 -0
- data/lib/stick/units/data/currency-standard.rb +2 -0
- data/lib/stick/units/data/currency/base.rb +89 -0
- data/lib/stick/units/data/iec.rb +5 -0
- data/lib/stick/units/data/iec_binary/base.rb +6 -0
- data/lib/stick/units/data/si.rb +7 -0
- data/lib/stick/units/data/si/base.rb +9 -0
- data/lib/stick/units/data/si/derived.rb +26 -0
- data/lib/stick/units/data/si/extra.rb +22 -0
- data/lib/stick/units/data/uk.rb +10 -0
- data/lib/stick/units/data/uk/base.rb +22 -0
- data/lib/stick/units/data/units-default.rb +11 -0
- data/lib/stick/units/data/units-standard.rb +5 -0
- data/lib/stick/units/data/us.rb +10 -0
- data/lib/stick/units/data/us/base.rb +23 -0
- data/lib/stick/units/data/xmethods.rb +5 -0
- data/lib/stick/units/data/xmethods/cached.rb +84 -0
- data/lib/stick/units/data/xmethods/mapping.rb +87 -0
- data/lib/stick/units/loaders.rb +98 -0
- data/lib/stick/units/units.rb +109 -0
- data/meta/MANIFEST +76 -0
- data/meta/ROLLRC +2 -0
- data/meta/icli.yaml +16 -0
- data/meta/project.yaml +18 -0
- data/task/clobber/package +10 -0
- data/task/publish +57 -0
- data/task/release +10 -0
- data/task/setup +1616 -0
- data/task/test +25 -0
- data/test/spec_matrix.rb +342 -0
- data/test/test_currency.rb +26 -0
- data/test/test_matrix.rb +359 -0
- data/test/test_units.rb +205 -0
- data/work/TODO +20 -0
- data/work/bytes.rb +231 -0
- data/work/multipliers.rb +195 -0
- metadata +138 -0
@@ -0,0 +1,562 @@
|
|
1
|
+
# Title:
|
2
|
+
#
|
3
|
+
# Quaternion
|
4
|
+
#
|
5
|
+
# Copyright:
|
6
|
+
#
|
7
|
+
# Copyright (c) 2002 K. Kodama
|
8
|
+
#
|
9
|
+
# License:
|
10
|
+
#
|
11
|
+
# Ruby License
|
12
|
+
#
|
13
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
14
|
+
# software under the same terms as Ruby.
|
15
|
+
#
|
16
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
17
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
18
|
+
# FOR A PARTICULAR PURPOSE.
|
19
|
+
#
|
20
|
+
# Authors:
|
21
|
+
#
|
22
|
+
# K. Kodama
|
23
|
+
#
|
24
|
+
# Todo:
|
25
|
+
#
|
26
|
+
# - The following documentation should occur before the methods
|
27
|
+
# they describe.
|
28
|
+
|
29
|
+
require "mathn"
|
30
|
+
require "complex"
|
31
|
+
|
32
|
+
# = Quaternion
|
33
|
+
#
|
34
|
+
# NOTE This Quaternion class is still very experimental.
|
35
|
+
#
|
36
|
+
# Quaternions are attributed to Sir William Rowan Hamilton
|
37
|
+
# who find it in 1843, and published a major analysis in 1844 called
|
38
|
+
# "On a Species of Imaginary Quantities Connected with a Theory of Quaternions"
|
39
|
+
# in the Proceedings of the Royal Irish Academ. (2, pp 424-434).
|
40
|
+
#
|
41
|
+
# Typical quaternion number q is of the form q = r + a i + b j + c k.
|
42
|
+
# Bases i j k behaves as follows:
|
43
|
+
#
|
44
|
+
# i^2 = j^2 = k^2 = -1
|
45
|
+
# i j = k, j k = i, k i = j
|
46
|
+
# j i = -k, k j = -i, i k = -j
|
47
|
+
#
|
48
|
+
# Quaternion numbers are not Commutative.
|
49
|
+
# Quaternion is 4-D space over Real number,
|
50
|
+
# and 2-D space over Complex numbers as q = (a + b i) + (c + d i)j.
|
51
|
+
#
|
52
|
+
# === Polar Coordinates
|
53
|
+
#
|
54
|
+
# A Quaternion q = r + a i + b j + k c have 1st level polar form such that
|
55
|
+
#
|
56
|
+
# q = |q|(cos t1 + sin t1 u1) , where u1 is unit vector of u1 = a1 i + b1 j + c1 k.
|
57
|
+
#
|
58
|
+
# u1 have 2nd level
|
59
|
+
#
|
60
|
+
# u1 = i cos t2 + sin t2 u2, where u2 is unit vector of u2 = b2 j + c2 k.
|
61
|
+
#
|
62
|
+
# And u2 have 3rd level
|
63
|
+
#
|
64
|
+
# u2 = j cos t3 + k sin t3.
|
65
|
+
#
|
66
|
+
# So we have
|
67
|
+
#
|
68
|
+
# q=|q|( cos t1 + sin t1 ( i cos t2 + sin t2 ( j cos t3 + k sin t3 )))
|
69
|
+
#
|
70
|
+
# The equivalent to polar coordinates in quaternion space are
|
71
|
+
#
|
72
|
+
# r = |q| cos(t1)
|
73
|
+
# a = |q| sin(t1) cos(t2)
|
74
|
+
# b = |q| sin(t1) sin(t2) cos(t3)
|
75
|
+
# c = |q| sin(t1) sin(t2) sin(t3)
|
76
|
+
#
|
77
|
+
# |q| is known as the magnitude of the quaternion, t1 is the amplitude(or angle),
|
78
|
+
# t2 and t3 are the latitude (or co-latitude) and longitude respectively.
|
79
|
+
#
|
80
|
+
# === Vector
|
81
|
+
#
|
82
|
+
# A Quaternions q= r + a i + b j + c k is 4-D space over Real numbers.
|
83
|
+
# A Quaternions with zero real part q = a i + b j + c k is 3-D space,
|
84
|
+
# and called a vector quaternion or, simply, vector.
|
85
|
+
# For q = r + a i + b j + c k,
|
86
|
+
# v=a i + b j + c k is called vector part of q.
|
87
|
+
# A vector u of |u|=1 is called a unit vector.
|
88
|
+
# We can write q = r + u |v| = |q|(cos t + u sin t).
|
89
|
+
# Note that u^2=-1.
|
90
|
+
# Vectors are 3-D space And can define cross-product q1 x q2.
|
91
|
+
#
|
92
|
+
# === Rotation
|
93
|
+
#
|
94
|
+
# Quaternion can be used to describe rotation in 3-D space.
|
95
|
+
# For a vector v and a Quaternion q = |q|(cos t/2 + u sin t/2),
|
96
|
+
# q v q^(-1) is a vector v t-rotated along u.
|
97
|
+
# Composit rotation of q1, q2 is described as q2 q1,
|
98
|
+
# because q2 (q1 v q1^(-1)) q2^(-1) = (q2 q1) v (q2 q1)^(-1).
|
99
|
+
#
|
100
|
+
# === GCD
|
101
|
+
#
|
102
|
+
# D4 lattice space is lattice points of Quaternion q = r + a i + b j + c k as follows.
|
103
|
+
# (1) r,a,b,c are all integer, or
|
104
|
+
# (2) r,a,b,c are all half-integer.
|
105
|
+
# D4 is sub-ring of Quaternion with GCD.
|
106
|
+
# (Ring means a space with +, -, *.)
|
107
|
+
|
108
|
+
=begin
|
109
|
+
|
110
|
+
* Building quaternions and taking them apart
|
111
|
+
Quaternion(real number r) # r as quaternion
|
112
|
+
Quaternion(a+bi) # a+bi as quaternion
|
113
|
+
Quaternion(a+bi,c+di) # a+bi+cj+dk = (a+bi)+(c+di)j
|
114
|
+
Quaternion(a,b=0,c=0,d=0) # a+bi+cj+dk
|
115
|
+
Quaternion(quaternion number q) # return q
|
116
|
+
Quaternion::Zero (=0 as quaternion)
|
117
|
+
Quaternion::One (=1 as quaternion)
|
118
|
+
Quaternion::I (=i as quaternion)
|
119
|
+
Quaternion::J (=j)
|
120
|
+
Quaternion::K (=k)
|
121
|
+
q.re ( = real part of q)
|
122
|
+
q.real ( = real part of q)
|
123
|
+
q.real_part ( = real part of q)
|
124
|
+
q.im ( = i part of q)
|
125
|
+
q.image ( = i part of q)
|
126
|
+
q.jm ( = j part of q)
|
127
|
+
q.km ( = k part of q)
|
128
|
+
orthogonal_split(o)
|
129
|
+
return [q1,q2].
|
130
|
+
q = q1 + q2 such that q1 parallel to o, and q2 orthogonal to o.
|
131
|
+
|
132
|
+
* Vector
|
133
|
+
q.vector ( = vector part of q = non-real part of q)
|
134
|
+
q.unit_vector ( = unit vector of q)
|
135
|
+
Quaternian::vector(v)
|
136
|
+
# 3-D vector v=[x,y,z] as array to Quaternion vector
|
137
|
+
Quaternion::rotation(v,t)
|
138
|
+
# t-rotatin along the 3-D vector v
|
139
|
+
q.rotate(r)
|
140
|
+
rotate by r = q r^(-1)
|
141
|
+
q.rotate_angle
|
142
|
+
# = q.amplitude/2
|
143
|
+
|
144
|
+
* Polar notation
|
145
|
+
Quaternion::polar(m,t1=0,t2=0,t3=0)
|
146
|
+
q.magnitude (= |q|)
|
147
|
+
q.amplitude (=arg1)
|
148
|
+
q.latitude (=arg2)
|
149
|
+
q.longitude (=arg3)
|
150
|
+
q.arg1
|
151
|
+
q.arg2
|
152
|
+
q.arg3
|
153
|
+
q.abs ( = |q| )
|
154
|
+
q.abs2 ( = |q|^2 )
|
155
|
+
q.polar # get array of [magnitude, amplitude, latitude, longitude]
|
156
|
+
|
157
|
+
* Boolean
|
158
|
+
Quaternion::generic?(other)
|
159
|
+
q.is_vector? # have no real part
|
160
|
+
q.is_unit_vector? # q is vector and |q|=1
|
161
|
+
q.is_complex? # j part =0 and k part = 0
|
162
|
+
q.is_quaternion? # have j or k part
|
163
|
+
q.is_real? # i,j,k parts are all 0
|
164
|
+
|
165
|
+
* Arithmetic
|
166
|
+
q.conjugate (= q~)
|
167
|
+
q.inverse (=1/q)
|
168
|
+
q1<=>q2 ( same as |q1|<=>|q2| )
|
169
|
+
q1==q2
|
170
|
+
q1 + q2
|
171
|
+
q1 - q2
|
172
|
+
q1 * q2
|
173
|
+
q1 / q2
|
174
|
+
q1.dot_product(q2) (Dot product q1, q2 = (q1*q2.conjugate).real_part )
|
175
|
+
q1.cross_product(q2) (Cross product as vectors q1,q2)
|
176
|
+
Assume q1, q2 be 3-D vectors.
|
177
|
+
q1.rdiv(q2) # right division: q1/q2 (same as /)
|
178
|
+
q1.ldiv(q2) # left division: 1/q1 * q2
|
179
|
+
|
180
|
+
* lattice or D4-lattice
|
181
|
+
q.round # round to integer coefficients
|
182
|
+
q.round_D4 # round to D4 lattice
|
183
|
+
q1.divmod(q2) # right divmod: q1=d*q2+m
|
184
|
+
q1.ldivmod(q2) # left divmod: q2=q1*d+m
|
185
|
+
q1 % q2 # right mod
|
186
|
+
q1.rmod(q2) # right mod(same as %)
|
187
|
+
q1.lmod(q2) # left mod
|
188
|
+
divmod_D4 other # right divmod: q1=d*q2+m, d be D4 value
|
189
|
+
ldivmod_D4 other # left divmod: q2=q1*d+m, d be D4 value
|
190
|
+
rmod_D4 other # right mod with d be D4 value
|
191
|
+
lmod_D4 other # left mod with d be D4 value
|
192
|
+
q1.gcd(q2) # Asume that q1, q2 are D4 value
|
193
|
+
|
194
|
+
* Exponential and logarithmic functions
|
195
|
+
q.exp # e^(r+uv)=exp(r)(cos(v)+u*sin(v))
|
196
|
+
q.log # log(r+uv)=1/2 log(r^2+v^2)+u atan(v/w)
|
197
|
+
q1**q2 ( = q1^q2 )
|
198
|
+
q.sqrt ( = q^1/2 )
|
199
|
+
q.sinh
|
200
|
+
q.cosh
|
201
|
+
q.tanh
|
202
|
+
|
203
|
+
* Trigonometric functions
|
204
|
+
q.sin
|
205
|
+
q.cos
|
206
|
+
q.tan
|
207
|
+
|
208
|
+
* Inverse trigonometric functions
|
209
|
+
q.asin
|
210
|
+
q.acos
|
211
|
+
q.atan
|
212
|
+
|
213
|
+
* Conversion
|
214
|
+
to_s # get string
|
215
|
+
to_c # real and i part as complex
|
216
|
+
to_c2 # j and k part as complex
|
217
|
+
to_v # vector part as array
|
218
|
+
to_a # array of parts
|
219
|
+
|
220
|
+
* Other
|
221
|
+
q.hash
|
222
|
+
q.inspect
|
223
|
+
=end
|
224
|
+
|
225
|
+
|
226
|
+
def Quaternion(a=0, b=0,c=0, d=0)
|
227
|
+
if a.kind_of?(Quaternion);
|
228
|
+
a;
|
229
|
+
elsif a.kind_of?(Complex) and b.kind_of?(Complex);
|
230
|
+
Quaternion.new(a.real, a.image, b.real, b.image)
|
231
|
+
elsif a.kind_of?(Complex);
|
232
|
+
Quaternion.new(a.real, a.image)
|
233
|
+
else
|
234
|
+
Quaternion.new(a,b,c,d);
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class Quaternion < Numeric
|
239
|
+
attr :re
|
240
|
+
attr :im
|
241
|
+
attr :jm
|
242
|
+
attr :km
|
243
|
+
def image; return @im; end
|
244
|
+
def real_part; return @re; end
|
245
|
+
def real; return @re; end
|
246
|
+
def to_c; return Complex(@re,@im); end
|
247
|
+
def to_c2; return Complex(@jm,@km); end
|
248
|
+
def to_a; return [@re, @im, @jm, @km]; end
|
249
|
+
def Quaternion::generic?(other)
|
250
|
+
return (other.kind_of?(Complex) or Complex.generic?(other));
|
251
|
+
end
|
252
|
+
def initialize(a=0,b=0,c=0,d=0)
|
253
|
+
raise "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric;
|
254
|
+
raise "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric;
|
255
|
+
raise "non numeric 3rd arg `#{c.inspect}'" if !c.kind_of? Numeric;
|
256
|
+
raise "non numeric 4th arg `#{d.inspect}'" if !d.kind_of? Numeric;
|
257
|
+
@re=a; @im=b; @jm=c; @km=d
|
258
|
+
end
|
259
|
+
private :initialize
|
260
|
+
|
261
|
+
Zero=Quaternion(0)
|
262
|
+
One=Quaternion(1)
|
263
|
+
I=Quaternion(0,1)
|
264
|
+
J=Quaternion(0,0,1)
|
265
|
+
K=Quaternion(0,0,0,1)
|
266
|
+
|
267
|
+
def Quaternion::polar(m,t1=0,t2=0,t3=0)
|
268
|
+
# q=
|
269
|
+
# m*cos(t1)
|
270
|
+
# +m*sin(t1)cos(t2)i
|
271
|
+
# +m*sin(t1)sin(t2)cos(t3)j
|
272
|
+
# +m*sin(t1)sin(t2)sin(t3)k
|
273
|
+
# m is known as the magnitude,
|
274
|
+
# t1 is the amplitude(or angle) of the quaternion,
|
275
|
+
# t2 and t3 are the latitude (or co-latitude) and longitude respectively.
|
276
|
+
if m.kind_of?(Array) and (m.size==4); t1=m[1]; t2=m[2]; t3=m[3]; m=m[0]; end;
|
277
|
+
s=m
|
278
|
+
r_part=s*Math.cos(t1); s=s*Math.sin(t1)
|
279
|
+
i_part=s*Math.cos(t2); s=s*Math.sin(t2)
|
280
|
+
j_part=s*Math.cos(t3); k_part=s*Math.sin(t3)
|
281
|
+
new(r_part, i_part, j_part, k_part)
|
282
|
+
end
|
283
|
+
def amplitude; Math.atan2(Math.sqrt((@im*@im+@jm*@jm+@km*@km).to_f),@re.to_f); end
|
284
|
+
def latitude; Math.atan2(Math.sqrt((@jm*@jm+@km*@km).to_f),@im.to_f); end
|
285
|
+
def longitude; Math.atan2( @km.to_f, @jm.to_f); end
|
286
|
+
def arg1; return amplitude; end
|
287
|
+
def arg2; return latitude; end
|
288
|
+
def arg3; return longitude; end
|
289
|
+
def polar; [magnitude, amplitude, latitude, longitude]; end
|
290
|
+
|
291
|
+
def round; Quaternion(@re.round,@im.round,@jm.round,@km.round);end
|
292
|
+
def round_D4
|
293
|
+
# round to D4 lattice
|
294
|
+
r1=@re.round; a1=@im.round; b1=@jm.round; c1=@km.round;
|
295
|
+
q1=Quaternion(r1,a1,b1,c1); d1=(q1-self).abs2
|
296
|
+
if d1<=1/4; return q1; end
|
297
|
+
if @re<r1; r2=r1-1/2; else r2=r1+1/2; end
|
298
|
+
if @im<r1; a2=a1-1/2; else a2=a1+1/2; end
|
299
|
+
if @jm<r1; b2=b1-1/2; else b2=b1+1/2; end
|
300
|
+
if @km<r1; c2=c1-1/2; else c2=c1+1/2; end
|
301
|
+
q2=Quaternion(r2,a2,b2,c2); d2=(q2-self).abs2
|
302
|
+
if d1<=d2; return q1; else return q2; end
|
303
|
+
end
|
304
|
+
def abs2; return @re*@re+@im*@im+@jm*@jm+@km*@km; end
|
305
|
+
def abs; Math.sqrt((@re*@re+@im*@im+@jm*@jm+@km*@km).to_f); end
|
306
|
+
def magnitude; return abs; end
|
307
|
+
def conjugate; Quaternion(@re,-@im,-@jm,-@km); end
|
308
|
+
def inverse; conjugate/abs2; end
|
309
|
+
|
310
|
+
def is_real?; @im==0 and @jm==0 and @km==0; end
|
311
|
+
def is_complex?; @jm==0 and @km==0; end
|
312
|
+
def is_quaternion?; not(is_complex?); end
|
313
|
+
|
314
|
+
def vector; Quaternion(0,@im,@jm,@km); end
|
315
|
+
def is_vector?; @re==0; end
|
316
|
+
def to_v; return [@im, @jm, @km]; end
|
317
|
+
def Quaternion::vector(v)
|
318
|
+
# 3-D vector v=[x,y,z]
|
319
|
+
Quaternion(0,v[0],v[1],v[2])
|
320
|
+
end
|
321
|
+
def unit_vector
|
322
|
+
if is_real?; return Quaternion(0,1); end
|
323
|
+
m=Math::sqrt((@im*@im+@jm*@jm+@km*@km).to_f)
|
324
|
+
Quaternion(0,@im/m,@jm/m,@km/m);
|
325
|
+
end
|
326
|
+
def is_unit_vector?; @re==0 and abs2==1; end
|
327
|
+
def Quaternion::rotation(v,t)
|
328
|
+
# t-rotatin along the 3-D vector v
|
329
|
+
(Quaternion::vector(v).unit_vector) * Math::sin(t/2) + Math::cos(t/2)
|
330
|
+
end
|
331
|
+
def rotate(r); r * self * r.conjugate / r.abs2; end
|
332
|
+
def rotate_angle; amplitude/2; end
|
333
|
+
|
334
|
+
# Arithmetic
|
335
|
+
def coerce(other)
|
336
|
+
if other.kind_of?(Complex); return Quaternion(other), self
|
337
|
+
elsif Complex::generic?(other); return Quaternion(other), self
|
338
|
+
else super
|
339
|
+
end
|
340
|
+
end
|
341
|
+
def <=> (other); self.abs <=> other.abs; end
|
342
|
+
def == (other)
|
343
|
+
if other.kind_of?(Quaternion)
|
344
|
+
return (@re==other.re and @im==other.im and @jm==other.jm and @km==other.km)
|
345
|
+
elsif other.kind_of?(Complex)
|
346
|
+
@re==other.real and @im==other.image and @jm==0 and @km==0
|
347
|
+
elsif Complex.generic?(other)
|
348
|
+
@re==other and @im==0 and @jm==0 and @km==0
|
349
|
+
else x , y = other.coerce(self); x == y
|
350
|
+
end
|
351
|
+
end
|
352
|
+
def + (other)
|
353
|
+
if other.kind_of?(Quaternion)
|
354
|
+
Quaternion(@re+other.re,@im+other.im,@jm+other.jm,@km+other.km)
|
355
|
+
elsif other.kind_of?(Complex)
|
356
|
+
Quaternion(@re+other.real,@im+other.image, @jm, @km)
|
357
|
+
elsif Complex.generic?(other)
|
358
|
+
Quaternion(@re+other.real,@im, @jm, @km)
|
359
|
+
else x , y = other.coerce(self); x + y
|
360
|
+
end
|
361
|
+
end
|
362
|
+
def - (other)
|
363
|
+
if other.kind_of?(Quaternion)
|
364
|
+
Quaternion(@re-other.re,@im-other.im,@jm-other.jm,@km-other.km)
|
365
|
+
elsif other.kind_of?(Complex)
|
366
|
+
Quaternion(@re-other.real,@im-other.image, @jm, @km)
|
367
|
+
elsif Complex.generic?(other)
|
368
|
+
Quaternion(@re-other.real,@im, @jm, @km)
|
369
|
+
else x , y = other.coerce(self); x - y
|
370
|
+
end
|
371
|
+
end
|
372
|
+
def * (other)
|
373
|
+
if other.kind_of?(Quaternion)
|
374
|
+
Quaternion(@re*other.re-@im*other.im-@jm*other.jm-@km*other.km,
|
375
|
+
@re*other.im+@im*other.re+@jm*other.km-@km*other.jm,
|
376
|
+
@re*other.jm-@im*other.km+@jm*other.re+@km*other.im,
|
377
|
+
@re*other.km+@im*other.jm-@jm*other.im+@km*other.re)
|
378
|
+
elsif other.kind_of?(Complex)
|
379
|
+
Quaternion(@re*other.real - @im*other.image,
|
380
|
+
@re*other.image + @im*other.real,
|
381
|
+
@jm*other.real + @km*other.image,
|
382
|
+
@km*other.real - @jm*other.image)
|
383
|
+
elsif Complex.generic?(other)
|
384
|
+
Quaternion(@re * other, @im * other, @jm * other, @km * other)
|
385
|
+
else x , y = other.coerce(self); x * y
|
386
|
+
end
|
387
|
+
end
|
388
|
+
def dot_product other
|
389
|
+
(self*other.conjugate).re
|
390
|
+
end
|
391
|
+
def cross_product other
|
392
|
+
-(self*other.conjugate).vector
|
393
|
+
end
|
394
|
+
def / other
|
395
|
+
if other.kind_of?(Quaternion); self*other.conjugate/other.abs2
|
396
|
+
elsif other.kind_of?(Complex); self*other.conjugate/other.abs2
|
397
|
+
elsif Complex.generic?(other);
|
398
|
+
Quaternion(@re/other, @im/other, @jm/other, @km/other )
|
399
|
+
else x, y = other.coerce(self); x / y
|
400
|
+
end
|
401
|
+
end
|
402
|
+
def rdiv other
|
403
|
+
# right division: q1/q2
|
404
|
+
self/other
|
405
|
+
end
|
406
|
+
def ldiv other
|
407
|
+
# left division: 1/q1 * q2
|
408
|
+
(self.conjugate)*other/self.abs2
|
409
|
+
end
|
410
|
+
def divmod other
|
411
|
+
# right divmod: q1=d*q2+m
|
412
|
+
d=self.rdiv(other).round; m=self-d*other; return d,m
|
413
|
+
end
|
414
|
+
def divmod_D4 other
|
415
|
+
# right divmod: q1=d*q2+m, d be D4
|
416
|
+
d=self.rdiv(other).round_D4; m=self-d*other; return d,m
|
417
|
+
end
|
418
|
+
def ldivmod other
|
419
|
+
# left divmod: q2=q1*d+m
|
420
|
+
d=self.ldiv(other).round; m=other-self*d; return d,m
|
421
|
+
end
|
422
|
+
def ldivmod_D4 other
|
423
|
+
# left divmod: q2=q1*d+m, d be D4
|
424
|
+
d=self.ldiv(other).round_D4; m=other-self*d; return d,m
|
425
|
+
end
|
426
|
+
def % other
|
427
|
+
# right mod
|
428
|
+
d,m=divmod(other); return m
|
429
|
+
end
|
430
|
+
def rmod other
|
431
|
+
# right mod(same as %)
|
432
|
+
d,m=divmod(other); return m
|
433
|
+
end
|
434
|
+
def rmod_D4 other
|
435
|
+
# right mod with D4
|
436
|
+
d,m=divmod_D4(other); return m
|
437
|
+
end
|
438
|
+
def lmod other
|
439
|
+
# left mod
|
440
|
+
d,m=ldivmod(other); return m
|
441
|
+
end
|
442
|
+
def lmod_D4 other
|
443
|
+
# left mod with D4
|
444
|
+
d,m=ldivmod_D4(other); return m
|
445
|
+
end
|
446
|
+
def gcd other
|
447
|
+
a=self; b=other
|
448
|
+
while true
|
449
|
+
if b==0 ; return a;end
|
450
|
+
a=a.rmod_D4(b)
|
451
|
+
if a==0 ; return b;end
|
452
|
+
b=a.lmod_D4(b)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
def orthogonal_split(o)
|
456
|
+
# [q1,q2]. q = q1 + q2 such that q1 parallel to o, and q2 orthogonal to o.
|
457
|
+
q1 = o * dot_product(o); q2=self-q1; return q1,q2
|
458
|
+
end
|
459
|
+
|
460
|
+
# Exponential and logarithmic functions
|
461
|
+
def exp
|
462
|
+
# e^(r+uv)=exp(r)(cos(v)+u*sin(v))
|
463
|
+
if is_real?; return Quaternion(Math::exp(@re)); end
|
464
|
+
vec=self.vector; v=vec.abs; u = vec/v;
|
465
|
+
Math::exp(@re)*(Math::cos(v)+u*Math::sin(v))
|
466
|
+
end
|
467
|
+
def log
|
468
|
+
# log(r+uv)=1/2 log(r^2+v^2)+u atan(v/r)
|
469
|
+
if is_real?;
|
470
|
+
if @re>=0; return Quaternion(Math::log(@re));
|
471
|
+
else return Quaternion(Math::log(-@re),Math::PI,0,0);
|
472
|
+
end
|
473
|
+
end
|
474
|
+
vec=self.vector; v=vec.abs; u = vec/v;
|
475
|
+
Math::log(self.abs2.to_f)/2+u*Math::atan2( v, @re)
|
476
|
+
end
|
477
|
+
def ** other
|
478
|
+
# q1^q2 = exp((log q1)*q2)
|
479
|
+
if other.kind_of?(Quaternion); ((self.log)*other).exp
|
480
|
+
elsif other.kind_of?(Complex); ((self.log)*other).exp
|
481
|
+
elsif other.kind_of?(Integer);
|
482
|
+
if other==0; return One;
|
483
|
+
elsif other>0;
|
484
|
+
x = self; q = x; n = other - 1
|
485
|
+
while n != 0
|
486
|
+
while (d, m = n.divmod(2); m == 0); x = x*x; n = d; end
|
487
|
+
q *= x; n -= 1
|
488
|
+
end
|
489
|
+
return q
|
490
|
+
else return self.inverse**(-other)
|
491
|
+
end
|
492
|
+
elsif Quaternion::generic?(other); ((self.log)*other).exp
|
493
|
+
else x, y = other.coerce(self); x ** y
|
494
|
+
end;
|
495
|
+
end
|
496
|
+
def sqrt; self**(0.5); end
|
497
|
+
def sinh; e=exp; return (e-e.inverse)/2; end
|
498
|
+
def cosh; e=exp; return (e+e.inverse)/2; end
|
499
|
+
def tanh; e=exp; e=e*e; return (e-1)/(e+1); end
|
500
|
+
# Trigonometric functions
|
501
|
+
def sin
|
502
|
+
# sin(r+uv)=sin r cosh v + u cos r sinh v
|
503
|
+
vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::sin(@re)); end
|
504
|
+
u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
|
505
|
+
(Math::sin(@re)*c+u*Math::cos(@re)*s)/2
|
506
|
+
end
|
507
|
+
def cos
|
508
|
+
# cos(r+uv)=cos r cosh v - u sin r sinh v
|
509
|
+
vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::cos(@re)); end
|
510
|
+
u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
|
511
|
+
(Math::cos(@re)*c-u*Math::sin(@re)*s)/2
|
512
|
+
end
|
513
|
+
def tan
|
514
|
+
vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::tan(@re)); end
|
515
|
+
u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
|
516
|
+
co=Math::cos(@re); si=Math::sin(@re); (si*c+u*co*s)/(co*c-u*si*s)
|
517
|
+
end
|
518
|
+
|
519
|
+
# Inverse trigonometric functions
|
520
|
+
def asin
|
521
|
+
# asin q = -u log(uq+sqrt(1-q^2))
|
522
|
+
q=self; u=unit_vector; -u*((u*q+(1-q*q).sqrt).log)
|
523
|
+
end
|
524
|
+
def acos
|
525
|
+
# acos q = -u log(q+sqrt(q^2-1))
|
526
|
+
q=self; u=unit_vector; -u*((q+(q*q-1).sqrt).log)
|
527
|
+
end
|
528
|
+
def atan
|
529
|
+
# atan q = u/2 log( (u+q)/(u-q) )
|
530
|
+
q=self; u=q.unit_vector; u*((u+q)/(u-q)).log/2
|
531
|
+
end
|
532
|
+
|
533
|
+
|
534
|
+
def hash; @re^@im^@jm^@km; end
|
535
|
+
|
536
|
+
def inspect
|
537
|
+
sprintf("Quaternion(%s,%s,%s,%s)",
|
538
|
+
@re.inspect, @im.inspect, @jm.inspect, @km.inspect)
|
539
|
+
end
|
540
|
+
def to_s
|
541
|
+
s=""
|
542
|
+
if @re!=0; s=@re.to_s; end
|
543
|
+
if @im!=0;
|
544
|
+
if s==""; s=sprintf("%si", @im);
|
545
|
+
else if @im>0; s=sprintf("%s+%si",s,@im); else s=sprintf("%s-%si",s,-@im); end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
if @jm!=0;
|
549
|
+
if s==""; s=sprintf("%sj", @jm);
|
550
|
+
else if @jm>0; s=sprintf("%s+%sj",s,@jm); else s=sprintf("%s-%sj",s,-@jm); end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
if @km!=0;
|
554
|
+
if s==""; s=sprintf("%sk", @km);
|
555
|
+
else if @km>0; s=sprintf("%s+%sk",s,@km); else s=sprintf("%s-%sk",s,-@km); end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
if s=="" ; s="0"; end;
|
559
|
+
return s
|
560
|
+
end
|
561
|
+
|
562
|
+
end # Quaternion
|