gonum.org/v1/gonum@v0.14.0/num/dualcmplx/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 dualcmplx
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/cmplx"
    11  	"testing"
    12  
    13  	"gonum.org/v1/gonum/floats/scalar"
    14  )
    15  
    16  var 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  
    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  // FIXME(kortschak): See golang/go#29320.
    48  
    49  func 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  
    81  func 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  }
    87  func 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  }
    99  func dInv(x complex128) complex128 { return -1 / (x * cmplx.Conj(x)) }
   100  
   101  var (
   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  
   115  var 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  
   152  func 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  
   173  var 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  
   183  func 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  
   195  var 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  
   222  func 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  
   234  func 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  
   249  var 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  
   398  func 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  
   409  var 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  
   429  func 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  
   451  func sameDual(a, b Number, tol float64) bool {
   452  	return same(a.Real, b.Real, tol) && same(a.Dual, b.Dual, tol)
   453  }
   454  
   455  func 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  }