gonum.org/v1/gonum@v0.14.0/num/hyperdual/hyperdual_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 hyperdual 6 7 import ( 8 "fmt" 9 "math" 10 "testing" 11 12 "gonum.org/v1/gonum/floats/scalar" 13 ) 14 15 var formatTests = []struct { 16 h Number 17 format string 18 want string 19 }{ 20 {h: Number{1.1, 2.1, 3.1, 4.1}, format: "%#v", want: "hyperdual.Number{Real:1.1, E1mag:2.1, E2mag:3.1, E1E2mag:4.1}"}, // Bootstrap test. 21 {h: Number{-1.1, -2.1, -3.1, -4.1}, format: "%#v", want: "hyperdual.Number{Real:-1.1, E1mag:-2.1, E2mag:-3.1, E1E2mag:-4.1}"}, // Bootstrap test. 22 {h: Number{1.1, 2.1, 3.1, 4.1}, format: "%+v", want: "{Real:1.1, E1mag:2.1, E2mag:3.1, E1E2mag:4.1}"}, 23 {h: Number{-1.1, -2.1, -3.1, -4.1}, format: "%+v", want: "{Real:-1.1, E1mag:-2.1, E2mag:-3.1, E1E2mag:-4.1}"}, 24 {h: Number{1, 2, 3, 4}, format: "%v", want: "(1+2ϵ₁+3ϵ₂+4ϵ₁ϵ₂)"}, 25 {h: Number{-1, -2, -3, -4}, format: "%v", want: "(-1-2ϵ₁-3ϵ₂-4ϵ₁ϵ₂)"}, 26 {h: Number{1, 2, 3, 4}, format: "%g", want: "(1+2ϵ₁+3ϵ₂+4ϵ₁ϵ₂)"}, 27 {h: Number{-1, -2, -3, -4}, format: "%g", want: "(-1-2ϵ₁-3ϵ₂-4ϵ₁ϵ₂)"}, 28 {h: Number{1, 2, 3, 4}, format: "%e", want: "(1.000000e+00+2.000000e+00ϵ₁+3.000000e+00ϵ₂+4.000000e+00ϵ₁ϵ₂)"}, 29 {h: Number{-1, -2, -3, -4}, format: "%e", want: "(-1.000000e+00-2.000000e+00ϵ₁-3.000000e+00ϵ₂-4.000000e+00ϵ₁ϵ₂)"}, 30 {h: Number{1, 2, 3, 4}, format: "%E", want: "(1.000000E+00+2.000000E+00ϵ₁+3.000000E+00ϵ₂+4.000000E+00ϵ₁ϵ₂)"}, 31 {h: Number{-1, -2, -3, -4}, format: "%E", want: "(-1.000000E+00-2.000000E+00ϵ₁-3.000000E+00ϵ₂-4.000000E+00ϵ₁ϵ₂)"}, 32 {h: Number{1, 2, 3, 4}, format: "%f", want: "(1.000000+2.000000ϵ₁+3.000000ϵ₂+4.000000ϵ₁ϵ₂)"}, 33 {h: Number{-1, -2, -3, -4}, format: "%f", want: "(-1.000000-2.000000ϵ₁-3.000000ϵ₂-4.000000ϵ₁ϵ₂)"}, 34 } 35 36 func TestFormat(t *testing.T) { 37 t.Parallel() 38 for _, test := range formatTests { 39 got := fmt.Sprintf(test.format, test.h) 40 if got != test.want { 41 t.Errorf("unexpected result for fmt.Sprintf(%q, %#v): got:%q, want:%q", test.format, test.h, got, test.want) 42 } 43 } 44 } 45 46 // First derivatives: 47 48 func dSin(x float64) float64 { return math.Cos(x) } 49 func dCos(x float64) float64 { return -math.Sin(x) } 50 func dTan(x float64) float64 { return sec(x) * sec(x) } 51 func dAsin(x float64) float64 { return 1 / math.Sqrt(1-x*x) } 52 func dAcos(x float64) float64 { return -1 / math.Sqrt(1-x*x) } 53 func dAtan(x float64) float64 { return 1 / (1 + x*x) } 54 55 func dSinh(x float64) float64 { return math.Cosh(x) } 56 func dCosh(x float64) float64 { return math.Sinh(x) } 57 func dTanh(x float64) float64 { return sech(x) * sech(x) } 58 func dAsinh(x float64) float64 { return 1 / math.Sqrt(x*x+1) } 59 func dAcosh(x float64) float64 { return 1 / (math.Sqrt(x-1) * math.Sqrt(x+1)) } 60 func dAtanh(x float64) float64 { 61 switch { 62 case math.Abs(x) == 1: 63 return math.NaN() 64 case math.IsInf(x, 0): 65 return negZero 66 } 67 return 1 / (1 - x*x) 68 } 69 70 func dExp(x float64) float64 { return math.Exp(x) } 71 func dLog(x float64) float64 { 72 if x < 0 { 73 return math.NaN() 74 } 75 return 1 / x 76 } 77 func dSqrt(x float64) float64 { 78 // For whatever reason, math.Sqrt(-0) returns -0. 79 // In this case, that is clearly a wrong approach. 80 if x == 0 { 81 return math.Inf(1) 82 } 83 return 0.5 / math.Sqrt(x) 84 } 85 func dInv(x float64) float64 { return -1 / (x * x) } 86 87 // Second derivatives: 88 89 func d2Sin(x float64) float64 { return -math.Sin(x) } 90 func d2Cos(x float64) float64 { return -math.Cos(x) } 91 func d2Tan(x float64) float64 { return 2 * math.Tan(x) * sec(x) * sec(x) } 92 func d2Asin(x float64) float64 { return x / math.Pow(1-x*x, 1.5) } 93 func d2Acos(x float64) float64 { return -x / math.Pow(1-x*x, 1.5) } 94 func d2Atan(x float64) float64 { return -2 * x / ((x*x + 1) * (x*x + 1)) } 95 96 func d2Sinh(x float64) float64 { return math.Sinh(x) } 97 func d2Cosh(x float64) float64 { return math.Cosh(x) } 98 func d2Tanh(x float64) float64 { return -2 * math.Tanh(x) * sech(x) * sech(x) } 99 func d2Asinh(x float64) float64 { return -x / math.Pow((x*x+1), 1.5) } 100 func d2Acosh(x float64) float64 { return -x / (math.Pow(x-1, 1.5) * math.Pow(x+1, 1.5)) } 101 func d2Atanh(x float64) float64 { return 2 * x / ((1 - x*x) * (1 - x*x)) } 102 103 func d2Exp(x float64) float64 { return math.Exp(x) } 104 func d2Log(x float64) float64 { 105 if x < 0 { 106 return math.NaN() 107 } 108 return -1 / (x * x) 109 } 110 func d2Sqrt(x float64) float64 { 111 // Again math.Sqyu, and math.Pow are odd. 112 switch x { 113 case math.Inf(1): 114 return 0 115 case math.Inf(-1): 116 return math.NaN() 117 } 118 return -0.25 * math.Pow(x, -1.5) 119 } 120 func d2Inv(x float64) float64 { return 2 / (x * x * x) } 121 122 // Helpers: 123 124 func sec(x float64) float64 { return 1 / math.Cos(x) } 125 func sech(x float64) float64 { return 1 / math.Cosh(x) } 126 127 var hyperdualTests = []struct { 128 name string 129 x []float64 130 fnHyperdual func(x Number) Number 131 fn func(x float64) float64 132 dFn func(x float64) float64 133 d2Fn func(x float64) float64 134 }{ 135 { 136 name: "sin", 137 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 138 fnHyperdual: Sin, 139 fn: math.Sin, 140 dFn: dSin, 141 d2Fn: d2Sin, 142 }, 143 { 144 name: "cos", 145 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 146 fnHyperdual: Cos, 147 fn: math.Cos, 148 dFn: dCos, 149 d2Fn: d2Cos, 150 }, 151 { 152 name: "tan", 153 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 154 fnHyperdual: Tan, 155 fn: math.Tan, 156 dFn: dTan, 157 d2Fn: d2Tan, 158 }, 159 { 160 name: "sinh", 161 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 162 fnHyperdual: Sinh, 163 fn: math.Sinh, 164 dFn: dSinh, 165 d2Fn: d2Sinh, 166 }, 167 { 168 name: "cosh", 169 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 170 fnHyperdual: Cosh, 171 fn: math.Cosh, 172 dFn: dCosh, 173 d2Fn: d2Cosh, 174 }, 175 { 176 name: "tanh", 177 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 178 fnHyperdual: Tanh, 179 fn: math.Tanh, 180 dFn: dTanh, 181 d2Fn: d2Tanh, 182 }, 183 184 { 185 name: "asin", 186 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 187 fnHyperdual: Asin, 188 fn: math.Asin, 189 dFn: dAsin, 190 d2Fn: d2Asin, 191 }, 192 { 193 name: "acos", 194 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 195 fnHyperdual: Acos, 196 fn: math.Acos, 197 dFn: dAcos, 198 d2Fn: d2Acos, 199 }, 200 { 201 name: "atan", 202 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 203 fnHyperdual: Atan, 204 fn: math.Atan, 205 dFn: dAtan, 206 d2Fn: d2Atan, 207 }, 208 { 209 name: "asinh", 210 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 211 fnHyperdual: Asinh, 212 fn: math.Asinh, 213 dFn: dAsinh, 214 d2Fn: d2Asinh, 215 }, 216 { 217 name: "acosh", 218 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 219 fnHyperdual: Acosh, 220 fn: math.Acosh, 221 dFn: dAcosh, 222 d2Fn: d2Acosh, 223 }, 224 { 225 name: "atanh", 226 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 227 fnHyperdual: Atanh, 228 fn: math.Atanh, 229 dFn: dAtanh, 230 d2Fn: d2Atanh, 231 }, 232 233 { 234 name: "exp", 235 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 236 fnHyperdual: Exp, 237 fn: math.Exp, 238 dFn: dExp, 239 d2Fn: d2Exp, 240 }, 241 { 242 name: "log", 243 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 244 fnHyperdual: Log, 245 fn: math.Log, 246 dFn: dLog, 247 d2Fn: d2Log, 248 }, 249 { 250 name: "inv", 251 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 252 fnHyperdual: Inv, 253 fn: func(x float64) float64 { return 1 / x }, 254 dFn: dInv, 255 d2Fn: d2Inv, 256 }, 257 { 258 name: "sqrt", 259 x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)}, 260 fnHyperdual: Sqrt, 261 fn: math.Sqrt, 262 dFn: dSqrt, 263 d2Fn: d2Sqrt, 264 }, 265 266 { 267 name: "Fike example fn", 268 x: []float64{1, 2, 3, 4, 5}, 269 fnHyperdual: func(x Number) Number { 270 return Mul( 271 Exp(x), 272 Inv(Sqrt( 273 Add( 274 PowReal(Sin(x), 3), 275 PowReal(Cos(x), 3))))) 276 }, 277 fn: func(x float64) float64 { 278 return math.Exp(x) / math.Sqrt(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3)) 279 }, 280 dFn: func(x float64) float64 { 281 return math.Exp(x) * (3*math.Cos(x) + 5*math.Cos(3*x) + 9*math.Sin(x) + math.Sin(3*x)) / 282 (8 * math.Pow(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3), 1.5)) 283 }, 284 d2Fn: func(x float64) float64 { 285 return math.Exp(x) * (130 - 12*math.Cos(2*x) + 30*math.Cos(4*x) + 12*math.Cos(6*x) - 111*math.Sin(2*x) + 48*math.Sin(4*x) + 5*math.Sin(6*x)) / 286 (64 * math.Pow(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3), 2.5)) 287 }, 288 }, 289 } 290 291 func TestHyperdual(t *testing.T) { 292 t.Parallel() 293 const tol = 1e-14 294 for _, test := range hyperdualTests { 295 for _, x := range test.x { 296 fxHyperdual := test.fnHyperdual(Number{Real: x, E1mag: 1, E2mag: 1}) 297 fx := test.fn(x) 298 dFx := test.dFn(x) 299 d2Fx := test.d2Fn(x) 300 if !same(fxHyperdual.Real, fx, tol) { 301 t.Errorf("unexpected %s(%v): got:%v want:%v", test.name, x, fxHyperdual.Real, fx) 302 } 303 if !same(fxHyperdual.E1mag, dFx, tol) { 304 t.Errorf("unexpected %s′(%v) (ϵ₁): got:%v want:%v", test.name, x, fxHyperdual.E1mag, dFx) 305 } 306 if !same(fxHyperdual.E1mag, fxHyperdual.E2mag, tol) { 307 t.Errorf("mismatched ϵ₁ and ϵ₂ for %s(%v): ϵ₁:%v ϵ₂:%v", test.name, x, fxHyperdual.E1mag, fxHyperdual.E2mag) 308 } 309 if !same(fxHyperdual.E1E2mag, d2Fx, tol) { 310 t.Errorf("unexpected %s′′(%v): got:%v want:%v", test.name, x, fxHyperdual.E1E2mag, d2Fx) 311 } 312 } 313 } 314 } 315 316 var powRealTests = []struct { 317 d Number 318 p float64 319 want Number 320 }{ 321 // PowReal(NaN+xϵ₁+yϵ₂, ±0) = 1+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for any x and y 322 {d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 323 {d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 324 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 325 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 326 {d: Number{Real: math.NaN(), E1mag: 3, E2mag: 3}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 327 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 328 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 329 {d: Number{Real: math.NaN(), E1mag: 3, E2mag: 3}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 330 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 3}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 331 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 3}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 332 333 // PowReal(x, ±0) = 1 for any x 334 {d: Number{Real: 0, E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}}, 335 {d: Number{Real: math.Inf(1), E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}}, 336 {d: Number{Real: math.Inf(-1), E1mag: 0, E2mag: 0}, p: negZero, want: Number{Real: 1, E1mag: 0, E2mag: 0}}, 337 {d: Number{Real: 0, E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}}, 338 {d: Number{Real: math.Inf(1), E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}}, 339 {d: Number{Real: math.Inf(-1), E1mag: 1, E2mag: 1}, p: negZero, want: Number{Real: 1, E1mag: 0, E2mag: 0}}, 340 // These two satisfy the claim above, but the sign of zero is negative. Do we care? 341 {d: Number{Real: negZero, E1mag: 0, E2mag: 0}, p: negZero, want: Number{Real: 1, E1mag: negZero, E2mag: negZero}}, 342 {d: Number{Real: negZero, E1mag: 1, E2mag: 1}, p: negZero, want: Number{Real: 1, E1mag: negZero, E2mag: negZero}}, 343 344 // PowReal(1+xϵ₁+yϵ₂, z) = 1+xzϵ₁+yzϵ₂+2xyzϵ₁ϵ₂ for any z 345 {d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 346 {d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 347 {d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 2, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 348 {d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 3, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 349 {d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 350 {d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: 1, E1mag: 1, E2mag: 1, E1E2mag: 0}}, 351 {d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 2, want: Number{Real: 1, E1mag: 2, E2mag: 2, E1E2mag: 2}}, 352 {d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 3, want: Number{Real: 1, E1mag: 3, E2mag: 3, E1E2mag: 6}}, 353 {d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 354 {d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 1, want: Number{Real: 1, E1mag: 2, E2mag: 2, E1E2mag: 0}}, 355 {d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 2, want: Number{Real: 1, E1mag: 4, E2mag: 4, E1E2mag: 8}}, 356 {d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 3, want: Number{Real: 1, E1mag: 6, E2mag: 6, E1E2mag: 24}}, 357 {d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}}, 358 {d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: 1, E1mag: 1, E2mag: 2, E1E2mag: 0}}, 359 {d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 2, want: Number{Real: 1, E1mag: 2, E2mag: 4, E1E2mag: 4}}, 360 {d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 3, want: Number{Real: 1, E1mag: 3, E2mag: 6, E1E2mag: 12}}, 361 362 // PowReal(NaN+xϵ₁+yϵ₂, 1) = NaN+xϵ₁+yϵ₂+NaNϵ₁ϵ₂ for any x 363 {d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: math.NaN(), E1mag: 0, E2mag: 0, E1E2mag: math.NaN()}}, 364 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: math.NaN(), E1mag: 1, E2mag: 1, E1E2mag: math.NaN()}}, 365 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: 1, want: Number{Real: math.NaN(), E1mag: 2, E2mag: 2, E1E2mag: math.NaN()}}, 366 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: math.NaN(), E1mag: 1, E2mag: 2, E1E2mag: math.NaN()}}, 367 368 // PowReal(x, 1) = x for any x 369 {d: Number{Real: 0, E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: 0, E1mag: 0, E2mag: 0}}, 370 {d: Number{Real: negZero, E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: negZero, E1mag: 0, E2mag: 0}}, 371 {d: Number{Real: 0, E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: 0, E1mag: 1, E2mag: 1}}, 372 {d: Number{Real: negZero, E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: negZero, E1mag: 1, E2mag: 1}}, 373 {d: Number{Real: 0, E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: 0, E1mag: 1, E2mag: 2}}, 374 {d: Number{Real: negZero, E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: negZero, E1mag: 1, E2mag: 2}}, 375 376 // PowReal(NaN+xϵ₁+xϵ₂, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ 377 {d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: 2, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 378 {d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: 3, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 379 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: 2, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 380 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: 3, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 381 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: 2, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 382 {d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: 3, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 383 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 2}, p: 2, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 384 {d: Number{Real: math.NaN(), E1mag: 1, E2mag: 2}, p: 3, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 385 386 // PowReal(x, NaN) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ 387 {d: Number{Real: 0, E1mag: 0, E2mag: 0}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 388 {d: Number{Real: 2, E1mag: 0, E2mag: 0}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 389 {d: Number{Real: 3, E1mag: 0, E2mag: 0}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 390 {d: Number{Real: 0, E1mag: 1, E2mag: 1}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 391 {d: Number{Real: 2, E1mag: 1, E2mag: 1}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 392 {d: Number{Real: 3, E1mag: 1, E2mag: 1}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 393 {d: Number{Real: 0, E1mag: 2, E2mag: 2}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 394 {d: Number{Real: 2, E1mag: 2, E2mag: 2}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 395 {d: Number{Real: 3, E1mag: 2, E2mag: 2}, p: math.NaN(), want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 396 397 // Handled by math.Pow tests: 398 // 399 // Pow(±0, y) = ±Inf for y an odd integer < 0 400 // Pow(±0, -Inf) = +Inf 401 // Pow(±0, +Inf) = +0 402 // Pow(±0, y) = +Inf for finite y < 0 and not an odd integer 403 // Pow(±0, y) = ±0 for y an odd integer > 0 404 // Pow(±0, y) = +0 for finite y > 0 and not an odd integer 405 // Pow(-1, ±Inf) = 1 406 407 // PowReal(x+0ϵ₁+0ϵ₂, +Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1 408 {d: Number{Real: 2, E1mag: 0, E2mag: 0}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 409 {d: Number{Real: 3, E1mag: 0, E2mag: 0}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 410 411 // PowReal(x+xϵ₁+yϵ₂, +Inf) = +Inf+Infϵ₁+Infϵ₂+NaNϵ₁ϵ₂ for |x| > 1 412 {d: Number{Real: 2, E1mag: 1, E2mag: 1}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.Inf(1), E2mag: math.Inf(1), E1E2mag: math.NaN()}}, 413 {d: Number{Real: 3, E1mag: 1, E2mag: 1}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.Inf(1), E2mag: math.Inf(1), E1E2mag: math.NaN()}}, 414 {d: Number{Real: 2, E1mag: 2, E2mag: 2}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.Inf(1), E2mag: math.Inf(1), E1E2mag: math.NaN()}}, 415 {d: Number{Real: 3, E1mag: 2, E2mag: 2}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.Inf(1), E2mag: math.Inf(1), E1E2mag: math.NaN()}}, 416 {d: Number{Real: 3, E1mag: 2, E2mag: 3}, p: math.Inf(1), want: Number{Real: math.Inf(1), E1mag: math.Inf(1), E2mag: math.Inf(1), E1E2mag: math.NaN()}}, 417 418 // PowReal(x, -Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1 419 {d: Number{Real: 2, E1mag: 0, E2mag: 0}, p: math.Inf(-1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 420 {d: Number{Real: 3, E1mag: 0, E2mag: 0}, p: math.Inf(-1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 421 {d: Number{Real: 2, E1mag: 1, E2mag: 1}, p: math.Inf(-1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 422 {d: Number{Real: 3, E1mag: 1, E2mag: 1}, p: math.Inf(-1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 423 {d: Number{Real: 2, E1mag: 2, E2mag: 2}, p: math.Inf(-1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 424 {d: Number{Real: 3, E1mag: 2, E2mag: 2}, p: math.Inf(-1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 425 426 // PowReal(x+yϵ₁+zϵ₂, +Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1 427 {d: Number{Real: 0.1, E1mag: 0, E2mag: 0}, p: math.Inf(1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 428 {d: Number{Real: 0.1, E1mag: 0.1, E2mag: 0.1}, p: math.Inf(1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 429 {d: Number{Real: 0.2, E1mag: 0.2, E2mag: 0.2}, p: math.Inf(1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 430 {d: Number{Real: 0.5, E1mag: 0.3, E2mag: 0.5}, p: math.Inf(1), want: Number{Real: 0, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 431 432 // PowReal(x+0ϵ₁+0ϵ₂, -Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1 433 {d: Number{Real: 0.1, E1mag: 0, E2mag: 0}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 434 {d: Number{Real: 0.2, E1mag: 0, E2mag: 0}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 435 436 // PowReal(x, -Inf) = +Inf-Infϵ₁-Infϵ₂+NaNϵ₁ϵ₂ for |x| < 1 437 {d: Number{Real: 0.1, E1mag: 0.1, E2mag: 0.1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 438 {d: Number{Real: 0.2, E1mag: 0.1, E2mag: 0.1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 439 {d: Number{Real: 0.1, E1mag: 0.2, E2mag: 0.2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 440 {d: Number{Real: 0.2, E1mag: 0.3, E2mag: 0.2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 441 {d: Number{Real: 0.1, E1mag: 1, E2mag: 1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 442 {d: Number{Real: 0.2, E1mag: 1, E2mag: 1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 443 {d: Number{Real: 0.1, E1mag: 2, E2mag: 2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 444 {d: Number{Real: 0.2, E1mag: 2, E2mag: 2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), E1mag: math.Inf(-1), E2mag: math.Inf(-1), E1E2mag: math.NaN()}}, 445 446 // Handled by math.Pow tests: 447 // 448 // Pow(+Inf, y) = +Inf for y > 0 449 // Pow(+Inf, y) = +0 for y < 0 450 // Pow(-Inf, y) = Pow(-0, -y) 451 452 // PowReal(x, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for finite x < 0 and finite non-integer y 453 {d: Number{Real: -1, E1mag: -1, E2mag: -1}, p: 0.5, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 454 {d: Number{Real: -1, E1mag: 2, E2mag: 2}, p: 0.5, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 455 {d: Number{Real: -1, E1mag: -1, E2mag: 2}, p: 0.5, want: Number{Real: math.NaN(), E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}}, 456 } 457 458 func TestPowReal(t *testing.T) { 459 t.Parallel() 460 const tol = 1e-15 461 for _, test := range powRealTests { 462 got := PowReal(test.d, test.p) 463 if !sameHyperdual(got, test.want, tol) { 464 t.Errorf("unexpected PowReal(%v, %v): got:%v want:%v", test.d, test.p, got, test.want) 465 } 466 } 467 } 468 469 func sameHyperdual(a, b Number, tol float64) bool { 470 return same(a.Real, b.Real, tol) && same(a.E1mag, b.E1mag, tol) && 471 same(a.E2mag, b.E2mag, tol) && same(a.E1E2mag, b.E1E2mag, tol) 472 } 473 474 func same(a, b, tol float64) bool { 475 return (math.IsNaN(a) && math.IsNaN(b)) || 476 (scalar.EqualWithinAbsOrRel(a, b, tol, tol) && math.Float64bits(a)&(1<<63) == math.Float64bits(b)&(1<<63)) 477 }