Class: Quaternion

Inherits:
Numeric show all
Defined in:
lib/carat-dev/quaternion/quaternion.rb

Constant Summary collapse

Zero =
Quaternion(0)
One =
Quaternion(1)
I =
Quaternion(0,1)
J =
Quaternion(0,0,1)
K =
Quaternion(0,0,0,1)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#imObject (readonly)

Returns the value of attribute im.



207
208
209
# File 'lib/carat-dev/quaternion/quaternion.rb', line 207

def im
  @im
end

#jmObject (readonly)

Returns the value of attribute jm.



208
209
210
# File 'lib/carat-dev/quaternion/quaternion.rb', line 208

def jm
  @jm
end

#kmObject (readonly)

Returns the value of attribute km.



209
210
211
# File 'lib/carat-dev/quaternion/quaternion.rb', line 209

def km
  @km
end

#reObject (readonly)

Returns the value of attribute re.



206
207
208
# File 'lib/carat-dev/quaternion/quaternion.rb', line 206

def re
  @re
end

Class Method Details

.generic?(other) ⇒ Boolean

Returns:

  • (Boolean)


216
217
218
# File 'lib/carat-dev/quaternion/quaternion.rb', line 216

def Quaternion::generic?(other)
	return (other.kind_of?(Complex) or Complex.generic?(other));
end

.polar(m, t1 = 0, t2 = 0, t3 = 0) ⇒ Object



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/carat-dev/quaternion/quaternion.rb', line 234

def Quaternion::polar(m,t1=0,t2=0,t3=0)
	# q=
	#   m*cos(t1)
	#   +m*sin(t1)cos(t2)i
	#   +m*sin(t1)sin(t2)cos(t3)j
	#   +m*sin(t1)sin(t2)sin(t3)k
	# m is known as the magnitude,
	# t1 is the amplitude(or angle) of the quaternion,
	# t2 and t3 are the latitude (or co-latitude) and longitude respectively.
	if m.kind_of?(Array) and (m.size==4); t1=m[1]; t2=m[2]; t3=m[3]; m=m[0]; end;
	s=m
	r_part=s*Math.cos(t1); s=s*Math.sin(t1)
	i_part=s*Math.cos(t2); s=s*Math.sin(t2)
	j_part=s*Math.cos(t3); k_part=s*Math.sin(t3)
	new(r_part, i_part, j_part, k_part)
end

.rotation(v, t) ⇒ Object



294
295
296
297
# File 'lib/carat-dev/quaternion/quaternion.rb', line 294

def Quaternion::rotation(v,t)
	# t-rotatin along the 3-D vector v
	(Quaternion::vector(v).unit_vector) * Math::sin(t/2) + Math::cos(t/2)
end

.vector(v) ⇒ Object



284
285
286
287
# File 'lib/carat-dev/quaternion/quaternion.rb', line 284

def Quaternion::vector(v)
	# 3-D vector v=[x,y,z]
	Quaternion(0,v[0],v[1],v[2])
end

Instance Method Details

#%(other) ⇒ Object



393
394
395
396
# File 'lib/carat-dev/quaternion/quaternion.rb', line 393

def % other
	# right mod
	d,m=divmod(other); return m
end

#*(other) ⇒ Object



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/carat-dev/quaternion/quaternion.rb', line 339

def * (other)
	if other.kind_of?(Quaternion)
		Quaternion(@re*other.re-@im*other.im-@jm*other.jm-@km*other.km,
			   @re*other.im+@im*other.re+@jm*other.km-@km*other.jm,
			   @re*other.jm-@im*other.km+@jm*other.re+@km*other.im,
			   @re*other.km+@im*other.jm-@jm*other.im+@km*other.re)
	elsif other.kind_of?(Complex)
		Quaternion(@re*other.real - @im*other.image,
			   @re*other.image + @im*other.real,
			   @jm*other.real + @km*other.image,
			   @km*other.real - @jm*other.image)
	elsif Complex.generic?(other)
		Quaternion(@re * other, @im * other, @jm * other, @km * other)
	else x , y = other.coerce(self); x * y
	end
end

#**(other) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/carat-dev/quaternion/quaternion.rb', line 444

def ** other
	# q1^q2 = exp((log q1)*q2)
	if other.kind_of?(Quaternion); ((self.log)*other).exp
	elsif other.kind_of?(Complex); ((self.log)*other).exp
	elsif other.kind_of?(Integer);
		if other==0; return One;
		elsif other>0;
			x = self; q = x; n = other - 1
			while n != 0
				while (d, m = n.divmod(2); m == 0); x = x*x; n = d; end
				q *= x; n -= 1
			end
			return q
		else return self.inverse**(-other)
		end
	elsif Quaternion::generic?(other); ((self.log)*other).exp
	else x, y = other.coerce(self); x ** y
	end;
end

#+(other) ⇒ Object



319
320
321
322
323
324
325
326
327
328
# File 'lib/carat-dev/quaternion/quaternion.rb', line 319

def + (other)
	if other.kind_of?(Quaternion)
		Quaternion(@re+other.re,@im+other.im,@jm+other.jm,@km+other.km)
	elsif other.kind_of?(Complex)
		Quaternion(@re+other.real,@im+other.image, @jm, @km)
	elsif Complex.generic?(other)
		Quaternion(@re+other.real,@im, @jm, @km)
	else x , y = other.coerce(self); x + y
	end
end

#-(other) ⇒ Object



329
330
331
332
333
334
335
336
337
338
# File 'lib/carat-dev/quaternion/quaternion.rb', line 329

def - (other)
	if other.kind_of?(Quaternion)
		Quaternion(@re-other.re,@im-other.im,@jm-other.jm,@km-other.km)
	elsif other.kind_of?(Complex)
		Quaternion(@re-other.real,@im-other.image, @jm, @km)
	elsif Complex.generic?(other)
		Quaternion(@re-other.real,@im, @jm, @km)
	else x , y = other.coerce(self); x - y
	end
end

#/(other) ⇒ Object



361
362
363
364
365
366
367
368
# File 'lib/carat-dev/quaternion/quaternion.rb', line 361

def / other
	if other.kind_of?(Quaternion); self*other.conjugate/other.abs2
	elsif other.kind_of?(Complex); self*other.conjugate/other.abs2
	elsif Complex.generic?(other); 
		Quaternion(@re/other, @im/other, @jm/other, @km/other )
	else x, y = other.coerce(self); x / y
	end
end

#<=>(other) ⇒ Object



308
# File 'lib/carat-dev/quaternion/quaternion.rb', line 308

def <=> (other); self.abs <=> other.abs; end

#==(other) ⇒ Object



309
310
311
312
313
314
315
316
317
318
# File 'lib/carat-dev/quaternion/quaternion.rb', line 309

def == (other)
	if other.kind_of?(Quaternion)
		return (@re==other.re and @im==other.im and @jm==other.jm and @km==other.km)
	elsif other.kind_of?(Complex)
		@re==other.real and @im==other.image and @jm==0 and @km==0
	elsif Complex.generic?(other)
		@re==other and @im==0 and @jm==0 and @km==0
	else x , y = other.coerce(self); x == y
	end
end

#absObject



272
# File 'lib/carat-dev/quaternion/quaternion.rb', line 272

def abs; Math.sqrt((@re*@re+@im*@im+@jm*@jm+@km*@km).to_f); end

#abs2Object



271
# File 'lib/carat-dev/quaternion/quaternion.rb', line 271

def abs2; return @re*@re+@im*@im+@jm*@jm+@km*@km; end

#acosObject



491
492
493
494
# File 'lib/carat-dev/quaternion/quaternion.rb', line 491

def acos
	# acos q = -u log(q+sqrt(q^2-1))
	q=self; u=unit_vector; -u*((q+(q*q-1).sqrt).log)
end

#amplitudeObject



250
# File 'lib/carat-dev/quaternion/quaternion.rb', line 250

def amplitude; Math.atan2(Math.sqrt((@im*@im+@jm*@jm+@km*@km).to_f),@re.to_f); end

#arg1Object



253
# File 'lib/carat-dev/quaternion/quaternion.rb', line 253

def arg1; return amplitude; end

#arg2Object



254
# File 'lib/carat-dev/quaternion/quaternion.rb', line 254

def arg2; return latitude; end

#arg3Object



255
# File 'lib/carat-dev/quaternion/quaternion.rb', line 255

def arg3; return longitude; end

#asinObject

Inverse trigonometric functions



487
488
489
490
# File 'lib/carat-dev/quaternion/quaternion.rb', line 487

def asin
	# asin q = -u log(uq+sqrt(1-q^2))
	q=self; u=unit_vector; -u*((u*q+(1-q*q).sqrt).log)
end

#atanObject



495
496
497
498
# File 'lib/carat-dev/quaternion/quaternion.rb', line 495

def atan
	# atan q = u/2 log( (u+q)/(u-q)  )
	q=self; u=q.unit_vector; u*((u+q)/(u-q)).log/2
end

#coerce(other) ⇒ Object

Arithmetic



302
303
304
305
306
307
# File 'lib/carat-dev/quaternion/quaternion.rb', line 302

def coerce(other)
	if other.kind_of?(Complex); return Quaternion(other), self
	elsif Complex::generic?(other); return Quaternion(other), self
	else super
	end
end

#conjugateObject



274
# File 'lib/carat-dev/quaternion/quaternion.rb', line 274

def conjugate; Quaternion(@re,-@im,-@jm,-@km); end

#cosObject



474
475
476
477
478
479
# File 'lib/carat-dev/quaternion/quaternion.rb', line 474

def cos
	# cos(r+uv)=cos r cosh v - u sin r sinh v
	vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::cos(@re)); end
	u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
	(Math::cos(@re)*c-u*Math::sin(@re)*s)/2
end

#coshObject



465
# File 'lib/carat-dev/quaternion/quaternion.rb', line 465

def cosh; e=exp; return (e+e.inverse)/2; end

#cross_product(other) ⇒ Object



358
359
360
# File 'lib/carat-dev/quaternion/quaternion.rb', line 358

def cross_product other
	-(self*other.conjugate).vector
end

#divmod(other) ⇒ Object



377
378
379
380
# File 'lib/carat-dev/quaternion/quaternion.rb', line 377

def divmod other
	# right divmod: q1=d*q2+m
	d=self.rdiv(other).round; m=self-d*other; return d,m
end

#divmod_D4(other) ⇒ Object



381
382
383
384
# File 'lib/carat-dev/quaternion/quaternion.rb', line 381

def divmod_D4 other
	# right divmod: q1=d*q2+m, d be D4
	d=self.rdiv(other).round_D4; m=self-d*other; return d,m
end

#dot_product(other) ⇒ Object



355
356
357
# File 'lib/carat-dev/quaternion/quaternion.rb', line 355

def dot_product other
	(self*other.conjugate).re
end

#expObject

Exponential and logarithmic functions



428
429
430
431
432
433
# File 'lib/carat-dev/quaternion/quaternion.rb', line 428

def exp
	# e^(r+uv)=exp(r)(cos(v)+u*sin(v))
	if is_real?; return Quaternion(Math::exp(@re)); end
	vec=self.vector; v=vec.abs; u = vec/v;
	Math::exp(@re)*(Math::cos(v)+u*Math::sin(v))
end

#gcd(other) ⇒ Object



413
414
415
416
417
418
419
420
421
# File 'lib/carat-dev/quaternion/quaternion.rb', line 413

def gcd other
	a=self; b=other
	while true
		if b==0 ; return a;end
		a=a.rmod_D4(b)
		if a==0 ; return b;end
		b=a.lmod_D4(b)
	end
end

#hashObject



501
# File 'lib/carat-dev/quaternion/quaternion.rb', line 501

def hash; @re^@im^@jm^@km; end

#imageObject



210
# File 'lib/carat-dev/quaternion/quaternion.rb', line 210

def image; return @im; end

#inspectObject



503
504
505
506
# File 'lib/carat-dev/quaternion/quaternion.rb', line 503

def inspect
	sprintf("Quaternion(%s,%s,%s,%s)",
			@re.inspect, @im.inspect, @jm.inspect, @km.inspect)
end

#inverseObject



275
# File 'lib/carat-dev/quaternion/quaternion.rb', line 275

def inverse; conjugate/abs2; end

#is_complex?Boolean

Returns:

  • (Boolean)


278
# File 'lib/carat-dev/quaternion/quaternion.rb', line 278

def is_complex?; @jm==0 and @km==0; end

#is_quaternion?Boolean

Returns:

  • (Boolean)


279
# File 'lib/carat-dev/quaternion/quaternion.rb', line 279

def is_quaternion?; not(is_complex?); end

#is_real?Boolean

Returns:

  • (Boolean)


277
# File 'lib/carat-dev/quaternion/quaternion.rb', line 277

def is_real?; @im==0 and @jm==0 and @km==0; end

#is_unit_vector?Boolean

Returns:

  • (Boolean)


293
# File 'lib/carat-dev/quaternion/quaternion.rb', line 293

def is_unit_vector?; @re==0 and abs2==1; end

#is_vector?Boolean

Returns:

  • (Boolean)


282
# File 'lib/carat-dev/quaternion/quaternion.rb', line 282

def is_vector?; @re==0; end

#latitudeObject



251
# File 'lib/carat-dev/quaternion/quaternion.rb', line 251

def latitude; Math.atan2(Math.sqrt((@jm*@jm+@km*@km).to_f),@im.to_f); end

#ldiv(other) ⇒ Object



373
374
375
376
# File 'lib/carat-dev/quaternion/quaternion.rb', line 373

def ldiv other
	# left division: 1/q1 * q2
	(self.conjugate)*other/self.abs2
end

#ldivmod(other) ⇒ Object



385
386
387
388
# File 'lib/carat-dev/quaternion/quaternion.rb', line 385

def ldivmod other
	# left divmod: q2=q1*d+m
	d=self.ldiv(other).round; m=other-self*d; return d,m
end

#ldivmod_D4(other) ⇒ Object



389
390
391
392
# File 'lib/carat-dev/quaternion/quaternion.rb', line 389

def ldivmod_D4 other
	# left divmod: q2=q1*d+m, d be D4
	d=self.ldiv(other).round_D4; m=other-self*d; return d,m
end

#lmod(other) ⇒ Object



405
406
407
408
# File 'lib/carat-dev/quaternion/quaternion.rb', line 405

def lmod other
	# left mod
	d,m=ldivmod(other); return m
end

#lmod_D4(other) ⇒ Object



409
410
411
412
# File 'lib/carat-dev/quaternion/quaternion.rb', line 409

def lmod_D4 other
	# left mod with D4
	d,m=ldivmod_D4(other); return m
end

#logObject



434
435
436
437
438
439
440
441
442
443
# File 'lib/carat-dev/quaternion/quaternion.rb', line 434

def log
	# log(r+uv)=1/2 log(r^2+v^2)+u atan(v/r)
	if is_real?;
		if @re>=0; return Quaternion(Math::log(@re));
		else return Quaternion(Math::log(-@re),Math::PI,0,0);
		end
	end
	vec=self.vector; v=vec.abs; u = vec/v;
	Math::log(self.abs2.to_f)/2+u*Math::atan2( v, @re)
end

#longitudeObject



252
# File 'lib/carat-dev/quaternion/quaternion.rb', line 252

def longitude; Math.atan2( @km.to_f, @jm.to_f); end

#magnitudeObject



273
# File 'lib/carat-dev/quaternion/quaternion.rb', line 273

def magnitude; return abs; end

#orthogonal_split(o) ⇒ Object



422
423
424
425
# File 'lib/carat-dev/quaternion/quaternion.rb', line 422

def orthogonal_split(o)
	# [q1,q2]. q = q1 + q2 such that q1 parallel to o, and q2 orthogonal to o.
	q1 = o * dot_product(o); q2=self-q1; return q1,q2
end

#polarObject



256
# File 'lib/carat-dev/quaternion/quaternion.rb', line 256

def polar; [magnitude, amplitude, latitude, longitude]; end

#rdiv(other) ⇒ Object



369
370
371
372
# File 'lib/carat-dev/quaternion/quaternion.rb', line 369

def rdiv other
	# right division: q1/q2
	self/other
end

#realObject



212
# File 'lib/carat-dev/quaternion/quaternion.rb', line 212

def real; return @re; end

#real_partObject



211
# File 'lib/carat-dev/quaternion/quaternion.rb', line 211

def real_part; return @re; end

#rmod(other) ⇒ Object



397
398
399
400
# File 'lib/carat-dev/quaternion/quaternion.rb', line 397

def rmod other
	# right mod(same as %)
	d,m=divmod(other); return m
end

#rmod_D4(other) ⇒ Object



401
402
403
404
# File 'lib/carat-dev/quaternion/quaternion.rb', line 401

def rmod_D4 other
	# right mod with D4
	d,m=divmod_D4(other); return m
end

#rotate(r) ⇒ Object



298
# File 'lib/carat-dev/quaternion/quaternion.rb', line 298

def rotate(r); r * self * r.conjugate / r.abs2; end

#rotate_angleObject



299
# File 'lib/carat-dev/quaternion/quaternion.rb', line 299

def rotate_angle; amplitude/2; end

#roundObject



258
# File 'lib/carat-dev/quaternion/quaternion.rb', line 258

def round; Quaternion(@re.round,@im.round,@jm.round,@km.round);end

#round_D4Object



259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/carat-dev/quaternion/quaternion.rb', line 259

def round_D4
	# round to D4 lattice
	r1=@re.round; a1=@im.round; b1=@jm.round; c1=@km.round;
	q1=Quaternion(r1,a1,b1,c1); d1=(q1-self).abs2
	if d1<=1/4; return q1; end
	if @re<r1; r2=r1-1/2; else r2=r1+1/2; end
	if @im<r1; a2=a1-1/2; else a2=a1+1/2; end
	if @jm<r1; b2=b1-1/2; else b2=b1+1/2; end
	if @km<r1; c2=c1-1/2; else c2=c1+1/2; end
	q2=Quaternion(r2,a2,b2,c2); d2=(q2-self).abs2
	if d1<=d2; return q1; else return q2; end
end

#sinObject

Trigonometric functions



468
469
470
471
472
473
# File 'lib/carat-dev/quaternion/quaternion.rb', line 468

def sin
	# sin(r+uv)=sin r cosh v + u cos r sinh v
	vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::sin(@re)); end
	u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
	(Math::sin(@re)*c+u*Math::cos(@re)*s)/2
end

#sinhObject



464
# File 'lib/carat-dev/quaternion/quaternion.rb', line 464

def sinh; e=exp; return (e-e.inverse)/2; end

#sqrtObject



463
# File 'lib/carat-dev/quaternion/quaternion.rb', line 463

def sqrt; self**(0.5); end

#tanObject



480
481
482
483
484
# File 'lib/carat-dev/quaternion/quaternion.rb', line 480

def tan
	vec=self.vector; v=vec.abs; if v==0; return Quaternion(Math::tan(@re)); end
	u = vec/v; e=Math::exp(v); er=1/e; c=e+er; s=e-er
	co=Math::cos(@re); si=Math::sin(@re);  (si*c+u*co*s)/(co*c-u*si*s)
end

#tanhObject



466
# File 'lib/carat-dev/quaternion/quaternion.rb', line 466

def tanh; e=exp; e=e*e; return (e-1)/(e+1); end

#to_aObject



215
# File 'lib/carat-dev/quaternion/quaternion.rb', line 215

def to_a; return [@re, @im, @jm, @km]; end

#to_cObject



213
# File 'lib/carat-dev/quaternion/quaternion.rb', line 213

def to_c; return Complex(@re,@im); end

#to_c2Object



214
# File 'lib/carat-dev/quaternion/quaternion.rb', line 214

def to_c2; return Complex(@jm,@km); end

#to_sObject



507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/carat-dev/quaternion/quaternion.rb', line 507

def to_s
	s=""
	if @re!=0; s=@re.to_s; end
	if @im!=0; 
		if s==""; s=sprintf("%si", @im);
		else if @im>0; s=sprintf("%s+%si",s,@im); else s=sprintf("%s-%si",s,-@im); end
		end
	end
	if @jm!=0; 
		if s==""; s=sprintf("%sj", @jm); 
		else if @jm>0; s=sprintf("%s+%sj",s,@jm); else s=sprintf("%s-%sj",s,-@jm); end
		end
	end
	if @km!=0; 
		if s==""; s=sprintf("%sk", @km);
		else if @km>0; s=sprintf("%s+%sk",s,@km); else s=sprintf("%s-%sk",s,-@km); end
		end
	end
	if s=="" ; s="0"; end;
	return s
end

#to_vObject



283
# File 'lib/carat-dev/quaternion/quaternion.rb', line 283

def to_v; return [@im, @jm, @km]; end

#unit_vectorObject



288
289
290
291
292
# File 'lib/carat-dev/quaternion/quaternion.rb', line 288

def unit_vector
	if is_real?; return Quaternion(0,1); end
	m=Math::sqrt((@im*@im+@jm*@jm+@km*@km).to_f)
	Quaternion(0,@im/m,@jm/m,@km/m);
end

#vectorObject



281
# File 'lib/carat-dev/quaternion/quaternion.rb', line 281

def vector; Quaternion(0,@im,@jm,@km); end