gonum.org/v1/gonum@v0.14.0/num/quat/quat_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 quat 6 7 import ( 8 "fmt" 9 "math" 10 "testing" 11 12 "gonum.org/v1/gonum/floats/scalar" 13 ) 14 15 var arithTests = []struct { 16 x, y Number 17 f float64 18 19 wantAdd Number 20 wantSub Number 21 wantMul Number 22 wantScale Number 23 }{ 24 { 25 x: Number{1, 1, 1, 1}, y: Number{1, 1, 1, 1}, 26 f: 2, 27 28 wantAdd: Number{2, 2, 2, 2}, 29 wantSub: Number{0, 0, 0, 0}, 30 wantMul: Number{-2, 2, 2, 2}, 31 wantScale: Number{2, 2, 2, 2}, 32 }, 33 { 34 x: Number{1, 1, 1, 1}, y: Number{2, -1, 1, -1}, 35 f: -2, 36 37 wantAdd: Number{3, 0, 2, 0}, 38 wantSub: Number{-1, 2, 0, 2}, 39 wantMul: Number{3, -1, 3, 3}, 40 wantScale: Number{-2, -2, -2, -2}, 41 }, 42 { 43 x: Number{1, 2, 3, 4}, y: Number{4, -3, 2, -1}, 44 f: 2, 45 46 wantAdd: Number{5, -1, 5, 3}, 47 wantSub: Number{-3, 5, 1, 5}, 48 wantMul: Number{8, -6, 4, 28}, 49 wantScale: Number{2, 4, 6, 8}, 50 }, 51 { 52 x: Number{1, 2, 3, 4}, y: Number{-4, 3, -2, 1}, 53 f: -2, 54 55 wantAdd: Number{-3, 5, 1, 5}, 56 wantSub: Number{5, -1, 5, 3}, 57 wantMul: Number{-8, 6, -4, -28}, 58 wantScale: Number{-2, -4, -6, -8}, 59 }, 60 { 61 x: Number{-4, 3, -2, 1}, y: Number{1, 2, 3, 4}, 62 f: 0.5, 63 64 wantAdd: Number{-3, 5, 1, 5}, 65 wantSub: Number{-5, 1, -5, -3}, 66 wantMul: Number{-8, -16, -24, -2}, 67 wantScale: Number{-2, 1.5, -1, 0.5}, 68 }, 69 } 70 71 func TestArithmetic(t *testing.T) { 72 t.Parallel() 73 for _, test := range arithTests { 74 gotAdd := Add(test.x, test.y) 75 if gotAdd != test.wantAdd { 76 t.Errorf("unexpected result for %v+%v: got:%v, want:%v", test.x, test.y, gotAdd, test.wantAdd) 77 } 78 gotSub := Sub(test.x, test.y) 79 if gotSub != test.wantSub { 80 t.Errorf("unexpected result for %v-%v: got:%v, want:%v", test.x, test.y, gotSub, test.wantSub) 81 } 82 gotMul := Mul(test.x, test.y) 83 if gotMul != test.wantMul { 84 t.Errorf("unexpected result for %v*%v: got:%v, want:%v", test.x, test.y, gotMul, test.wantMul) 85 } 86 gotScale := Scale(test.f, test.x) 87 if gotScale != test.wantScale { 88 t.Errorf("unexpected result for %v*%v: got:%v, want:%v", test.f, test.x, gotScale, test.wantScale) 89 } 90 } 91 } 92 93 var formatTests = []struct { 94 q Number 95 format string 96 want string 97 }{ 98 {q: Number{1.1, 2.1, 3.1, 4.1}, format: "%#v", want: "quat.Number{Real:1.1, Imag:2.1, Jmag:3.1, Kmag:4.1}"}, // Bootstrap test. 99 {q: Number{-1.1, -2.1, -3.1, -4.1}, format: "%#v", want: "quat.Number{Real:-1.1, Imag:-2.1, Jmag:-3.1, Kmag:-4.1}"}, // Bootstrap test. 100 {q: Number{1.1, 2.1, 3.1, 4.1}, format: "%+v", want: "{Real:1.1, Imag:2.1, Jmag:3.1, Kmag:4.1}"}, 101 {q: Number{-1.1, -2.1, -3.1, -4.1}, format: "%+v", want: "{Real:-1.1, Imag:-2.1, Jmag:-3.1, Kmag:-4.1}"}, 102 {q: Number{1, 2, 3, 4}, format: "%v", want: "(1+2i+3j+4k)"}, 103 {q: Number{-1, -2, -3, -4}, format: "%v", want: "(-1-2i-3j-4k)"}, 104 {q: Number{1, 2, 3, 4}, format: "%g", want: "(1+2i+3j+4k)"}, 105 {q: Number{-1, -2, -3, -4}, format: "%g", want: "(-1-2i-3j-4k)"}, 106 {q: Number{1, 2, 3, 4}, format: "%e", want: "(1.000000e+00+2.000000e+00i+3.000000e+00j+4.000000e+00k)"}, 107 {q: Number{-1, -2, -3, -4}, format: "%e", want: "(-1.000000e+00-2.000000e+00i-3.000000e+00j-4.000000e+00k)"}, 108 {q: Number{1, 2, 3, 4}, format: "%E", want: "(1.000000E+00+2.000000E+00i+3.000000E+00j+4.000000E+00k)"}, 109 {q: Number{-1, -2, -3, -4}, format: "%E", want: "(-1.000000E+00-2.000000E+00i-3.000000E+00j-4.000000E+00k)"}, 110 {q: Number{1, 2, 3, 4}, format: "%f", want: "(1.000000+2.000000i+3.000000j+4.000000k)"}, 111 {q: Number{-1, -2, -3, -4}, format: "%f", want: "(-1.000000-2.000000i-3.000000j-4.000000k)"}, 112 } 113 114 func TestFormat(t *testing.T) { 115 t.Parallel() 116 for _, test := range formatTests { 117 got := fmt.Sprintf(test.format, test.q) 118 if got != test.want { 119 t.Errorf("unexpected result for fmt.Sprintf(%q, %#v): got:%q, want:%q", test.format, test.q, got, test.want) 120 } 121 } 122 } 123 124 var parseTests = []struct { 125 s string 126 want Number 127 wantErr error 128 }{ 129 // Simple error states: 130 {s: "", wantErr: parseError{state: -1}}, 131 {s: "()", wantErr: parseError{string: "()", state: -1}}, 132 {s: "(1", wantErr: parseError{string: "(1", state: -1}}, 133 {s: "1)", wantErr: parseError{string: "1)", state: -1}}, 134 135 // Ambiguous parse error states: 136 {s: "1+2i+3i", wantErr: parseError{string: "1+2i+3i", state: -1}}, 137 {s: "1+2i3j", wantErr: parseError{string: "1+2i3j", state: -1}}, 138 {s: "1e-4i-4k+10.3e6j+", wantErr: parseError{string: "1e-4i-4k+10.3e6j+", state: -1}}, 139 {s: "1e-4i-4k+10.3e6j-", wantErr: parseError{string: "1e-4i-4k+10.3e6j-", state: -1}}, 140 141 // Valid input: 142 {s: "1+4i", want: Number{Real: 1, Imag: 4}}, 143 {s: "4i+1", want: Number{Real: 1, Imag: 4}}, 144 {s: "+1+4i", want: Number{Real: 1, Imag: 4}}, 145 {s: "+4i+1", want: Number{Real: 1, Imag: 4}}, 146 {s: "1e-4-4k+10.3e6j+1i", want: Number{Real: 1e-4, Imag: 1, Jmag: 10.3e6, Kmag: -4}}, 147 {s: "1e-4-4k+10.3e6j+i", want: Number{Real: 1e-4, Imag: 1, Jmag: 10.3e6, Kmag: -4}}, 148 {s: "1e-4-4k+10.3e6j-i", want: Number{Real: 1e-4, Imag: -1, Jmag: 10.3e6, Kmag: -4}}, 149 {s: "1e-4i-4k+10.3e6j-1", want: Number{Real: -1, Imag: 1e-4, Jmag: 10.3e6, Kmag: -4}}, 150 {s: "1e-4i-4k+10.3e6j+1", want: Number{Real: 1, Imag: 1e-4, Jmag: 10.3e6, Kmag: -4}}, 151 {s: "(1+4i)", want: Number{Real: 1, Imag: 4}}, 152 {s: "(4i+1)", want: Number{Real: 1, Imag: 4}}, 153 {s: "(+1+4i)", want: Number{Real: 1, Imag: 4}}, 154 {s: "(+4i+1)", want: Number{Real: 1, Imag: 4}}, 155 {s: "(1e-4-4k+10.3e6j+1i)", want: Number{Real: 1e-4, Imag: 1, Jmag: 10.3e6, Kmag: -4}}, 156 {s: "(1e-4-4k+10.3e6j+i)", want: Number{Real: 1e-4, Imag: 1, Jmag: 10.3e6, Kmag: -4}}, 157 {s: "(1e-4-4k+10.3e6j-i)", want: Number{Real: 1e-4, Imag: -1, Jmag: 10.3e6, Kmag: -4}}, 158 {s: "(1e-4i-4k+10.3e6j-1)", want: Number{Real: -1, Imag: 1e-4, Jmag: 10.3e6, Kmag: -4}}, 159 {s: "(1e-4i-4k+10.3e6j+1)", want: Number{Real: 1, Imag: 1e-4, Jmag: 10.3e6, Kmag: -4}}, 160 {s: "NaN", want: NaN()}, 161 {s: "nan", want: NaN()}, 162 {s: "Inf", want: Inf()}, 163 {s: "inf", want: Inf()}, 164 {s: "(Inf+Infi)", want: Number{Real: math.Inf(1), Imag: math.Inf(1)}}, 165 {s: "(-Inf+Infi)", want: Number{Real: math.Inf(-1), Imag: math.Inf(1)}}, 166 {s: "(+Inf-Infi)", want: Number{Real: math.Inf(1), Imag: math.Inf(-1)}}, 167 {s: "(inf+infi)", want: Number{Real: math.Inf(1), Imag: math.Inf(1)}}, 168 {s: "(-inf+infi)", want: Number{Real: math.Inf(-1), Imag: math.Inf(1)}}, 169 {s: "(+inf-infi)", want: Number{Real: math.Inf(1), Imag: math.Inf(-1)}}, 170 {s: "(nan+nani)", want: Number{Real: math.NaN(), Imag: math.NaN()}}, 171 {s: "(nan-nani)", want: Number{Real: math.NaN(), Imag: math.NaN()}}, 172 {s: "(nan+nani+1k)", want: Number{Real: math.NaN(), Imag: math.NaN(), Kmag: 1}}, 173 {s: "(nan-nani+1k)", want: Number{Real: math.NaN(), Imag: math.NaN(), Kmag: 1}}, 174 } 175 176 func TestParse(t *testing.T) { 177 t.Parallel() 178 for _, test := range parseTests { 179 got, err := Parse(test.s) 180 if err != test.wantErr { 181 t.Errorf("unexpected error for Parse(%q): got:%#v, want:%#v", test.s, err, test.wantErr) 182 } 183 if err != nil { 184 continue 185 } 186 if !sameNumber(got, test.want) { 187 t.Errorf("unexpected result for Parse(%q): got:%v, want:%v", test.s, got, test.want) 188 } 189 } 190 } 191 192 func equalApprox(a, b Number, tol float64) bool { 193 return scalar.EqualWithinAbsOrRel(a.Real, b.Real, tol, tol) && 194 scalar.EqualWithinAbsOrRel(a.Imag, b.Imag, tol, tol) && 195 scalar.EqualWithinAbsOrRel(a.Jmag, b.Jmag, tol, tol) && 196 scalar.EqualWithinAbsOrRel(a.Kmag, b.Kmag, tol, tol) 197 } 198 199 func sameApprox(a, b Number, tol float64) bool { 200 switch { 201 case a.Real == 0 && b.Real == 0: 202 return math.Signbit(a.Real) == math.Signbit(b.Real) 203 case a.Imag == 0 && b.Imag == 0: 204 return math.Signbit(a.Imag) == math.Signbit(b.Imag) 205 case a.Jmag == 0 && b.Jmag == 0: 206 return math.Signbit(a.Jmag) == math.Signbit(b.Jmag) 207 case a.Kmag == 0 && b.Kmag == 0: 208 return math.Signbit(a.Kmag) == math.Signbit(b.Kmag) 209 } 210 return (sameFloat(a.Real, b.Real) || scalar.EqualWithinAbsOrRel(a.Real, b.Real, tol, tol)) && 211 (sameFloat(a.Imag, b.Imag) || scalar.EqualWithinAbsOrRel(a.Imag, b.Imag, tol, tol)) && 212 (sameFloat(a.Jmag, b.Jmag) || scalar.EqualWithinAbsOrRel(a.Jmag, b.Jmag, tol, tol)) && 213 (sameFloat(a.Kmag, b.Kmag) || scalar.EqualWithinAbsOrRel(a.Kmag, b.Kmag, tol, tol)) 214 } 215 216 func sameNumber(a, b Number) bool { 217 return sameFloat(a.Real, b.Real) && 218 sameFloat(a.Imag, b.Imag) && 219 sameFloat(a.Jmag, b.Jmag) && 220 sameFloat(a.Kmag, b.Kmag) 221 } 222 223 func sameFloat(a, b float64) bool { 224 return a == b || (math.IsNaN(a) && math.IsNaN(b)) 225 }