blob: d0dc582345a42d0c038eb83c8ef1ac616d4e0da4 [file] [log] [blame]
// Copyright ©2018 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package quat
import (
"math"
"testing"
)
var expTests = []struct {
q Number
want Number
}{
{q: Number{}, want: Number{Real: 1}},
// Expected velues below are from pyquaternion.
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1},
want: Number{Real: -0.43643792124786496, Imag: 1.549040352371697, Jmag: 1.549040352371697, Kmag: 1.549040352371697},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1},
want: Number{Real: 0.42389891174348104, Imag: 0, Jmag: 1.8986002490721081, Kmag: 1.8986002490721081},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1},
want: Number{Real: 1.4686939399158851, Imag: 0, Jmag: 0, Kmag: 2.2873552871788423},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1},
want: Number{Real: -0.16055653857469052, Imag: 0.569860099182514, Jmag: 0.569860099182514, Kmag: 0.569860099182514},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1},
want: Number{Real: 0.15594369476537437, Imag: 0, Jmag: 0.6984559986366083, Kmag: 0.6984559986366083},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1},
want: Number{Real: 0.5403023058681398, Imag: 0, Jmag: 0, Kmag: 0.8414709848078965},
},
}
func TestExp(t *testing.T) {
t.Parallel()
const tol = 1e-14
for _, test := range expTests {
got := Exp(test.q)
if !equalApprox(got, test.want, tol) {
t.Errorf("unexpected result for Exp(%v): got:%v want:%v", test.q, got, test.want)
}
}
}
var logTests = []struct {
q Number
want Number
}{
{q: Number{}, want: Number{Real: -inf}},
// Expected velues below are from pyquaternion.
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1},
want: Number{Real: 0.6931471805599453, Imag: 0.6045997880780728, Jmag: 0.6045997880780728, Kmag: 0.6045997880780728},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1},
want: Number{Real: 0.5493061443340548, Imag: 0, Jmag: 0.6755108588560398, Kmag: 0.6755108588560398},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1},
want: Number{Real: 0.3465735902799727, Imag: 0, Jmag: 0, Kmag: 0.7853981633974484},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1},
want: Number{Real: 0.5493061443340548, Imag: 0.906899682117109, Jmag: 0.906899682117109, Kmag: 0.906899682117109},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1},
want: Number{Real: 0.3465735902799727, Imag: 0, Jmag: 1.1107207345395915, Kmag: 1.1107207345395915},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1},
want: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1.5707963267948966},
},
}
func TestLog(t *testing.T) {
t.Parallel()
const tol = 1e-14
for _, test := range logTests {
got := Log(test.q)
if !equalApprox(got, test.want, tol) {
t.Errorf("unexpected result for Log(%v): got:%v want:%v", test.q, got, test.want)
}
}
}
var powTests = []struct {
q, r Number
want Number
}{
{q: Number{}, r: Number{}, want: Number{Real: 1}},
// Expected velues below are from pyquaternion.
// pyquaternion does not support quaternion powers.
// TODO(kortschak): Add non-real r cases.
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, r: Number{Real: 2},
want: Number{Real: -2, Imag: 2, Jmag: 2, Kmag: 2},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1}, r: Number{Real: 2},
want: Number{Real: -1, Imag: 0, Jmag: 2, Kmag: 2},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1}, r: Number{Real: 2},
want: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 2},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1}, r: Number{Real: 2},
want: Number{Real: -3, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1}, r: Number{Real: 2},
want: Number{Real: -2, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1}, r: Number{Real: 2},
want: Number{Real: -1, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, r: Number{Real: math.Pi},
want: Number{Real: -8.728144138959564, Imag: -0.7527136547040768, Jmag: -0.7527136547040768, Kmag: -0.7527136547040768},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1}, r: Number{Real: math.Pi},
want: Number{Real: -5.561182514695044, Imag: 0, Jmag: 0.5556661490713818, Kmag: 0.5556661490713818},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1}, r: Number{Real: math.Pi},
want: Number{Real: -2.320735561810013, Imag: 0, Jmag: 0, Kmag: 1.8544983901925216},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1}, r: Number{Real: math.Pi},
want: Number{Real: 1.2388947209955585, Imag: -3.162774128856231, Jmag: -3.162774128856231, Kmag: -3.162774128856231},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1}, r: Number{Real: math.Pi},
want: Number{Real: 0.6552860151073727, Imag: 0, Jmag: -2.0488506614051922, Kmag: -2.0488506614051922},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1}, r: Number{Real: math.Pi},
want: Number{Real: 0.22058404074969779, Imag: 0, Jmag: 0, Kmag: -0.9753679720836315},
},
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, r: Number{Real: 3},
want: Number{Real: -8, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1}, r: Number{Real: 3},
want: Number{Real: -5, Imag: 0, Jmag: 1, Kmag: 1},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1}, r: Number{Real: 3},
want: Number{Real: -2, Imag: 0, Jmag: 0, Kmag: 2},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1}, r: Number{Real: 3},
want: Number{Real: 0, Imag: -3, Jmag: -3, Kmag: -3},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1}, r: Number{Real: 3},
want: Number{Real: 0, Imag: 0, Jmag: -2, Kmag: -2},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1}, r: Number{Real: 3},
want: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: -1},
},
}
func TestPow(t *testing.T) {
t.Parallel()
const tol = 1e-14
for _, test := range powTests {
got := Pow(test.q, test.r)
if !equalApprox(got, test.want, tol) {
t.Errorf("unexpected result for Pow(%v, %v): got:%v want:%v", test.q, test.r, got, test.want)
}
}
}
var powRealTests = []struct {
q Number
r float64
want Number
}{
{q: Number{}, r: 0, want: Number{Real: 1}},
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, r: 2,
want: Number{Real: -2, Imag: 2, Jmag: 2, Kmag: 2},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1}, r: 2,
want: Number{Real: -1, Imag: 0, Jmag: 2, Kmag: 2},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1}, r: 2,
want: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 2},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1}, r: 2,
want: Number{Real: -3, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1}, r: 2,
want: Number{Real: -2, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1}, r: 2,
want: Number{Real: -1, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, r: math.Pi,
want: Number{Real: -8.728144138959564, Imag: -0.7527136547040768, Jmag: -0.7527136547040768, Kmag: -0.7527136547040768},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1}, r: math.Pi,
want: Number{Real: -5.561182514695044, Imag: 0, Jmag: 0.5556661490713818, Kmag: 0.5556661490713818},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1}, r: math.Pi,
want: Number{Real: -2.320735561810013, Imag: 0, Jmag: 0, Kmag: 1.8544983901925216},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1}, r: math.Pi,
want: Number{Real: 1.2388947209955585, Imag: -3.162774128856231, Jmag: -3.162774128856231, Kmag: -3.162774128856231},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1}, r: math.Pi,
want: Number{Real: 0.6552860151073727, Imag: 0, Jmag: -2.0488506614051922, Kmag: -2.0488506614051922},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1}, r: math.Pi,
want: Number{Real: 0.22058404074969779, Imag: 0, Jmag: 0, Kmag: -0.9753679720836315},
},
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, r: 3,
want: Number{Real: -8, Imag: 0, Jmag: 0, Kmag: 0},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1}, r: 3,
want: Number{Real: -5, Imag: 0, Jmag: 1, Kmag: 1},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1}, r: 3,
want: Number{Real: -2, Imag: 0, Jmag: 0, Kmag: 2},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1}, r: 3,
want: Number{Real: 0, Imag: -3, Jmag: -3, Kmag: -3},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1}, r: 3,
want: Number{Real: 0, Imag: 0, Jmag: -2, Kmag: -2},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1}, r: 3,
want: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: -1},
},
}
func TestRealPow(t *testing.T) {
t.Parallel()
const tol = 1e-14
for _, test := range powRealTests {
got := PowReal(test.q, test.r)
if !equalApprox(got, test.want, tol) {
t.Errorf("unexpected result for Pow(%v, %v): got:%v want:%v", test.q, test.r, got, test.want)
}
}
}
var sqrtTests = []struct {
q Number
want Number
}{
{q: Number{}, want: Number{}},
// Expected velues below are from pyquaternion.
{
q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1},
want: Number{Real: 1.2247448713915892, Imag: 0.4082482904638631, Jmag: 0.4082482904638631, Kmag: 0.4082482904638631},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 1, Kmag: 1},
want: Number{Real: 1.1687708944803676, Imag: 0, Jmag: 0.42779983858367593, Kmag: 0.42779983858367593},
},
{
q: Number{Real: 1, Imag: 0, Jmag: 0, Kmag: 1},
want: Number{Real: 1.0986841134678098, Imag: 0, Jmag: 0, Kmag: 0.45508986056222733},
},
{
q: Number{Real: 0, Imag: 1, Jmag: 1, Kmag: 1},
want: Number{Real: 0.9306048591020996, Imag: 0.5372849659117709, Jmag: 0.5372849659117709, Kmag: 0.5372849659117709},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 1, Kmag: 1},
want: Number{Real: 0.8408964152537146, Imag: 0, Jmag: 0.5946035575013604, Kmag: 0.5946035575013604},
},
{
q: Number{Real: 0, Imag: 0, Jmag: 0, Kmag: 1},
want: Number{Real: 0.7071067811865476, Imag: 0, Jmag: 0, Kmag: 0.7071067811865475},
},
}
func TestSqrt(t *testing.T) {
t.Parallel()
const tol = 1e-14
for _, test := range sqrtTests {
got := Sqrt(test.q)
if !equalApprox(got, test.want, tol) {
t.Errorf("unexpected result for Sqrt(%v): got:%v want:%v", test.q, got, test.want)
}
}
}