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.
Files changed (56) hide show
  1. data/CHANGES +7 -0
  2. data/COPYING +344 -0
  3. data/README +110 -0
  4. data/lib/stick/constants.rb +3 -0
  5. data/lib/stick/constants/cgs.rb +151 -0
  6. data/lib/stick/constants/mks.rb +158 -0
  7. data/lib/stick/constants/number.rb +33 -0
  8. data/lib/stick/constants/typeless_cgs.rb +141 -0
  9. data/lib/stick/constants/typeless_mks.rb +142 -0
  10. data/lib/stick/currency.rb +8 -0
  11. data/lib/stick/mapcar.rb +61 -0
  12. data/lib/stick/matrix.rb +1022 -0
  13. data/lib/stick/quaternion.rb +562 -0
  14. data/lib/stick/times.rb +441 -0
  15. data/lib/stick/units.rb +112 -0
  16. data/lib/stick/units/base.rb +980 -0
  17. data/lib/stick/units/currency.rb +159 -0
  18. data/lib/stick/units/data/binary/base.rb +4 -0
  19. data/lib/stick/units/data/cex.rb +5 -0
  20. data/lib/stick/units/data/currency-default.rb +5 -0
  21. data/lib/stick/units/data/currency-standard.rb +2 -0
  22. data/lib/stick/units/data/currency/base.rb +89 -0
  23. data/lib/stick/units/data/iec.rb +5 -0
  24. data/lib/stick/units/data/iec_binary/base.rb +6 -0
  25. data/lib/stick/units/data/si.rb +7 -0
  26. data/lib/stick/units/data/si/base.rb +9 -0
  27. data/lib/stick/units/data/si/derived.rb +26 -0
  28. data/lib/stick/units/data/si/extra.rb +22 -0
  29. data/lib/stick/units/data/uk.rb +10 -0
  30. data/lib/stick/units/data/uk/base.rb +22 -0
  31. data/lib/stick/units/data/units-default.rb +11 -0
  32. data/lib/stick/units/data/units-standard.rb +5 -0
  33. data/lib/stick/units/data/us.rb +10 -0
  34. data/lib/stick/units/data/us/base.rb +23 -0
  35. data/lib/stick/units/data/xmethods.rb +5 -0
  36. data/lib/stick/units/data/xmethods/cached.rb +84 -0
  37. data/lib/stick/units/data/xmethods/mapping.rb +87 -0
  38. data/lib/stick/units/loaders.rb +98 -0
  39. data/lib/stick/units/units.rb +109 -0
  40. data/meta/MANIFEST +76 -0
  41. data/meta/ROLLRC +2 -0
  42. data/meta/icli.yaml +16 -0
  43. data/meta/project.yaml +18 -0
  44. data/task/clobber/package +10 -0
  45. data/task/publish +57 -0
  46. data/task/release +10 -0
  47. data/task/setup +1616 -0
  48. data/task/test +25 -0
  49. data/test/spec_matrix.rb +342 -0
  50. data/test/test_currency.rb +26 -0
  51. data/test/test_matrix.rb +359 -0
  52. data/test/test_units.rb +205 -0
  53. data/work/TODO +20 -0
  54. data/work/bytes.rb +231 -0
  55. data/work/multipliers.rb +195 -0
  56. 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