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
5package dualcmplx
6
7import (
8	"fmt"
9	"math"
10	"math/cmplx"
11	"testing"
12
13	"gonum.org/v1/gonum/floats/scalar"
14)
15
16var formatTests = []struct {
17	d      Number
18	format string
19	want   string
20}{
21	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%#v", want: "dualcmplx.Number{Real:(1.1+2.1i), Dual:(1.2+2.2i)}"},     // Bootstrap test.
22	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%#v", want: "dualcmplx.Number{Real:(-1.1-2.1i), Dual:(-1.2-2.2i)}"}, // Bootstrap test.
23	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%+v", want: "{Real:(1.1+2.1i), Dual:(1.2+2.2i)}"},
24	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%+v", want: "{Real:(-1.1-2.1i), Dual:(-1.2-2.2i)}"},
25	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%v", want: "((1.1+2.1i)+(+1.2+2.2i)ϵ)"},
26	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%v", want: "((-1.1-2.1i)+(-1.2-2.2i)ϵ)"},
27	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%g", want: "((1.1+2.1i)+(+1.2+2.2i)ϵ)"},
28	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%g", want: "((-1.1-2.1i)+(-1.2-2.2i)ϵ)"},
29	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%e", want: "((1.100000e+00+2.100000e+00i)+(+1.200000e+00+2.200000e+00i)ϵ)"},
30	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%e", want: "((-1.100000e+00-2.100000e+00i)+(-1.200000e+00-2.200000e+00i)ϵ)"},
31	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%E", want: "((1.100000E+00+2.100000E+00i)+(+1.200000E+00+2.200000E+00i)ϵ)"},
32	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%E", want: "((-1.100000E+00-2.100000E+00i)+(-1.200000E+00-2.200000E+00i)ϵ)"},
33	{d: Number{1.1 + 2.1i, 1.2 + 2.2i}, format: "%f", want: "((1.100000+2.100000i)+(+1.200000+2.200000i)ϵ)"},
34	{d: Number{-1.1 - 2.1i, -1.2 - 2.2i}, format: "%f", want: "((-1.100000-2.100000i)+(-1.200000-2.200000i)ϵ)"},
35}
36
37func 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// FIXME(kortschak): See golang/go#29320.
48
49func sqrt(x complex128) complex128 {
50	switch {
51	case math.IsInf(imag(x), 1):
52		return cmplx.Inf()
53	case math.IsNaN(imag(x)):
54		return cmplx.NaN()
55	case math.IsInf(real(x), -1):
56		if imag(x) >= 0 && !math.IsInf(imag(x), 1) {
57			return complex(0, math.NaN())
58		}
59		if math.IsNaN(imag(x)) {
60			return complex(math.NaN(), math.Inf(1))
61		}
62	case math.IsInf(real(x), 1):
63		if imag(x) >= 0 && !math.IsInf(imag(x), 1) {
64			return complex(math.Inf(1), 0)
65		}
66		if math.IsNaN(imag(x)) {
67			return complex(math.Inf(1), math.NaN())
68		}
69	case math.IsInf(real(x), -1):
70		return complex(0, math.Inf(1))
71	case math.IsNaN(real(x)):
72		if math.IsNaN(imag(x)) || math.IsInf(imag(x), 0) {
73			return cmplx.NaN()
74		}
75	}
76	return cmplx.Sqrt(x)
77}
78
79// First derivatives:
80
81func dExp(x complex128) complex128 {
82	if imag(x) == 0 {
83		return cmplx.Exp(x)
84	}
85	return (cmplx.Exp(x) - cmplx.Exp(cmplx.Conj(x))) / (x - cmplx.Conj(x))
86}
87func dLog(x complex128) complex128 {
88	if cmplx.IsInf(x) {
89		return 0
90	}
91	if x == 0 {
92		if math.Copysign(1, real(x)) < 0 {
93			return complex(math.Inf(-1), math.NaN())
94		}
95		return complex(math.Inf(1), math.NaN())
96	}
97	return (cmplx.Log(x) - cmplx.Log(cmplx.Conj(x))) / (x - cmplx.Conj(x))
98}
99func dInv(x complex128) complex128 { return -1 / (x * cmplx.Conj(x)) }
100
101var (
102	negZero      = math.Copysign(0, -1)
103	zeroCmplx    = 0 + 0i
104	negZeroCmplx = -1 * zeroCmplx
105	one          = 1 + 1i
106	negOne       = -1 - 1i
107	half         = one / 2
108	negHalf      = negOne / 2
109	two          = 2 + 2i
110	negTwo       = -2 - 2i
111	three        = 3 + 3i
112	negThree     = -3 + 3i
113)
114
115var dualTests = []struct {
116	name   string
117	x      []complex128
118	fnDual func(x Number) Number
119	fn     func(x complex128) complex128
120	dFn    func(x complex128) complex128
121}{
122	{
123		name:   "exp",
124		x:      []complex128{cmplx.NaN(), cmplx.Inf(), negThree, negTwo, negOne, negHalf, negZeroCmplx, zeroCmplx, half, one, two, three},
125		fnDual: Exp,
126		fn:     cmplx.Exp,
127		dFn:    dExp,
128	},
129	{
130		name:   "log",
131		x:      []complex128{cmplx.NaN(), cmplx.Inf(), negThree, negTwo, negOne, negHalf, negZeroCmplx, zeroCmplx, half, one, two, three},
132		fnDual: Log,
133		fn:     cmplx.Log,
134		dFn:    dLog,
135	},
136	{
137		name:   "inv",
138		x:      []complex128{cmplx.NaN(), cmplx.Inf(), negThree, negTwo, negOne, negHalf, negZeroCmplx, zeroCmplx, half, one, two, three},
139		fnDual: Inv,
140		fn:     func(x complex128) complex128 { return 1 / x },
141		dFn:    dInv,
142	},
143	{
144		name:   "sqrt",
145		x:      []complex128{cmplx.NaN(), cmplx.Inf(), negThree, negTwo, negOne, negHalf, negZeroCmplx, zeroCmplx, half, one, two, three},
146		fnDual: Sqrt,
147		fn:     sqrt,
148		// TODO(kortschak): Find a concise dSqrt.
149	},
150}
151
152func TestNumber(t *testing.T) {
153	t.Parallel()
154	const tol = 1e-15
155	for _, test := range dualTests {
156		for _, x := range test.x {
157			fxDual := test.fnDual(Number{Real: x, Dual: 1})
158			fx := test.fn(x)
159			if !same(fxDual.Real, fx, tol) {
160				t.Errorf("unexpected %s(%v): got:%v want:%v", test.name, x, fxDual.Real, fx)
161			}
162			if test.dFn == nil {
163				continue
164			}
165			dFx := test.dFn(x)
166			if !same(fxDual.Dual, dFx, tol) {
167				t.Errorf("unexpected %s'(%v): got:%v want:%v", test.name, x, fxDual.Dual, dFx)
168			}
169		}
170	}
171}
172
173var invTests = []Number{
174	{Real: 1, Dual: 0},
175	{Real: 1, Dual: 1},
176	{Real: 1i, Dual: 1},
177	{Real: 1, Dual: 1 + 1i},
178	{Real: 1 + 1i, Dual: 1 + 1i},
179	{Real: 1 + 10i, Dual: 1 + 5i},
180	{Real: 10 + 1i, Dual: 5 + 1i},
181}
182
183func TestInv(t *testing.T) {
184	t.Parallel()
185	const tol = 1e-15
186	for _, x := range invTests {
187		got := Mul(x, Inv(x))
188		want := Number{Real: 1}
189		if !sameDual(got, want, tol) {
190			t.Errorf("unexpected Mul(%[1]v, Inv(%[1]v)): got:%v want:%v", x, got, want)
191		}
192	}
193}
194
195var expLogTests = []Number{
196	{Real: 1i, Dual: 1i},
197	{Real: 1 + 1i, Dual: 1 + 1i},
198	{Real: 1 + 1e-1i, Dual: 1 + 1i},
199	{Real: 1 + 1e-2i, Dual: 1 + 1i},
200	{Real: 1 + 1e-4i, Dual: 1 + 1i},
201	{Real: 1 + 1e-6i, Dual: 1 + 1i},
202	{Real: 1 + 1e-8i, Dual: 1 + 1i},
203	{Real: 1 + 1e-10i, Dual: 1 + 1i},
204	{Real: 1 + 1e-12i, Dual: 1 + 1i},
205	{Real: 1 + 1e-14i, Dual: 1 + 1i},
206	{Dual: 1 + 1i},
207	{Dual: 1 + 2i},
208	{Dual: 2 + 1i},
209	{Dual: 2 + 2i},
210	{Dual: 1 + 1i},
211	{Dual: 1 + 5i},
212	{Dual: 5 + 1i},
213	{Real: 1 + 0i, Dual: 1 + 1i},
214	{Real: 1 + 0i, Dual: 1 + 2i},
215	{Real: 1 + 0i, Dual: 2 + 1i},
216	{Real: 1 + 0i, Dual: 2 + 2i},
217	{Real: 1 + 1i, Dual: 1 + 1i},
218	{Real: 1 + 3i, Dual: 1 + 5i},
219	{Real: 2 + 1i, Dual: 5 + 1i},
220}
221
222func TestExpLog(t *testing.T) {
223	t.Parallel()
224	const tol = 1e-15
225	for _, x := range expLogTests {
226		got := Log(Exp(x))
227		want := x
228		if !sameDual(got, want, tol) {
229			t.Errorf("unexpected Log(Exp(%v)): got:%v want:%v", x, got, want)
230		}
231	}
232}
233
234func TestLogExp(t *testing.T) {
235	t.Parallel()
236	const tol = 1e-15
237	for _, x := range expLogTests {
238		if x.Real == 0 {
239			continue
240		}
241		got := Exp(Log(x))
242		want := x
243		if !sameDual(got, want, tol) {
244			t.Errorf("unexpected Log(Exp(%v)): got:%v want:%v", x, got, want)
245		}
246	}
247}
248
249var powRealSpecialTests = []struct {
250	d    Number
251	p    float64
252	want Number
253}{
254	// PowReal(NaN+xϵ, ±0) = 1+NaNϵ for any x
255	{d: Number{Real: cmplx.NaN(), Dual: 0}, p: 0, want: Number{Real: 1, Dual: cmplx.NaN()}},
256	{d: Number{Real: cmplx.NaN(), Dual: 0}, p: negZero, want: Number{Real: 1, Dual: cmplx.NaN()}},
257	{d: Number{Real: cmplx.NaN(), Dual: 1}, p: 0, want: Number{Real: 1, Dual: cmplx.NaN()}},
258	{d: Number{Real: cmplx.NaN(), Dual: 2}, p: negZero, want: Number{Real: 1, Dual: cmplx.NaN()}},
259	{d: Number{Real: cmplx.NaN(), Dual: 3}, p: 0, want: Number{Real: 1, Dual: cmplx.NaN()}},
260	{d: Number{Real: cmplx.NaN(), Dual: 1}, p: negZero, want: Number{Real: 1, Dual: cmplx.NaN()}},
261	{d: Number{Real: cmplx.NaN(), Dual: 2}, p: 0, want: Number{Real: 1, Dual: cmplx.NaN()}},
262	{d: Number{Real: cmplx.NaN(), Dual: 3}, p: negZero, want: Number{Real: 1, Dual: cmplx.NaN()}},
263
264	// Pow(0+xϵ, y) = 0+Infϵ for all y < 1.
265	{d: Number{Real: 0}, p: 0.1, want: Number{Dual: cmplx.Inf()}},
266	{d: Number{Real: 0}, p: -1, want: Number{Dual: cmplx.Inf()}},
267	{d: Number{Dual: 1}, p: 0.1, want: Number{Dual: cmplx.Inf()}},
268	{d: Number{Dual: 1}, p: -1, want: Number{Dual: cmplx.Inf()}},
269	{d: Number{Dual: 1 + 1i}, p: 0.1, want: Number{Dual: cmplx.Inf()}},
270	{d: Number{Dual: 1 + 1i}, p: -1, want: Number{Dual: cmplx.Inf()}},
271	{d: Number{Dual: 1i}, p: 0.1, want: Number{Dual: cmplx.Inf()}},
272	{d: Number{Dual: 1i}, p: -1, want: Number{Dual: cmplx.Inf()}},
273	// Pow(0+xϵ, y) = 0 for all y > 1.
274	{d: Number{Real: 0}, p: 1.1, want: Number{Real: 0}},
275	{d: Number{Real: 0}, p: 2, want: Number{Real: 0}},
276	{d: Number{Dual: 1}, p: 1.1, want: Number{Real: 0}},
277	{d: Number{Dual: 1}, p: 2, want: Number{Real: 0}},
278	{d: Number{Dual: 1 + 1i}, p: 1.1, want: Number{Real: 0}},
279	{d: Number{Dual: 1 + 1i}, p: 2, want: Number{Real: 0}},
280	{d: Number{Dual: 1i}, p: 1.1, want: Number{Real: 0}},
281	{d: Number{Dual: 1i}, p: 2, want: Number{Real: 0}},
282
283	// PowReal(x, ±0) = 1 for any x
284	{d: Number{Real: 0, Dual: 0}, p: 0, want: Number{Real: 1, Dual: 0}},
285	{d: Number{Real: negZeroCmplx, Dual: 0}, p: negZero, want: Number{Real: 1, Dual: 0}},
286	{d: Number{Real: cmplx.Inf(), Dual: 0}, p: 0, want: Number{Real: 1, Dual: 0}},
287	{d: Number{Real: cmplx.Inf(), Dual: 0}, p: negZero, want: Number{Real: 1, Dual: 0}},
288	{d: Number{Real: 0, Dual: 1}, p: 0, want: Number{Real: 1, Dual: 0}},
289	{d: Number{Real: negZeroCmplx, Dual: 1}, p: negZero, want: Number{Real: 1, Dual: 0}},
290	{d: Number{Real: cmplx.Inf(), Dual: 1}, p: 0, want: Number{Real: 1, Dual: 0}},
291	{d: Number{Real: cmplx.Inf(), Dual: 1}, p: negZero, want: Number{Real: 1, Dual: 0}},
292
293	// PowReal(1+xϵ, y) = (1+xyϵ) for any y
294	{d: Number{Real: 1, Dual: 0}, p: 0, want: Number{Real: 1, Dual: 0}},
295	{d: Number{Real: 1, Dual: 0}, p: 1, want: Number{Real: 1, Dual: 0}},
296	{d: Number{Real: 1, Dual: 0}, p: 2, want: Number{Real: 1, Dual: 0}},
297	{d: Number{Real: 1, Dual: 0}, p: 3, want: Number{Real: 1, Dual: 0}},
298	{d: Number{Real: 1, Dual: 1}, p: 0, want: Number{Real: 1, Dual: 0}},
299	{d: Number{Real: 1, Dual: 1}, p: 1, want: Number{Real: 1, Dual: 1}},
300	{d: Number{Real: 1, Dual: 1}, p: 2, want: Number{Real: 1, Dual: 2}},
301	{d: Number{Real: 1, Dual: 1}, p: 3, want: Number{Real: 1, Dual: 3}},
302	{d: Number{Real: 1, Dual: 2}, p: 0, want: Number{Real: 1, Dual: 0}},
303	{d: Number{Real: 1, Dual: 2}, p: 1, want: Number{Real: 1, Dual: 2}},
304	{d: Number{Real: 1, Dual: 2}, p: 2, want: Number{Real: 1, Dual: 4}},
305	{d: Number{Real: 1, Dual: 2}, p: 3, want: Number{Real: 1, Dual: 6}},
306
307	// Pow(Inf, y) = +Inf+NaNϵ for y > 0
308	{d: Number{Real: cmplx.Inf()}, p: 0.5, want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
309	{d: Number{Real: cmplx.Inf()}, p: 1, want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
310	{d: Number{Real: cmplx.Inf()}, p: 1.1, want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
311	{d: Number{Real: cmplx.Inf()}, p: 2, want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
312	// Pow(Inf, y) = +0+NaNϵ for y < 0
313	{d: Number{Real: cmplx.Inf()}, p: -0.5, want: Number{Real: 0, Dual: cmplx.NaN()}},
314	{d: Number{Real: cmplx.Inf()}, p: -1, want: Number{Real: 0, Dual: cmplx.NaN()}},
315	{d: Number{Real: cmplx.Inf()}, p: -1.1, want: Number{Real: 0, Dual: cmplx.NaN()}},
316	{d: Number{Real: cmplx.Inf()}, p: -2, want: Number{Real: 0, Dual: cmplx.NaN()}},
317
318	// PowReal(x, 1) = x for any x
319	{d: Number{Real: 0, Dual: 0}, p: 1, want: Number{Real: 0, Dual: 0}},
320	{d: Number{Real: negZeroCmplx, Dual: 0}, p: 1, want: Number{Real: negZeroCmplx, Dual: 0}},
321	{d: Number{Real: 0, Dual: 1}, p: 1, want: Number{Real: 0, Dual: 1}},
322	{d: Number{Real: negZeroCmplx, Dual: 1}, p: 1, want: Number{Real: negZeroCmplx, Dual: 1}},
323	{d: Number{Real: cmplx.NaN(), Dual: 0}, p: 1, want: Number{Real: cmplx.NaN(), Dual: 0}},
324	{d: Number{Real: cmplx.NaN(), Dual: 1}, p: 1, want: Number{Real: cmplx.NaN(), Dual: 1}},
325	{d: Number{Real: cmplx.NaN(), Dual: 2}, p: 1, want: Number{Real: cmplx.NaN(), Dual: 2}},
326
327	// PowReal(NaN+xϵ, y) = NaN+NaNϵ
328	{d: Number{Real: cmplx.NaN(), Dual: 0}, p: 2, want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
329	{d: Number{Real: cmplx.NaN(), Dual: 0}, p: 3, want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
330	{d: Number{Real: cmplx.NaN(), Dual: 1}, p: 2, want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
331	{d: Number{Real: cmplx.NaN(), Dual: 1}, p: 3, want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
332	{d: Number{Real: cmplx.NaN(), Dual: 2}, p: 2, want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
333	{d: Number{Real: cmplx.NaN(), Dual: 2}, p: 3, want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
334
335	// PowReal(x, NaN) = NaN+NaNϵ
336	{d: Number{Real: 0, Dual: 0}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
337	{d: Number{Real: 2, Dual: 0}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
338	{d: Number{Real: 3, Dual: 0}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
339	{d: Number{Real: 0, Dual: 1}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
340	{d: Number{Real: 2, Dual: 1}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
341	{d: Number{Real: 3, Dual: 1}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
342	{d: Number{Real: 0, Dual: 2}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
343	{d: Number{Real: 2, Dual: 2}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
344	{d: Number{Real: 3, Dual: 2}, p: math.NaN(), want: Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}},
345
346	// Pow(-1, ±Inf) = 1
347	{d: Number{Real: -1}, p: math.Inf(-1), want: Number{Real: 1, Dual: cmplx.NaN()}},
348	{d: Number{Real: -1}, p: math.Inf(1), want: Number{Real: 1, Dual: cmplx.NaN()}},
349
350	// The following tests described for cmplx.Pow ar enot valid for this type and
351	// are handled by the special cases Pow(0+xϵ, y) above.
352	// Pow(±0, y) = ±Inf for y an odd integer < 0
353	// Pow(±0, -Inf) = +Inf
354	// Pow(±0, +Inf) = +0
355	// Pow(±0, y) = +Inf for finite y < 0 and not an odd integer
356	// Pow(±0, y) = ±0 for y an odd integer > 0
357	// Pow(±0, y) = +0 for finite y > 0 and not an odd integer
358
359	// PowReal(x+0ϵ, +Inf) = +Inf+NaNϵ for |x| > 1
360	{d: Number{Real: 2, Dual: 0}, p: math.Inf(1), want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
361	{d: Number{Real: 3, Dual: 0}, p: math.Inf(1), want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
362
363	// PowReal(x+yϵ, +Inf) = +Inf for |x| > 1
364	{d: Number{Real: 2, Dual: 1}, p: math.Inf(1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
365	{d: Number{Real: 3, Dual: 1}, p: math.Inf(1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
366	{d: Number{Real: 2, Dual: 2}, p: math.Inf(1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
367	{d: Number{Real: 3, Dual: 2}, p: math.Inf(1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
368
369	// PowReal(x, -Inf) = +0+NaNϵ for |x| > 1
370	{d: Number{Real: 2, Dual: 0}, p: math.Inf(-1), want: Number{Real: 0, Dual: cmplx.NaN()}},
371	{d: Number{Real: 3, Dual: 0}, p: math.Inf(-1), want: Number{Real: 0, Dual: cmplx.NaN()}},
372	{d: Number{Real: 2, Dual: 1}, p: math.Inf(-1), want: Number{Real: 0, Dual: cmplx.NaN()}},
373	{d: Number{Real: 3, Dual: 1}, p: math.Inf(-1), want: Number{Real: 0, Dual: cmplx.NaN()}},
374	{d: Number{Real: 2, Dual: 2}, p: math.Inf(-1), want: Number{Real: 0, Dual: cmplx.NaN()}},
375	{d: Number{Real: 3, Dual: 2}, p: math.Inf(-1), want: Number{Real: 0, Dual: cmplx.NaN()}},
376
377	// PowReal(x+yϵ, +Inf) = +0+NaNϵ for |x| < 1
378	{d: Number{Real: 0.1, Dual: 0}, p: math.Inf(1), want: Number{Real: 0, Dual: cmplx.NaN()}},
379	{d: Number{Real: 0.1, Dual: 0.1}, p: math.Inf(1), want: Number{Real: 0, Dual: cmplx.NaN()}},
380	{d: Number{Real: 0.2, Dual: 0.2}, p: math.Inf(1), want: Number{Real: 0, Dual: cmplx.NaN()}},
381	{d: Number{Real: 0.5, Dual: 0.5}, p: math.Inf(1), want: Number{Real: 0, Dual: cmplx.NaN()}},
382
383	// PowReal(x+0ϵ, -Inf) = +Inf+NaNϵ for |x| < 1
384	{d: Number{Real: 0.1, Dual: 0}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
385	{d: Number{Real: 0.2, Dual: 0}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}},
386
387	// PowReal(x, -Inf) = +Inf-Infϵ for |x| < 1
388	{d: Number{Real: 0.1, Dual: 0.1}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
389	{d: Number{Real: 0.2, Dual: 0.1}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
390	{d: Number{Real: 0.1, Dual: 0.2}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
391	{d: Number{Real: 0.2, Dual: 0.2}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
392	{d: Number{Real: 0.1, Dual: 1}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
393	{d: Number{Real: 0.2, Dual: 1}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
394	{d: Number{Real: 0.1, Dual: 2}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
395	{d: Number{Real: 0.2, Dual: 2}, p: math.Inf(-1), want: Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}},
396}
397
398func TestPowRealSpecial(t *testing.T) {
399	t.Parallel()
400	const tol = 1e-15
401	for _, test := range powRealSpecialTests {
402		got := PowReal(test.d, test.p)
403		if !sameDual(got, test.want, tol) {
404			t.Errorf("unexpected PowReal(%v, %v): got:%v want:%v", test.d, test.p, got, test.want)
405		}
406	}
407}
408
409var powRealTests = []struct {
410	d Number
411	p float64
412}{
413	{d: Number{Real: 1e-1, Dual: 2 + 2i}, p: 0.2},
414	{d: Number{Real: 1e-1, Dual: 2 + 2i}, p: 5},
415	{d: Number{Real: 1e-2, Dual: 2 + 2i}, p: 0.2},
416	{d: Number{Real: 1e-2, Dual: 2 + 2i}, p: 5},
417	{d: Number{Real: 1e-3, Dual: 2 + 2i}, p: 0.2},
418	{d: Number{Real: 1e-3, Dual: 2 + 2i}, p: 5},
419	{d: Number{Real: 1e-4, Dual: 2 + 2i}, p: 0.2},
420	{d: Number{Real: 1e-4, Dual: 2 + 2i}, p: 5},
421	{d: Number{Real: 2, Dual: 0}, p: 0.5},
422	{d: Number{Real: 2, Dual: 0}, p: 2},
423	{d: Number{Real: 4, Dual: 0}, p: 0.5},
424	{d: Number{Real: 4, Dual: 0}, p: 2},
425	{d: Number{Real: 8, Dual: 0}, p: 1.0 / 3},
426	{d: Number{Real: 8, Dual: 0}, p: 3},
427}
428
429func TestPowReal(t *testing.T) {
430	t.Parallel()
431	const tol = 1e-14
432	for _, test := range powRealTests {
433		got := PowReal(PowReal(test.d, test.p), 1/test.p)
434		if !sameDual(got, test.d, tol) {
435			t.Errorf("unexpected PowReal(PowReal(%v, %v), 1/%[2]v): got:%v want:%[1]v", test.d, test.p, got)
436		}
437		if test.p != math.Floor(test.p) {
438			continue
439		}
440		root := PowReal(test.d, 1/test.p)
441		got = Number{Real: 1}
442		for i := 0; i < int(test.p); i++ {
443			got = Mul(got, root)
444		}
445		if !sameDual(got, test.d, tol) {
446			t.Errorf("unexpected PowReal(%v, 1/%v)^%[2]v: got:%v want:%[1]v", test.d, test.p, got)
447		}
448	}
449}
450
451func sameDual(a, b Number, tol float64) bool {
452	return same(a.Real, b.Real, tol) && same(a.Dual, b.Dual, tol)
453}
454
455func same(a, b complex128, tol float64) bool {
456	return ((math.IsNaN(real(a)) && (math.IsNaN(real(b)))) || scalar.EqualWithinAbsOrRel(real(a), real(b), tol, tol)) &&
457		((math.IsNaN(imag(a)) && (math.IsNaN(imag(b)))) || scalar.EqualWithinAbsOrRel(imag(a), imag(b), tol, tol))
458}
459