a first attempt to gather from all these most helpful contributions
There is a groundswell of support for a well-specified API to which different implementations can conform. Here is a first take on the least required. For your comments, improvements:
four conventions for brevity,
const Q = Quaternion
- quaternion elements are as named fields:
s, i, j, k
-
T<:Number
, so T
could be Number
, Real
or Float64
ā¦
-
where {T}
is omitted
The API autodefines
sections are the default (fallback) definitions to be used where the API client does not provide specialized implementations.
imports
import Base: abs, abs2, conj, inv, +, -, *, /, \
import LinearAlgebra: normalize
Constructor
Q(s::T, i::T=zero(T), j::T=zero(T), k::T=zero(T))
API autodefines
Q(; s=0, i=0, j=0, k=0) = Q(promote(s,i,j,k)...)
Q(sijk::NTuple{4,T}) = Q(sijk...)
Q(s::T, ijk::NTuple{3,T}) = Q(s,ijk...)
Q(s, ijk::NTuple{3,T}) = Q(promote(s,ijk...))
Selectors
scalar(q::Q) = q.s
bivector(q::Q) = [q.i, q.j, q.k]
fieldvalues(q::Q) = (q.s, q.i, q.j, q.k)
bivecvalues(q::Q) = (q.i, q.j, q.k)
API autodefines
abs2(q::Q) = sum(fieldvalues(q).^2)
abs(q::Q) = sqrt(abs2(q))
pure(q::Q{T}) = Q(zero{T}, bivecvalues(q))
conj(q::Q) = Q(scalar(q), (-).(bivecvalues(q)))
normalize(q::Q) = Q(fieldvalues(q) ./ abs(q))
inv(q::Q) = Q(fieldvalues(q) ./ abs2(q))
+(p::Q{T}, a::Real) = Q((+)(promote(scalar(p),a)...), bivecvalues(q))
+(a::Real, p::Q{T}) = Q((+)(promote(a,scalar(p))...), bivecvalues(q))
+(p::Q{T}, q::Q{T}) = Q((+)(fieldvalues(p) .+ fieldvalues(q))
-(p::Q{T}, a::Real) = Q((-)(promote(scalar(p),a)...), bivecvalues(q))
-(a::Real, p::Q{T}) = Q((+)(promote(a,scalar(p))...), bivecvalues(q))
-(p::Q{T}, q::Q{T}) = Q((+)(fieldvalues(p) .+ fieldvalues(q))
*(p::Q{T}, a::Real) = Q(fieldvalues(p) .* a)
*(a::Real, p::Q{T}) = Q(a .* fieldvalues(p))
*(p::Q{T}, q::Q{T}) =
Q(p.s*q.s - p.i*q.i - p.j*q.j - p.k*q.k,
p.s*q.i + p.i*q.s + p.j*q.k - p.k*q.j,
p.s*q.j - p.i*q.k + p.j*q.s + p.k*q.i,
p.s*q.k + p.i*q.j - p.j*q.i + p.k*q.s)
/(p::Q{T}, a::Real) = Q(fieldvalues(p) ./ a)
/(a::Real, p::Q{T}) = a * inv(p)
/(p::Q{T}, q::Q{T}) = (p * conj(q)) / abs2(q)
\(p::Q{T}, a::Real) = (a * p) / a^2
\(a::Real, p::Q{T}) = (conj(p) * a) / abs2(p)
\(p::Q{T}, q::Q{T}) = (conj(q) * p) / abs2(q)
To consider
exp, log, trig
rotation related ops
- how do we want to show Quaternions
- including definitional tests with the API