github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/num/dualquat/dual_test.go (about)

     1  // Copyright ©2018 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package dualquat
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"testing"
    11  
    12  	"github.com/jingcheng-WU/gonum/floats/scalar"
    13  	"github.com/jingcheng-WU/gonum/num/quat"
    14  )
    15  
    16  var formatTests = []struct {
    17  	d      Number
    18  	format string
    19  	want   string
    20  }{
    21  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%#v", want: "dualquat.Number{Real:quat.Number{Real:1.1, Imag:2.1, Jmag:3.1, Kmag:4.1}, Dual:quat.Number{Real:1.2, Imag:2.2, Jmag:3.2, Kmag:4.2}}"},                 // Bootstrap test.
    22  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%#v", want: "dualquat.Number{Real:quat.Number{Real:-1.1, Imag:-2.1, Jmag:-3.1, Kmag:-4.1}, Dual:quat.Number{Real:-1.2, Imag:-2.2, Jmag:-3.2, Kmag:-4.2}}"}, // Bootstrap test.
    23  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%+v", want: "{Real:{Real:1.1, Imag:2.1, Jmag:3.1, Kmag:4.1}, Dual:{Real:1.2, Imag:2.2, Jmag:3.2, Kmag:4.2}}"},
    24  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%+v", want: "{Real:{Real:-1.1, Imag:-2.1, Jmag:-3.1, Kmag:-4.1}, Dual:{Real:-1.2, Imag:-2.2, Jmag:-3.2, Kmag:-4.2}}"},
    25  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%v", want: "((1.1+2.1i+3.1j+4.1k)+(+1.2+2.2i+3.2j+4.2k)ϵ)"},
    26  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%v", want: "((-1.1-2.1i-3.1j-4.1k)+(-1.2-2.2i-3.2j-4.2k)ϵ)"},
    27  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%g", want: "((1.1+2.1i+3.1j+4.1k)+(+1.2+2.2i+3.2j+4.2k)ϵ)"},
    28  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%g", want: "((-1.1-2.1i-3.1j-4.1k)+(-1.2-2.2i-3.2j-4.2k)ϵ)"},
    29  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%e", want: "((1.100000e+00+2.100000e+00i+3.100000e+00j+4.100000e+00k)+(+1.200000e+00+2.200000e+00i+3.200000e+00j+4.200000e+00k)ϵ)"},
    30  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%e", want: "((-1.100000e+00-2.100000e+00i-3.100000e+00j-4.100000e+00k)+(-1.200000e+00-2.200000e+00i-3.200000e+00j-4.200000e+00k)ϵ)"},
    31  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%E", want: "((1.100000E+00+2.100000E+00i+3.100000E+00j+4.100000E+00k)+(+1.200000E+00+2.200000E+00i+3.200000E+00j+4.200000E+00k)ϵ)"},
    32  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%E", want: "((-1.100000E+00-2.100000E+00i-3.100000E+00j-4.100000E+00k)+(-1.200000E+00-2.200000E+00i-3.200000E+00j-4.200000E+00k)ϵ)"},
    33  	{d: Number{quat.Number{Real: 1.1, Imag: 2.1, Jmag: 3.1, Kmag: 4.1}, quat.Number{Real: 1.2, Imag: 2.2, Jmag: 3.2, Kmag: 4.2}}, format: "%f", want: "((1.100000+2.100000i+3.100000j+4.100000k)+(+1.200000+2.200000i+3.200000j+4.200000k)ϵ)"},
    34  	{d: Number{quat.Number{Real: -1.1, Imag: -2.1, Jmag: -3.1, Kmag: -4.1}, quat.Number{Real: -1.2, Imag: -2.2, Jmag: -3.2, Kmag: -4.2}}, format: "%f", want: "((-1.100000-2.100000i-3.100000j-4.100000k)+(-1.200000-2.200000i-3.200000j-4.200000k)ϵ)"},
    35  }
    36  
    37  func TestFormat(t *testing.T) {
    38  	t.Parallel()
    39  	for _, test := range formatTests {
    40  		got := fmt.Sprintf(test.format, test.d)
    41  		if got != test.want {
    42  			t.Errorf("unexpected result for fmt.Sprintf(%q, %#v): got:%q, want:%q", test.format, test.d, got, test.want)
    43  		}
    44  	}
    45  }
    46  
    47  // First derivatives:
    48  
    49  func dExp(x quat.Number) quat.Number { return quat.Exp(x) }
    50  func dLog(x quat.Number) quat.Number {
    51  	switch {
    52  	case x == zeroQuat:
    53  		return quat.Inf()
    54  	case quat.IsInf(x):
    55  		return zeroQuat
    56  	}
    57  	return quat.Inv(x)
    58  }
    59  func dSqrt(x quat.Number) quat.Number { return quat.Scale(0.5, quat.Inv(quat.Sqrt(x))) }
    60  func dInv(x quat.Number) quat.Number  { return quat.Scale(-1, quat.Inv(quat.Mul(x, x))) }
    61  
    62  var (
    63  	negZero     = math.Copysign(0, -1)
    64  	oneReal     = quat.Number{Real: 1}
    65  	negZeroQuat = quat.Scale(-1, zeroQuat)
    66  	one         = quat.Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}
    67  	negOne      = quat.Scale(-1, one)
    68  	half        = quat.Scale(0.5, one)
    69  	negHalf     = quat.Scale(-1, half)
    70  	two         = quat.Scale(2, one)
    71  	negTwo      = quat.Scale(-1, two)
    72  	three       = quat.Scale(3, one)
    73  	negThree    = quat.Scale(-1, three)
    74  	four        = quat.Scale(4, one)
    75  	six         = quat.Scale(6, one)
    76  )
    77  
    78  var dualTests = []struct {
    79  	name   string
    80  	x      []quat.Number
    81  	fnDual func(x Number) Number
    82  	fn     func(x quat.Number) quat.Number
    83  	dFn    func(x quat.Number) quat.Number
    84  }{
    85  	{
    86  		name:   "exp",
    87  		x:      []quat.Number{quat.NaN(), quat.Inf(), negThree, negTwo, negOne, negHalf, negZeroQuat, zeroQuat, half, one, two, three},
    88  		fnDual: Exp,
    89  		fn:     quat.Exp,
    90  		dFn:    dExp,
    91  	},
    92  	{
    93  		name:   "log",
    94  		x:      []quat.Number{quat.NaN(), quat.Inf(), negThree, negTwo, negOne, negHalf, negZeroQuat, zeroQuat, half, one, two, three},
    95  		fnDual: Log,
    96  		fn:     quat.Log,
    97  		dFn:    dLog,
    98  	},
    99  	{
   100  		name:   "inv",
   101  		x:      []quat.Number{quat.NaN(), quat.Inf(), negThree, negTwo, negOne, negHalf, negZeroQuat, zeroQuat, half, one, two, three},
   102  		fnDual: Inv,
   103  		fn:     quat.Inv,
   104  		dFn:    dInv,
   105  	},
   106  	{
   107  		name:   "sqrt",
   108  		x:      []quat.Number{quat.NaN(), quat.Inf(), negThree, negTwo, negOne, negHalf, negZeroQuat, zeroQuat, half, one, two, three},
   109  		fnDual: Sqrt,
   110  		fn:     quat.Sqrt,
   111  		dFn:    dSqrt,
   112  	},
   113  }
   114  
   115  func TestNumber(t *testing.T) {
   116  	t.Parallel()
   117  	const tol = 1e-15
   118  	for _, test := range dualTests {
   119  		for _, x := range test.x {
   120  			fxDual := test.fnDual(Number{Real: x, Dual: oneReal})
   121  			fx := test.fn(x)
   122  			dFx := test.dFn(x)
   123  			if !same(fxDual.Real, fx, tol) {
   124  				t.Errorf("unexpected %s(%v): got:%v want:%v", test.name, x, fxDual.Real, fx)
   125  			}
   126  			if !same(fxDual.Dual, dFx, tol) {
   127  				t.Errorf("unexpected %s'(%v): got:%v want:%v", test.name, x, fxDual.Dual, dFx)
   128  			}
   129  		}
   130  	}
   131  }
   132  
   133  var invTests = []Number{
   134  	{Real: quat.Number{Real: 1}},
   135  	{Real: quat.Number{Real: 1}, Dual: quat.Number{Real: 1}},
   136  	{Real: quat.Number{Imag: 1}, Dual: quat.Number{Real: 1}},
   137  	{Real: quat.Number{Real: 1}, Dual: quat.Number{Real: 1, Imag: 1}},
   138  	{Real: quat.Number{Real: 1, Imag: 1}, Dual: quat.Number{Real: 1, Imag: 1}},
   139  	{Real: quat.Number{Real: 1, Imag: 10}, Dual: quat.Number{Real: 1, Imag: 5}},
   140  	{Real: quat.Number{Real: 10, Imag: 1}, Dual: quat.Number{Real: 5, Imag: 1}},
   141  	{Real: quat.Number{Real: 1}, Dual: quat.Number{Real: 1, Imag: 1, Kmag: 1}},
   142  	{Real: quat.Number{Real: 12, Imag: 1}, Dual: quat.Number{Real: 1, Imag: 1}},
   143  	{Real: quat.Number{Real: 12, Imag: 1, Jmag: 3}},
   144  }
   145  
   146  func TestInv(t *testing.T) {
   147  	t.Parallel()
   148  	const tol = 1e-15
   149  	for _, x := range invTests {
   150  		got := Mul(x, Inv(x))
   151  		want := Number{Real: quat.Number{Real: 1}}
   152  		if !sameDual(got, want, tol) {
   153  			t.Errorf("unexpected Mul(%[1]v, Inv(%[1]v)): got:%v want:%v", x, got, want)
   154  		}
   155  	}
   156  }
   157  
   158  var powRealTests = []struct {
   159  	d    Number
   160  	p    float64
   161  	want Number
   162  }{
   163  	// PowReal(NaN+xϵ, ±0) = 1+NaNϵ for any x
   164  	{d: Number{Real: quat.NaN(), Dual: zeroQuat}, p: 0, want: Number{Real: oneReal, Dual: quat.NaN()}},
   165  	{d: Number{Real: quat.NaN(), Dual: zeroQuat}, p: negZero, want: Number{Real: oneReal, Dual: quat.NaN()}},
   166  	{d: Number{Real: quat.NaN(), Dual: one}, p: 0, want: Number{Real: oneReal, Dual: quat.NaN()}},
   167  	{d: Number{Real: quat.NaN(), Dual: two}, p: negZero, want: Number{Real: oneReal, Dual: quat.NaN()}},
   168  	{d: Number{Real: quat.NaN(), Dual: three}, p: 0, want: Number{Real: oneReal, Dual: quat.NaN()}},
   169  	{d: Number{Real: quat.NaN(), Dual: one}, p: negZero, want: Number{Real: oneReal, Dual: quat.NaN()}},
   170  	{d: Number{Real: quat.NaN(), Dual: two}, p: 0, want: Number{Real: oneReal, Dual: quat.NaN()}},
   171  	{d: Number{Real: quat.NaN(), Dual: three}, p: negZero, want: Number{Real: oneReal, Dual: quat.NaN()}},
   172  
   173  	// PowReal(x, ±0) = 1 for any x
   174  	{d: Number{Real: zeroQuat, Dual: zeroQuat}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   175  	{d: Number{Real: negZeroQuat, Dual: zeroQuat}, p: negZero, want: Number{Real: oneReal, Dual: zeroQuat}},
   176  	{d: Number{Real: quat.Inf(), Dual: zeroQuat}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   177  	{d: Number{Real: quat.Inf(), Dual: zeroQuat}, p: negZero, want: Number{Real: oneReal, Dual: zeroQuat}},
   178  	{d: Number{Real: zeroQuat, Dual: one}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   179  	{d: Number{Real: negZeroQuat, Dual: one}, p: negZero, want: Number{Real: oneReal, Dual: zeroQuat}},
   180  	{d: Number{Real: quat.Inf(), Dual: one}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   181  	{d: Number{Real: quat.Inf(), Dual: one}, p: negZero, want: Number{Real: oneReal, Dual: zeroQuat}},
   182  
   183  	// PowReal(1+xϵ, y) = (1+xyϵ) for any y
   184  	{d: Number{Real: oneReal, Dual: zeroQuat}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   185  	{d: Number{Real: oneReal, Dual: zeroQuat}, p: 1, want: Number{Real: oneReal, Dual: zeroQuat}},
   186  	{d: Number{Real: oneReal, Dual: zeroQuat}, p: 2, want: Number{Real: oneReal, Dual: zeroQuat}},
   187  	{d: Number{Real: oneReal, Dual: zeroQuat}, p: 3, want: Number{Real: oneReal, Dual: zeroQuat}},
   188  	{d: Number{Real: oneReal, Dual: one}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   189  	{d: Number{Real: oneReal, Dual: one}, p: 1, want: Number{Real: oneReal, Dual: one}},
   190  	{d: Number{Real: oneReal, Dual: one}, p: 2, want: Number{Real: oneReal, Dual: two}},
   191  	{d: Number{Real: oneReal, Dual: one}, p: 3, want: Number{Real: oneReal, Dual: three}},
   192  	{d: Number{Real: oneReal, Dual: two}, p: 0, want: Number{Real: oneReal, Dual: zeroQuat}},
   193  	{d: Number{Real: oneReal, Dual: two}, p: 1, want: Number{Real: oneReal, Dual: two}},
   194  	{d: Number{Real: oneReal, Dual: two}, p: 2, want: Number{Real: oneReal, Dual: four}},
   195  	{d: Number{Real: oneReal, Dual: two}, p: 3, want: Number{Real: oneReal, Dual: six}},
   196  
   197  	// PowReal(x, 1) = x for any x
   198  	{d: Number{Real: zeroQuat, Dual: zeroQuat}, p: 1, want: Number{Real: zeroQuat, Dual: zeroQuat}},
   199  	{d: Number{Real: negZeroQuat, Dual: zeroQuat}, p: 1, want: Number{Real: negZeroQuat, Dual: zeroQuat}},
   200  	{d: Number{Real: zeroQuat, Dual: one}, p: 1, want: Number{Real: zeroQuat, Dual: one}},
   201  	{d: Number{Real: negZeroQuat, Dual: one}, p: 1, want: Number{Real: negZeroQuat, Dual: one}},
   202  	{d: Number{Real: quat.NaN(), Dual: zeroQuat}, p: 1, want: Number{Real: quat.NaN(), Dual: zeroQuat}},
   203  	{d: Number{Real: quat.NaN(), Dual: one}, p: 1, want: Number{Real: quat.NaN(), Dual: one}},
   204  	{d: Number{Real: quat.NaN(), Dual: two}, p: 1, want: Number{Real: quat.NaN(), Dual: two}},
   205  
   206  	// PowReal(NaN+xϵ, y) = NaN+NaNϵ
   207  	{d: Number{Real: quat.NaN(), Dual: zeroQuat}, p: 2, want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   208  	{d: Number{Real: quat.NaN(), Dual: zeroQuat}, p: 3, want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   209  	{d: Number{Real: quat.NaN(), Dual: one}, p: 2, want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   210  	{d: Number{Real: quat.NaN(), Dual: one}, p: 3, want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   211  	{d: Number{Real: quat.NaN(), Dual: two}, p: 2, want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   212  	{d: Number{Real: quat.NaN(), Dual: two}, p: 3, want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   213  
   214  	// PowReal(x, NaN) = NaN+NaNϵ
   215  	{d: Number{Real: zeroQuat, Dual: zeroQuat}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   216  	{d: Number{Real: two, Dual: zeroQuat}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   217  	{d: Number{Real: three, Dual: zeroQuat}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   218  	{d: Number{Real: zeroQuat, Dual: one}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   219  	{d: Number{Real: two, Dual: one}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   220  	{d: Number{Real: three, Dual: one}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   221  	{d: Number{Real: zeroQuat, Dual: two}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   222  	{d: Number{Real: two, Dual: two}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   223  	{d: Number{Real: three, Dual: two}, p: math.NaN(), want: Number{Real: quat.NaN(), Dual: quat.NaN()}},
   224  
   225  	// Handled by quat.Pow tests:
   226  	//
   227  	// Pow(±0, y) = ±Inf for y an odd integer < 0
   228  	// Pow(±0, -Inf) = +Inf
   229  	// Pow(±0, +Inf) = +0
   230  	// Pow(±0, y) = +Inf for finite y < 0 and not an odd integer
   231  	// Pow(±0, y) = ±0 for y an odd integer > 0
   232  	// Pow(±0, y) = +0 for finite y > 0 and not an odd integer
   233  	// Pow(-1, ±Inf) = 1
   234  
   235  	// PowReal(x+0ϵ, +Inf) = +Inf+NaNϵ for |x| > 1
   236  	{d: Number{Real: two, Dual: zeroQuat}, p: math.Inf(1), want: Number{Real: quat.Inf(), Dual: quat.NaN()}},
   237  	{d: Number{Real: three, Dual: zeroQuat}, p: math.Inf(1), want: Number{Real: quat.Inf(), Dual: quat.NaN()}},
   238  
   239  	// PowReal(x+yϵ, +Inf) = +Inf for |x| > 1
   240  	{d: Number{Real: two, Dual: one}, p: math.Inf(1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   241  	{d: Number{Real: three, Dual: one}, p: math.Inf(1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   242  	{d: Number{Real: two, Dual: two}, p: math.Inf(1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   243  	{d: Number{Real: three, Dual: two}, p: math.Inf(1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   244  
   245  	// PowReal(x, -Inf) = +0+NaNϵ for |x| > 1
   246  	{d: Number{Real: two, Dual: zeroQuat}, p: math.Inf(-1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   247  	{d: Number{Real: three, Dual: zeroQuat}, p: math.Inf(-1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   248  	{d: Number{Real: two, Dual: one}, p: math.Inf(-1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   249  	{d: Number{Real: three, Dual: one}, p: math.Inf(-1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   250  	{d: Number{Real: two, Dual: two}, p: math.Inf(-1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   251  	{d: Number{Real: three, Dual: two}, p: math.Inf(-1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   252  
   253  	// PowReal(x+yϵ, +Inf) = +0+NaNϵ for |x| < 1
   254  	{d: Number{Real: quat.Scale(0.1, one), Dual: zeroQuat}, p: math.Inf(1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   255  	{d: Number{Real: quat.Scale(0.1, one), Dual: quat.Scale(0.1, one)}, p: math.Inf(1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   256  	{d: Number{Real: quat.Scale(0.2, one), Dual: quat.Scale(0.2, one)}, p: math.Inf(1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   257  	{d: Number{Real: quat.Scale(0.5, one), Dual: quat.Scale(0.5, one)}, p: math.Inf(1), want: Number{Real: zeroQuat, Dual: quat.NaN()}},
   258  
   259  	// PowReal(x+0ϵ, -Inf) = +Inf+NaNϵ for |x| < 1
   260  	{d: Number{Real: quat.Scale(0.1, one), Dual: zeroQuat}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.NaN()}},
   261  	{d: Number{Real: quat.Scale(0.2, one), Dual: zeroQuat}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.NaN()}},
   262  
   263  	// PowReal(x, -Inf) = +Inf-Infϵ for |x| < 1
   264  	{d: Number{Real: quat.Scale(0.1, one), Dual: quat.Scale(0.1, one)}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   265  	{d: Number{Real: quat.Scale(0.2, one), Dual: quat.Scale(0.1, one)}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   266  	{d: Number{Real: quat.Scale(0.1, one), Dual: quat.Scale(0.2, one)}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   267  	{d: Number{Real: quat.Scale(0.2, one), Dual: quat.Scale(0.2, one)}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   268  	{d: Number{Real: quat.Scale(0.1, one), Dual: one}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   269  	{d: Number{Real: quat.Scale(0.2, one), Dual: one}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   270  	{d: Number{Real: quat.Scale(0.1, one), Dual: two}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   271  	{d: Number{Real: quat.Scale(0.2, one), Dual: two}, p: math.Inf(-1), want: Number{Real: quat.Inf(), Dual: quat.Inf()}},
   272  
   273  	// Handled by quat.Pow tests:
   274  	//
   275  	// Pow(+Inf, y) = +Inf for y > 0
   276  	// Pow(+Inf, y) = +0 for y < 0
   277  	// Pow(-Inf, y) = Pow(-0, -y)
   278  }
   279  
   280  func TestPowReal(t *testing.T) {
   281  	t.Parallel()
   282  	const tol = 1e-15
   283  	for _, test := range powRealTests {
   284  		got := PowReal(test.d, test.p)
   285  		if !sameDual(got, test.want, tol) {
   286  			t.Errorf("unexpected PowReal(%v, %v): got:%v want:%v", test.d, test.p, got, test.want)
   287  		}
   288  	}
   289  }
   290  
   291  func sameDual(a, b Number, tol float64) bool {
   292  	return same(a.Real, b.Real, tol) && same(a.Dual, b.Dual, tol)
   293  }
   294  
   295  func same(a, b quat.Number, tol float64) bool {
   296  	return (quat.IsNaN(a) && quat.IsNaN(b)) || (quat.IsInf(a) && quat.IsInf(b)) || equalApprox(a, b, tol)
   297  }
   298  
   299  func equalApprox(a, b quat.Number, tol float64) bool {
   300  	return scalar.EqualWithinAbsOrRel(a.Real, b.Real, tol, tol) &&
   301  		scalar.EqualWithinAbsOrRel(a.Imag, b.Imag, tol, tol) &&
   302  		scalar.EqualWithinAbsOrRel(a.Jmag, b.Jmag, tol, tol) &&
   303  		scalar.EqualWithinAbsOrRel(a.Kmag, b.Kmag, tol, tol)
   304  }