September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -32,14 +32,11 @@ Given the three quaternions and their components: <big>
And a wholly real number &nbsp; <big> <code> r = 7. </code> </big>
'''Note:''' ''The first formula below is invisible to the majority of browsers, including Chrome, IE/Edge, Safari, Opera etc. It may, subject to the installation of requisite fonts, prove visible in Firefox.''
Create functions &nbsp; (or classes) &nbsp; to perform simple maths with quaternions including computing:
# The norm of a quaternion: <br> <big> <code> <math> = \sqrt{ a^2 + b^2 + c^2 + d^2 } </math> </code> </big>
# The norm of a quaternion: <br><big><code><math>= \sqrt{a^2 + b^2 + c^2 + d^2}</math></code></big>
# The negative of a quaternion: <br> <big> <code> = (-a, -b, -c, -d)</code> </big>
# The conjugate of a quaternion: <br> <big> <code> = ( a, -b, -c, -d)</code> </big>
# Addition of a real number &nbsp; <big> <code> r </code> </big> &nbsp; and &nbsp; <big> <code> a </code> </big> &nbsp; quaternion &nbsp; <big> <code> q: </code> </big> <br> <big> <code> r + q = q + r = (a+r, b, c, d) </code> </big>
# Addition of a real number &nbsp; <big> <code> r </code> </big> &nbsp; and a quaternion &nbsp; <big> <code> q: </code> </big> <br> <big> <code> r + q = q + r = (a+r, b, c, d) </code> </big>
# Addition of two quaternions: <br> <big> <code> q<sub>1</sub> + q<sub>2</sub> = (a<sub>1</sub>+a<sub>2</sub>, b<sub>1</sub>+b<sub>2</sub>, c<sub>1</sub>+c<sub>2</sub>, d<sub>1</sub>+d<sub>2</sub>) </code> </big>
# Multiplication of a real number and a quaternion: <br> <big> <code> qr = rq = (ar, br, cr, dr) </code> </big>
# Multiplication of two quaternions &nbsp; <big> <code> q<sub>1</sub> </code> </big> &nbsp; and &nbsp; <big><code>q<sub>2</sub> </code> </big> &nbsp; is given by: <br> <big> <code> ( a<sub>1</sub>a<sub>2</sub> b<sub>1</sub>b<sub>2</sub> c<sub>1</sub>c<sub>2</sub> d<sub>1</sub>d<sub>2</sub>, </code> <br> <code> &nbsp; a<sub>1</sub>b<sub>2</sub> + b<sub>1</sub>a<sub>2</sub> + c<sub>1</sub>d<sub>2</sub> d<sub>1</sub>c<sub>2</sub>, </code> <br> <code> &nbsp; a<sub>1</sub>c<sub>2</sub> b<sub>1</sub>d<sub>2</sub> + c<sub>1</sub>a<sub>2</sub> + d<sub>1</sub>b<sub>2</sub>, </code> <br> <code> &nbsp; a<sub>1</sub>d<sub>2</sub> + b<sub>1</sub>c<sub>2</sub> c<sub>1</sub>b<sub>2</sub> + d<sub>1</sub>a<sub>2</sub> ) </code> </big>

View file

@ -0,0 +1,57 @@
// version 1.1.2
data class Quaternion(val a: Double, val b: Double, val c: Double, val d: Double) {
operator fun plus(other: Quaternion): Quaternion {
return Quaternion (this.a + other.a, this.b + other.b,
this.c + other.c, this.d + other.d)
}
operator fun plus(r: Double) = Quaternion(a + r, b, c, d)
operator fun times(other: Quaternion): Quaternion {
return Quaternion(
this.a * other.a - this.b * other.b - this.c * other.c - this.d * other.d,
this.a * other.b + this.b * other.a + this.c * other.d - this.d * other.c,
this.a * other.c - this.b * other.d + this.c * other.a + this.d * other.b,
this.a * other.d + this.b * other.c - this.c * other.b + this.d * other.a
)
}
operator fun times(r: Double) = Quaternion(a * r, b * r, c * r, d * r)
operator fun unaryMinus() = Quaternion(-a, -b, -c, -d)
fun conj() = Quaternion(a, -b, -c, -d)
fun norm() = Math.sqrt(a * a + b * b + c * c + d * d)
override fun toString() = "($a, $b, $c, $d)"
}
// extension functions for Double type
operator fun Double.plus(q: Quaternion) = q + this
operator fun Double.times(q: Quaternion) = q * this
fun main(args: Array<String>) {
val q = Quaternion(1.0, 2.0, 3.0, 4.0)
val q1 = Quaternion(2.0, 3.0, 4.0, 5.0)
val q2 = Quaternion(3.0, 4.0, 5.0, 6.0)
val r = 7.0
println("q = $q")
println("q1 = $q1")
println("q2 = $q2")
println("r = $r\n")
println("norm(q) = ${"%f".format(q.norm())}")
println("-q = ${-q}")
println("conj(q) = ${q.conj()}\n")
println("r + q = ${r + q}")
println("q + r = ${q + r}")
println("q1 + q2 = ${q1 + q2}\n")
println("r * q = ${r * q}")
println("q * r = ${q * r}")
val q3 = q1 * q2
val q4 = q2 * q1
println("q1 * q2 = $q3")
println("q2 * q1 = $q4\n")
println("q1 * q2 != q2 * q1 = ${q3 != q4}")
}

View file

@ -0,0 +1,150 @@
q = .quaternion~new(1, 2, 3, 4)
q1 = .quaternion~new(2, 3, 4, 5)
q2 = .quaternion~new(3, 4, 5, 6)
r = 7
say "q =" q
say "q1 =" q1
say "q2 =" q2
say "r =" r
say "norm(q) =" q~norm
say "-q =" (-q)
say "q* =" q~conjugate
say "q + r =" q + r
say "q1 + q2 =" q1 + q2
say "q * r =" q * r
q1q2 = q1 * q2
q2q1 = q2 * q1
say "q1 * q2 =" q1q2
say "q2 * q1 =" q2q1
say "q1 == q1 =" (q1 == q1)
say "q1q2 == q2q1 =" (q1q2 == q2q1)
::class quaternion
::method init
expose r i j k
use strict arg r, i = 0, j = 0, k = 0
-- quaternion instances are immutable, so these are
-- read only attributes
::attribute r GET
::attribute i GET
::attribute j GET
::attribute k GET
::method norm
expose r i j k
return rxcalcsqrt(r * r + i * i + j * j + k * k)
::method invert
expose r i j k
norm = self~norm
return self~class~new(r / norm, i / norm, j / norm, k / norm)
::method negative
expose r i j k
return self~class~new(-r, -i, -j, -k)
::method conjugate
expose r i j k
return self~class~new(r, -i, -j, -k)
::method add
expose r i j k
use strict arg other
if other~isa(.quaternion) then
return self~class~new(r + other~r, i + other~i, j + other~j, k + other~k)
else return self~class~new(r + other, i, j, k)
::method subtract
expose r i j k
use strict arg other
if other~isa(.quaternion) then
return self~class~new(r - other~r, i - other~i, j - other~j, k - other~k)
else return self~class~new(r - other, i, j, k)
::method times
expose r i j k
use strict arg other
if other~isa(.quaternion) then
return self~class~new(r * other~r - i * other~i - j * other~j - k * other~k, -
r * other~i + i * other~r + j * other~k - k * other~j, -
r * other~j - i * other~k + j * other~r + k * other~i, -
r * other~k + i * other~j - j * other~i + k * other~r)
else return self~class~new(r * other, i * other, j * other, k * other)
::method divide
use strict arg other
-- this is easier if everything is a quaternion
if \other~isA(.quaternion) then other = .quaternion~new(other)
-- division is multiplication with the inversion
return self * other~invert
::method "=="
expose r i j k
use strict arg other
if \other~isa(.quaternion) then return .false
-- Note: these are numeric comparisons, so we're using the "="
-- method so those are handled correctly
return r = other~r & i = other~i & j = other~j & k = other~k
::method "\=="
use strict arg other
return \self~"\=="(other)
::method "="
-- this is equivalent of "=="
forward message("==")
::method "\="
-- this is equivalent of "\=="
forward message("\==")
::method "<>"
-- this is equivalent of "\=="
forward message("\==")
::method "><"
-- this is equivalent of "\=="
forward message("\==")
-- some operator overrides -- these only work if the left-hand-side of the
-- subexpression is a quaternion
::method "*"
forward message("TIMES")
::method "/"
forward message("DIVIDE")
::method "-"
-- need to check if this is a prefix minus or a subtract
if arg() == 0 then
forward message("NEGATIVE")
else
forward message("SUBTRACT")
::method "+"
-- need to check if this is a prefix plus or an addition
if arg() == 0 then
return self -- we can return this copy since it is immutable
else
forward message("ADD")
::method string
expose r i j k
return r self~formatnumber(i)"i" self~formatnumber(j)"j" self~formatnumber(k)"k"
::method formatnumber private
use arg value
if value > 0 then return "+" value
else return "-" value~abs
-- override hashcode for collection class hash uses
::method hashCode
expose r i j k
return r~hashcode~bitxor(i~hashcode)~bitxor(j~hashcode)~bitxor(k~hashcode)
::requires rxmath LIBRARY

View file

@ -1,58 +0,0 @@
require 'matrix' # For Vector#norm
class Quaternion
def initialize(*parts)
raise "Invalid number of quaternion parts" unless parts.length == 4
@parts, @vector = parts, Vector[*parts]
end
def to_a; @parts; end
def to_s; "Quaternion#{to_a.to_s}" end
def complex_parts; [Complex(*to_a[0..1]), Complex(*to_a[2..3])]; end
def zip(other); to_a.zip(other.to_a); end
def real; @parts.first; end
def imag; @parts[1..3]; end
def conj; Quaternion.new(real, *imag.map(&:-@)); end
def norm; @vector.norm; end # Or: Math.sqrt(to_a.reduce { |sum, e| sum + e**2 }) # In Rails: Math.sqrt(to_a.sum { e**2 })
def ==(other); to_a == other.to_a end
def -@; Quaternion.new(*to_a.map(&:-@)); end
def -(other); self + -other; end
def +(other)
case other
when Numeric
Quaternion.new(real + other, *imag)
when Quaternion
Quaternion.new(*zip(other).map { |x,y| x + y }) # In Rails: zip(other).map(&:sum) # Or: (vector + other.vector).to_a
end
end
def *(other)
case other
when Numeric
Quaternion.new(*to_a.map { |x| x * other }) # Or: (vector * other).to_a
when Quaternion
# Multiplication of quaternions in C x C space. See "Cayley-Dickson construction".
a, b, c, d = *complex_parts, *other.complex_parts
x, y = a*c - d.conj*b, a*d + b*c.conj
Quaternion.new(x.real, x.imag, y.real, y.imag)
end
end
# Coerce is called by Ruby to return a compatible type/receiver when the called method/operation does not accept a Quaternion
def coerce(other)
case other
when Numeric then [Scalar.new(other), self]
else raise TypeError, "#{other.class} can't be coerced into #{self.class}"
end
end
class Scalar
def initialize(val); @val = val; end
def +(other); other + @val; end
def *(other); other * @val; end
def -(other); Quaternion.new(@val, 0, 0, 0) - other; end
end
end

View file

@ -1,38 +0,0 @@
irb(main):001:0> require 'quaternion'
=> true
irb(main):002:0> q = Quaternion.new(1,2,3,4)
=> Quaternion[1, 2, 3, 4]
irb(main):003:0> q1 = Quaternion.new(2,3,4,5)
=> Quaternion[2, 3, 4, 5]
irb(main):004:0> q2 = Quaternion.new(3,4,5,6)
=> Quaternion[3, 4, 5, 6]
irb(main):005:0> r = 7
=> 7
irb(main):006:0> q.norm
=> 5.477225575051661
irb(main):007:0> q1.norm
=> 7.3484692283495345
irb(main):008:0> q2.norm
=> 9.273618495495704
irb(main):009:0> -q
=> Quaternion[-1, -2, -3, -4]
irb(main):010:0> q.conj
=> Quaternion[1, -2, -3, -4]
irb(main):011:0> q1 + q2
=> Quaternion[5, 7, 9, 11]
irb(main):012:0> q2 + q1
=> Quaternion[5, 7, 9, 11]
irb(main):013:0> q + r
=> Quaternion[8, 2, 3, 4]
irb(main):014:0> r + q
=> Quaternion[8, 2, 3, 4]
irb(main):015:0> q * r
=> Quaternion[7, 14, 21, 28]
irb(main):016:0> r * q
=> Quaternion[7, 14, 21, 28]
irb(main):017:0> q1 * q2
=> Quaternion[-56, 16, 24, 26]
irb(main):018:0> q2 * q1
=> Quaternion[-56, 18, 20, 28]
irb(main):019:0> q1 * q2 != q2 * q1
=> true

View file

@ -0,0 +1,53 @@
class Quat{
fcn init(real=0,i1=0,i2=0,i3=0){
var [const] vector= // Quat(r,i,j,k) or Quat( (r,i,j,k) )
(if(List.isType(real)) real else vm.arglist).apply("toFloat");
var r,i,j,k; r,i,j,k=vector; // duplicate data for ease of coding
var [const] // properties: This is one way to do it
norm2=vector.apply("pow",2).sum(0.0), // Norm squared
abs=norm2.sqrt(), // Norm
arg=(r/abs()).acos(), // Theta !!!this may be incorrect...
;
}
fcn toString { String("[",vector.concat(","),"]") }
var [const proxy] // properties that need calculation (or are recursive)
conj =fcn{ Quat(r,-i,-j,-k) }, // Conjugate
recip =fcn{ n2:=norm2; Quat(r/n2,-i/n2,-j/n2,-k/n2) },// Reciprocal
pureim =fcn{ Quat(0, i, j, k) }, // Pure imagery
versor =fcn{ self / abs; }, // Unit versor
iversor=fcn{ pureim / pureim.abs; }, // Unit versor of imagery part
;
fcn __opEQ(z) { r == z.r and i == z.i and j == z.j and k == z.k }
fcn __opNEQ(z){ (not (self==z)) }
fcn __opNegate{ Quat(-r, -i, -j, -k) }
fcn __opAdd(z){
if (Quat.isInstanceOf(z)) Quat(vector.zipWith('+,z.vector));
else Quat(r+z,i,j,k);
}
fcn __opSub(z){
if (Quat.isInstanceOf(z)) Quat(vector.zipWith('-,z.vector));
else Quat(r-z,vector.xplode(1)); // same as above
}
fcn __opMul(z){
if (Quat.isInstanceOf(z)){
Quat(r*z.r - i*z.i - j*z.j - k*z.k,
r*z.i + i*z.r + j*z.k - k*z.j,
r*z.j - i*z.k + j*z.r + k*z.i,
r*z.k + i*z.j - j*z.i + k*z.r);
}
else Quat(vector.apply('*(z)));
}
fcn __opDiv(z){
if (Quat.isInstanceOf(z)) self*z.recip;
else Quat(r/z,i/z,j/z,k/z);
}
fcn pow(r){ exp(r*iversor*arg)*abs.pow(r) } // Power function
fcn log{ iversor*(r / abs).acos() + abs.log() }
fcn exp{ // e^q
inorm:=pureim.abs;
(iversor*inorm.sin() + inorm.cos()) * r.exp();
}
}

View file

@ -0,0 +1,39 @@
// Demo code
r:=7;
q:=Quat(2,3,4,5); q1:=Quat(2,3,4,5); q2:=Quat(3,4,5,6);
println("1. norm: q.abs: ", q.abs);
println("2. -q: ", -q);
println("3. conjugate: q.conj: ", q.conj);
println("4. Quat(r) + q: ", Quat(r) + q);
println(" q + r: ", q + r);
println("5. q1 + q2: ", q1 + q2);
println("6. Quat(r) * q: ", Quat(r) * q);
println(" q * r: ", q * r);
println("7. q1 * q2: ", q1 * q2);
println(" q2 * q1: ", q2 * q1);
println("8. q1 * q2 == q2 * q1 ? ", q1 * q2 == q2 * q1);
i:=Quat(0,1); j:=Quat(0,0,1); k:=Quat(0,0,0,1);
println("9.1 i * i: ", i * i);
println(" J * j: ", j * j);
println(" k * k: ", k * k);
println(" i * j * k: ", i * j * k);
println("9.2 q1 / q2: ", q1 / q2);
println("9.3 q1 / q2 * q2: ", q1 / q2 * q2);
println(" q2 * q1 / q2: ", q2 * q1 / q2);
println("9.4 (i * pi).exp(): ", (i * (0.0).pi).exp());
println(" exp(j * pi): ", (j * (0.0).pi).exp());
println(" exp(k * pi): ", (k * (0.0).pi).exp());
println(" q.exp(): ", q.exp());
println(" q.log(): ", q.log());
println(" q.log().exp(): ", q.log().exp());
println(" q.exp().log(): ", q.exp().log());
s:=q.exp().log();
println("9.5 let s=q.exp().log(): ", s);
println(" s.exp(): ", s.exp());
println(" s.log(): ", s.log());
println(" s.log().exp(): ", s.log().exp());
println(" s.exp().log(): ", s.exp().log());