mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 23:26:52 +08:00

This is necessary because gofmt in go1.19 imposes smart quotes on comments that use pairs of single quotes. Doubled-up single tick, U+2032, is chosen over double tick, U+2033, since the latter is harder to distinguish in many fonts at normally used font sizes, sometimes being indistinguishable from other superscript marks such as asterisk. Comparison: ′ ″ *
478 lines
24 KiB
Go
478 lines
24 KiB
Go
// 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 hyperdual
|
||
|
||
import (
|
||
"fmt"
|
||
"math"
|
||
"testing"
|
||
|
||
"gonum.org/v1/gonum/floats/scalar"
|
||
)
|
||
|
||
var formatTests = []struct {
|
||
h Number
|
||
format string
|
||
want string
|
||
}{
|
||
{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.
|
||
{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.
|
||
{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}"},
|
||
{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}"},
|
||
{h: Number{1, 2, 3, 4}, format: "%v", want: "(1+2ϵ₁+3ϵ₂+4ϵ₁ϵ₂)"},
|
||
{h: Number{-1, -2, -3, -4}, format: "%v", want: "(-1-2ϵ₁-3ϵ₂-4ϵ₁ϵ₂)"},
|
||
{h: Number{1, 2, 3, 4}, format: "%g", want: "(1+2ϵ₁+3ϵ₂+4ϵ₁ϵ₂)"},
|
||
{h: Number{-1, -2, -3, -4}, format: "%g", want: "(-1-2ϵ₁-3ϵ₂-4ϵ₁ϵ₂)"},
|
||
{h: Number{1, 2, 3, 4}, format: "%e", want: "(1.000000e+00+2.000000e+00ϵ₁+3.000000e+00ϵ₂+4.000000e+00ϵ₁ϵ₂)"},
|
||
{h: Number{-1, -2, -3, -4}, format: "%e", want: "(-1.000000e+00-2.000000e+00ϵ₁-3.000000e+00ϵ₂-4.000000e+00ϵ₁ϵ₂)"},
|
||
{h: Number{1, 2, 3, 4}, format: "%E", want: "(1.000000E+00+2.000000E+00ϵ₁+3.000000E+00ϵ₂+4.000000E+00ϵ₁ϵ₂)"},
|
||
{h: Number{-1, -2, -3, -4}, format: "%E", want: "(-1.000000E+00-2.000000E+00ϵ₁-3.000000E+00ϵ₂-4.000000E+00ϵ₁ϵ₂)"},
|
||
{h: Number{1, 2, 3, 4}, format: "%f", want: "(1.000000+2.000000ϵ₁+3.000000ϵ₂+4.000000ϵ₁ϵ₂)"},
|
||
{h: Number{-1, -2, -3, -4}, format: "%f", want: "(-1.000000-2.000000ϵ₁-3.000000ϵ₂-4.000000ϵ₁ϵ₂)"},
|
||
}
|
||
|
||
func TestFormat(t *testing.T) {
|
||
t.Parallel()
|
||
for _, test := range formatTests {
|
||
got := fmt.Sprintf(test.format, test.h)
|
||
if got != test.want {
|
||
t.Errorf("unexpected result for fmt.Sprintf(%q, %#v): got:%q, want:%q", test.format, test.h, got, test.want)
|
||
}
|
||
}
|
||
}
|
||
|
||
// First derivatives:
|
||
|
||
func dSin(x float64) float64 { return math.Cos(x) }
|
||
func dCos(x float64) float64 { return -math.Sin(x) }
|
||
func dTan(x float64) float64 { return sec(x) * sec(x) }
|
||
func dAsin(x float64) float64 { return 1 / math.Sqrt(1-x*x) }
|
||
func dAcos(x float64) float64 { return -1 / math.Sqrt(1-x*x) }
|
||
func dAtan(x float64) float64 { return 1 / (1 + x*x) }
|
||
|
||
func dSinh(x float64) float64 { return math.Cosh(x) }
|
||
func dCosh(x float64) float64 { return math.Sinh(x) }
|
||
func dTanh(x float64) float64 { return sech(x) * sech(x) }
|
||
func dAsinh(x float64) float64 { return 1 / math.Sqrt(x*x+1) }
|
||
func dAcosh(x float64) float64 { return 1 / (math.Sqrt(x-1) * math.Sqrt(x+1)) }
|
||
func dAtanh(x float64) float64 {
|
||
switch {
|
||
case math.Abs(x) == 1:
|
||
return math.NaN()
|
||
case math.IsInf(x, 0):
|
||
return negZero
|
||
}
|
||
return 1 / (1 - x*x)
|
||
}
|
||
|
||
func dExp(x float64) float64 { return math.Exp(x) }
|
||
func dLog(x float64) float64 {
|
||
if x < 0 {
|
||
return math.NaN()
|
||
}
|
||
return 1 / x
|
||
}
|
||
func dSqrt(x float64) float64 {
|
||
// For whatever reason, math.Sqrt(-0) returns -0.
|
||
// In this case, that is clearly a wrong approach.
|
||
if x == 0 {
|
||
return math.Inf(1)
|
||
}
|
||
return 0.5 / math.Sqrt(x)
|
||
}
|
||
func dInv(x float64) float64 { return -1 / (x * x) }
|
||
|
||
// Second derivatives:
|
||
|
||
func d2Sin(x float64) float64 { return -math.Sin(x) }
|
||
func d2Cos(x float64) float64 { return -math.Cos(x) }
|
||
func d2Tan(x float64) float64 { return 2 * math.Tan(x) * sec(x) * sec(x) }
|
||
func d2Asin(x float64) float64 { return x / math.Pow(1-x*x, 1.5) }
|
||
func d2Acos(x float64) float64 { return -x / math.Pow(1-x*x, 1.5) }
|
||
func d2Atan(x float64) float64 { return -2 * x / ((x*x + 1) * (x*x + 1)) }
|
||
|
||
func d2Sinh(x float64) float64 { return math.Sinh(x) }
|
||
func d2Cosh(x float64) float64 { return math.Cosh(x) }
|
||
func d2Tanh(x float64) float64 { return -2 * math.Tanh(x) * sech(x) * sech(x) }
|
||
func d2Asinh(x float64) float64 { return -x / math.Pow((x*x+1), 1.5) }
|
||
func d2Acosh(x float64) float64 { return -x / (math.Pow(x-1, 1.5) * math.Pow(x+1, 1.5)) }
|
||
func d2Atanh(x float64) float64 { return 2 * x / ((1 - x*x) * (1 - x*x)) }
|
||
|
||
func d2Exp(x float64) float64 { return math.Exp(x) }
|
||
func d2Log(x float64) float64 {
|
||
if x < 0 {
|
||
return math.NaN()
|
||
}
|
||
return -1 / (x * x)
|
||
}
|
||
func d2Sqrt(x float64) float64 {
|
||
// Again math.Sqyu, and math.Pow are odd.
|
||
switch x {
|
||
case math.Inf(1):
|
||
return 0
|
||
case math.Inf(-1):
|
||
return math.NaN()
|
||
}
|
||
return -0.25 * math.Pow(x, -1.5)
|
||
}
|
||
func d2Inv(x float64) float64 { return 2 / (x * x * x) }
|
||
|
||
// Helpers:
|
||
|
||
func sec(x float64) float64 { return 1 / math.Cos(x) }
|
||
func sech(x float64) float64 { return 1 / math.Cosh(x) }
|
||
|
||
var hyperdualTests = []struct {
|
||
name string
|
||
x []float64
|
||
fnHyperdual func(x Number) Number
|
||
fn func(x float64) float64
|
||
dFn func(x float64) float64
|
||
d2Fn func(x float64) float64
|
||
}{
|
||
{
|
||
name: "sin",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Sin,
|
||
fn: math.Sin,
|
||
dFn: dSin,
|
||
d2Fn: d2Sin,
|
||
},
|
||
{
|
||
name: "cos",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Cos,
|
||
fn: math.Cos,
|
||
dFn: dCos,
|
||
d2Fn: d2Cos,
|
||
},
|
||
{
|
||
name: "tan",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Tan,
|
||
fn: math.Tan,
|
||
dFn: dTan,
|
||
d2Fn: d2Tan,
|
||
},
|
||
{
|
||
name: "sinh",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Sinh,
|
||
fn: math.Sinh,
|
||
dFn: dSinh,
|
||
d2Fn: d2Sinh,
|
||
},
|
||
{
|
||
name: "cosh",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Cosh,
|
||
fn: math.Cosh,
|
||
dFn: dCosh,
|
||
d2Fn: d2Cosh,
|
||
},
|
||
{
|
||
name: "tanh",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Tanh,
|
||
fn: math.Tanh,
|
||
dFn: dTanh,
|
||
d2Fn: d2Tanh,
|
||
},
|
||
|
||
{
|
||
name: "asin",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Asin,
|
||
fn: math.Asin,
|
||
dFn: dAsin,
|
||
d2Fn: d2Asin,
|
||
},
|
||
{
|
||
name: "acos",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Acos,
|
||
fn: math.Acos,
|
||
dFn: dAcos,
|
||
d2Fn: d2Acos,
|
||
},
|
||
{
|
||
name: "atan",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Atan,
|
||
fn: math.Atan,
|
||
dFn: dAtan,
|
||
d2Fn: d2Atan,
|
||
},
|
||
{
|
||
name: "asinh",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Asinh,
|
||
fn: math.Asinh,
|
||
dFn: dAsinh,
|
||
d2Fn: d2Asinh,
|
||
},
|
||
{
|
||
name: "acosh",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Acosh,
|
||
fn: math.Acosh,
|
||
dFn: dAcosh,
|
||
d2Fn: d2Acosh,
|
||
},
|
||
{
|
||
name: "atanh",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Atanh,
|
||
fn: math.Atanh,
|
||
dFn: dAtanh,
|
||
d2Fn: d2Atanh,
|
||
},
|
||
|
||
{
|
||
name: "exp",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Exp,
|
||
fn: math.Exp,
|
||
dFn: dExp,
|
||
d2Fn: d2Exp,
|
||
},
|
||
{
|
||
name: "log",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Log,
|
||
fn: math.Log,
|
||
dFn: dLog,
|
||
d2Fn: d2Log,
|
||
},
|
||
{
|
||
name: "inv",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Inv,
|
||
fn: func(x float64) float64 { return 1 / x },
|
||
dFn: dInv,
|
||
d2Fn: d2Inv,
|
||
},
|
||
{
|
||
name: "sqrt",
|
||
x: []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
|
||
fnHyperdual: Sqrt,
|
||
fn: math.Sqrt,
|
||
dFn: dSqrt,
|
||
d2Fn: d2Sqrt,
|
||
},
|
||
|
||
{
|
||
name: "Fike example fn",
|
||
x: []float64{1, 2, 3, 4, 5},
|
||
fnHyperdual: func(x Number) Number {
|
||
return Mul(
|
||
Exp(x),
|
||
Inv(Sqrt(
|
||
Add(
|
||
PowReal(Sin(x), 3),
|
||
PowReal(Cos(x), 3)))))
|
||
},
|
||
fn: func(x float64) float64 {
|
||
return math.Exp(x) / math.Sqrt(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3))
|
||
},
|
||
dFn: func(x float64) float64 {
|
||
return math.Exp(x) * (3*math.Cos(x) + 5*math.Cos(3*x) + 9*math.Sin(x) + math.Sin(3*x)) /
|
||
(8 * math.Pow(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3), 1.5))
|
||
},
|
||
d2Fn: func(x float64) float64 {
|
||
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)) /
|
||
(64 * math.Pow(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3), 2.5))
|
||
},
|
||
},
|
||
}
|
||
|
||
func TestHyperdual(t *testing.T) {
|
||
t.Parallel()
|
||
const tol = 1e-14
|
||
for _, test := range hyperdualTests {
|
||
for _, x := range test.x {
|
||
fxHyperdual := test.fnHyperdual(Number{Real: x, E1mag: 1, E2mag: 1})
|
||
fx := test.fn(x)
|
||
dFx := test.dFn(x)
|
||
d2Fx := test.d2Fn(x)
|
||
if !same(fxHyperdual.Real, fx, tol) {
|
||
t.Errorf("unexpected %s(%v): got:%v want:%v", test.name, x, fxHyperdual.Real, fx)
|
||
}
|
||
if !same(fxHyperdual.E1mag, dFx, tol) {
|
||
t.Errorf("unexpected %s′(%v) (ϵ₁): got:%v want:%v", test.name, x, fxHyperdual.E1mag, dFx)
|
||
}
|
||
if !same(fxHyperdual.E1mag, fxHyperdual.E2mag, tol) {
|
||
t.Errorf("mismatched ϵ₁ and ϵ₂ for %s(%v): ϵ₁:%v ϵ₂:%v", test.name, x, fxHyperdual.E1mag, fxHyperdual.E2mag)
|
||
}
|
||
if !same(fxHyperdual.E1E2mag, d2Fx, tol) {
|
||
t.Errorf("unexpected %s′′(%v): got:%v want:%v", test.name, x, fxHyperdual.E1E2mag, d2Fx)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var powRealTests = []struct {
|
||
d Number
|
||
p float64
|
||
want Number
|
||
}{
|
||
// PowReal(NaN+xϵ₁+yϵ₂, ±0) = 1+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for any x and y
|
||
{d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 3, E2mag: 3}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 3, E2mag: 3}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 2, E2mag: 3}, p: 0, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 2, E2mag: 3}, p: negZero, want: Number{Real: 1, E1mag: math.NaN(), E2mag: math.NaN(), E1E2mag: math.NaN()}},
|
||
|
||
// PowReal(x, ±0) = 1 for any x
|
||
{d: Number{Real: 0, E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: math.Inf(1), E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: math.Inf(-1), E1mag: 0, E2mag: 0}, p: negZero, want: Number{Real: 1, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: 0, E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: math.Inf(1), E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: math.Inf(-1), E1mag: 1, E2mag: 1}, p: negZero, want: Number{Real: 1, E1mag: 0, E2mag: 0}},
|
||
// These two satisfy the claim above, but the sign of zero is negative. Do we care?
|
||
{d: Number{Real: negZero, E1mag: 0, E2mag: 0}, p: negZero, want: Number{Real: 1, E1mag: negZero, E2mag: negZero}},
|
||
{d: Number{Real: negZero, E1mag: 1, E2mag: 1}, p: negZero, want: Number{Real: 1, E1mag: negZero, E2mag: negZero}},
|
||
|
||
// PowReal(1+xϵ₁+yϵ₂, z) = 1+xzϵ₁+yzϵ₂+2xyzϵ₁ϵ₂ for any z
|
||
{d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 2, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 0, E2mag: 0}, p: 3, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: 1, E1mag: 1, E2mag: 1, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 2, want: Number{Real: 1, E1mag: 2, E2mag: 2, E1E2mag: 2}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 1}, p: 3, want: Number{Real: 1, E1mag: 3, E2mag: 3, E1E2mag: 6}},
|
||
{d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 1, want: Number{Real: 1, E1mag: 2, E2mag: 2, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 2, want: Number{Real: 1, E1mag: 4, E2mag: 4, E1E2mag: 8}},
|
||
{d: Number{Real: 1, E1mag: 2, E2mag: 2}, p: 3, want: Number{Real: 1, E1mag: 6, E2mag: 6, E1E2mag: 24}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 0, want: Number{Real: 1, E1mag: 0, E2mag: 0, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: 1, E1mag: 1, E2mag: 2, E1E2mag: 0}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 2, want: Number{Real: 1, E1mag: 2, E2mag: 4, E1E2mag: 4}},
|
||
{d: Number{Real: 1, E1mag: 1, E2mag: 2}, p: 3, want: Number{Real: 1, E1mag: 3, E2mag: 6, E1E2mag: 12}},
|
||
|
||
// PowReal(NaN+xϵ₁+yϵ₂, 1) = NaN+xϵ₁+yϵ₂+NaNϵ₁ϵ₂ for any x
|
||
{d: Number{Real: math.NaN(), E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: math.NaN(), E1mag: 0, E2mag: 0, E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: math.NaN(), E1mag: 1, E2mag: 1, E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 2, E2mag: 2}, p: 1, want: Number{Real: math.NaN(), E1mag: 2, E2mag: 2, E1E2mag: math.NaN()}},
|
||
{d: Number{Real: math.NaN(), E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: math.NaN(), E1mag: 1, E2mag: 2, E1E2mag: math.NaN()}},
|
||
|
||
// PowReal(x, 1) = x for any x
|
||
{d: Number{Real: 0, E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: 0, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: negZero, E1mag: 0, E2mag: 0}, p: 1, want: Number{Real: negZero, E1mag: 0, E2mag: 0}},
|
||
{d: Number{Real: 0, E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: 0, E1mag: 1, E2mag: 1}},
|
||
{d: Number{Real: negZero, E1mag: 1, E2mag: 1}, p: 1, want: Number{Real: negZero, E1mag: 1, E2mag: 1}},
|
||
{d: Number{Real: 0, E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: 0, E1mag: 1, E2mag: 2}},
|
||
{d: Number{Real: negZero, E1mag: 1, E2mag: 2}, p: 1, want: Number{Real: negZero, E1mag: 1, E2mag: 2}},
|
||
|
||
// PowReal(NaN+xϵ₁+xϵ₂, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// PowReal(x, NaN) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// Handled by math.Pow tests:
|
||
//
|
||
// Pow(±0, y) = ±Inf for y an odd integer < 0
|
||
// Pow(±0, -Inf) = +Inf
|
||
// Pow(±0, +Inf) = +0
|
||
// Pow(±0, y) = +Inf for finite y < 0 and not an odd integer
|
||
// Pow(±0, y) = ±0 for y an odd integer > 0
|
||
// Pow(±0, y) = +0 for finite y > 0 and not an odd integer
|
||
// Pow(-1, ±Inf) = 1
|
||
|
||
// PowReal(x+0ϵ₁+0ϵ₂, +Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// PowReal(x+xϵ₁+yϵ₂, +Inf) = +Inf+Infϵ₁+Infϵ₂+NaNϵ₁ϵ₂ for |x| > 1
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// PowReal(x, -Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// PowReal(x+yϵ₁+zϵ₂, +Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// PowReal(x+0ϵ₁+0ϵ₂, -Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// PowReal(x, -Inf) = +Inf-Infϵ₁-Infϵ₂+NaNϵ₁ϵ₂ for |x| < 1
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
|
||
// Handled by math.Pow tests:
|
||
//
|
||
// Pow(+Inf, y) = +Inf for y > 0
|
||
// Pow(+Inf, y) = +0 for y < 0
|
||
// Pow(-Inf, y) = Pow(-0, -y)
|
||
|
||
// PowReal(x, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for finite x < 0 and finite non-integer y
|
||
{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()}},
|
||
{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()}},
|
||
{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()}},
|
||
}
|
||
|
||
func TestPowReal(t *testing.T) {
|
||
t.Parallel()
|
||
const tol = 1e-15
|
||
for _, test := range powRealTests {
|
||
got := PowReal(test.d, test.p)
|
||
if !sameHyperdual(got, test.want, tol) {
|
||
t.Errorf("unexpected PowReal(%v, %v): got:%v want:%v", test.d, test.p, got, test.want)
|
||
}
|
||
}
|
||
}
|
||
|
||
func sameHyperdual(a, b Number, tol float64) bool {
|
||
return same(a.Real, b.Real, tol) && same(a.E1mag, b.E1mag, tol) &&
|
||
same(a.E2mag, b.E2mag, tol) && same(a.E1E2mag, b.E1E2mag, tol)
|
||
}
|
||
|
||
func same(a, b, tol float64) bool {
|
||
return (math.IsNaN(a) && math.IsNaN(b)) ||
|
||
(scalar.EqualWithinAbsOrRel(a, b, tol, tol) && math.Float64bits(a)&(1<<63) == math.Float64bits(b)&(1<<63))
|
||
}
|