gonum.org/v1/gonum@v0.14.0/num/quat/trig_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 "math" 9 "math/cmplx" 10 "testing" 11 ) 12 13 var sinTests = []struct { 14 q Number 15 want Number 16 }{ 17 {q: Number{}, want: Number{}}, 18 {q: Number{Real: math.Pi / 2}, want: Number{Real: 1}}, 19 {q: Number{Imag: math.Pi / 2}, want: Number{Imag: imag(cmplx.Sin(complex(0, math.Pi/2)))}}, 20 {q: Number{Jmag: math.Pi / 2}, want: Number{Jmag: imag(cmplx.Sin(complex(0, math.Pi/2)))}}, 21 {q: Number{Kmag: math.Pi / 2}, want: Number{Kmag: imag(cmplx.Sin(complex(0, math.Pi/2)))}}, 22 23 // Exercises from Real Quaternionic Calculus Handbook doi:10.1007/978-3-0348-0622-0 24 // Ex 6.159 (a) and (b). 25 {q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, want: func() Number { 26 p := math.Cos(1) * math.Sinh(math.Sqrt(3)) / math.Sqrt(3) 27 // An error exists in the book's given solution for the real part. 28 return Number{Real: math.Sin(1) * math.Cosh(math.Sqrt(3)), Imag: p, Jmag: p, Kmag: p} 29 }()}, 30 {q: Number{Imag: -2, Jmag: 1}, want: func() Number { 31 s := math.Sinh(math.Sqrt(5)) / math.Sqrt(5) 32 return Number{Imag: -2 * s, Jmag: s} 33 }()}, 34 } 35 36 func TestSin(t *testing.T) { 37 t.Parallel() 38 const tol = 1e-14 39 for _, test := range sinTests { 40 got := Sin(test.q) 41 if !equalApprox(got, test.want, tol) { 42 t.Errorf("unexpected result for Sin(%v): got:%v want:%v", test.q, got, test.want) 43 } 44 } 45 } 46 47 var sinhTests = []struct { 48 q Number 49 want Number 50 }{ 51 {q: Number{}, want: Number{}}, 52 {q: Number{Real: math.Pi / 2}, want: Number{Real: math.Sinh(math.Pi / 2)}}, 53 {q: Number{Imag: math.Pi / 2}, want: Number{Imag: imag(cmplx.Sinh(complex(0, math.Pi/2)))}}, 54 {q: Number{Jmag: math.Pi / 2}, want: Number{Jmag: imag(cmplx.Sinh(complex(0, math.Pi/2)))}}, 55 {q: Number{Kmag: math.Pi / 2}, want: Number{Kmag: imag(cmplx.Sinh(complex(0, math.Pi/2)))}}, 56 {q: Number{Real: 1, Imag: -1, Jmag: -1}, want: func() Number { 57 // This was based on the example on p118, but it too has an error. 58 q := Number{Real: 1, Imag: -1, Jmag: -1} 59 return Scale(0.5, Sub(Exp(q), Exp(Scale(-1, q)))) 60 }()}, 61 {q: Number{1, 1, 1, 1}, want: func() Number { 62 q := Number{1, 1, 1, 1} 63 return Scale(0.5, Sub(Exp(q), Exp(Scale(-1, q)))) 64 }()}, 65 {q: Asinh(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 66 {q: Asinh(Number{1, 1, 1, 1}), want: func() Number { 67 q := Asinh(Number{1, 1, 1, 1}) 68 return Scale(0.5, Sub(Exp(q), Exp(Scale(-1, q)))) 69 }()}, 70 {q: Number{Real: math.Inf(1)}, want: Number{Real: math.Inf(1)}}, 71 {q: Number{Real: math.Inf(1), Imag: math.Pi / 2}, want: Number{Real: math.Inf(1), Imag: math.Inf(1)}}, 72 {q: Number{Real: math.Inf(1), Imag: math.Pi}, want: Number{Real: math.Inf(-1), Imag: math.Inf(1)}}, 73 {q: Number{Real: math.Inf(1), Imag: 3 * math.Pi / 2}, want: Number{Real: math.Inf(-1), Imag: math.Inf(-1)}}, 74 {q: Number{Real: math.Inf(1), Imag: 2 * math.Pi}, want: Number{Real: math.Inf(1), Imag: math.Inf(-1)}}, 75 } 76 77 func TestSinh(t *testing.T) { 78 t.Parallel() 79 const tol = 1e-14 80 for _, test := range sinhTests { 81 got := Sinh(test.q) 82 if !sameApprox(got, test.want, tol) { 83 t.Errorf("unexpected result for Sinh(%v): got:%v want:%v", test.q, got, test.want) 84 } 85 } 86 } 87 88 var cosTests = []struct { 89 q Number 90 want Number 91 }{ 92 {q: Number{}, want: Number{Real: 1}}, 93 {q: Number{Real: math.Pi / 2}, want: Number{Real: 0}}, 94 {q: Number{Imag: math.Pi / 2}, want: Number{Real: real(cmplx.Cos(complex(0, math.Pi/2)))}}, 95 {q: Number{Jmag: math.Pi / 2}, want: Number{Real: real(cmplx.Cos(complex(0, math.Pi/2)))}}, 96 {q: Number{Kmag: math.Pi / 2}, want: Number{Real: real(cmplx.Cos(complex(0, math.Pi/2)))}}, 97 98 // Example from Real Quaternionic Calculus Handbook doi:10.1007/978-3-0348-0622-0 99 // p108. 100 {q: Number{Real: 1, Imag: 1, Jmag: 1, Kmag: 1}, want: func() Number { 101 p := math.Sin(1) * math.Sinh(math.Sqrt(3)) / math.Sqrt(3) 102 return Number{Real: math.Cos(1) * math.Cosh(math.Sqrt(3)), Imag: -p, Jmag: -p, Kmag: -p} 103 }()}, 104 } 105 106 func TestCos(t *testing.T) { 107 t.Parallel() 108 const tol = 1e-14 109 for _, test := range cosTests { 110 got := Cos(test.q) 111 if !equalApprox(got, test.want, tol) { 112 t.Errorf("unexpected result for Cos(%v): got:%v want:%v", test.q, got, test.want) 113 } 114 } 115 } 116 117 var coshTests = []struct { 118 q Number 119 want Number 120 }{ 121 {q: Number{}, want: Number{Real: 1}}, 122 {q: Number{Real: math.Pi / 2}, want: Number{Real: math.Cosh(math.Pi / 2)}}, 123 {q: Number{Imag: math.Pi / 2}, want: Number{Imag: imag(cmplx.Cosh(complex(0, math.Pi/2)))}}, 124 {q: Number{Jmag: math.Pi / 2}, want: Number{Jmag: imag(cmplx.Cosh(complex(0, math.Pi/2)))}}, 125 {q: Number{Kmag: math.Pi / 2}, want: Number{Kmag: imag(cmplx.Cosh(complex(0, math.Pi/2)))}}, 126 {q: Number{Real: 1, Imag: -1, Jmag: -1}, want: func() Number { 127 q := Number{Real: 1, Imag: -1, Jmag: -1} 128 return Scale(0.5, Add(Exp(q), Exp(Scale(-1, q)))) 129 }()}, 130 {q: Number{1, 1, 1, 1}, want: func() Number { 131 q := Number{1, 1, 1, 1} 132 return Scale(0.5, Add(Exp(q), Exp(Scale(-1, q)))) 133 }()}, 134 {q: Number{Real: math.Inf(1)}, want: Number{Real: math.Inf(1)}}, 135 {q: Number{Real: math.Inf(1), Imag: math.Pi / 2}, want: Number{Real: math.Inf(1), Imag: math.Inf(1)}}, 136 {q: Number{Real: math.Inf(1), Imag: math.Pi}, want: Number{Real: math.Inf(-1), Imag: math.Inf(1)}}, 137 {q: Number{Real: math.Inf(1), Imag: 3 * math.Pi / 2}, want: Number{Real: math.Inf(-1), Imag: math.Inf(-1)}}, 138 {q: Number{Real: math.Inf(1), Imag: 2 * math.Pi}, want: Number{Real: math.Inf(1), Imag: math.Inf(-1)}}, 139 } 140 141 func TestCosh(t *testing.T) { 142 t.Parallel() 143 const tol = 1e-14 144 for _, test := range coshTests { 145 got := Cosh(test.q) 146 if !sameApprox(got, test.want, tol) { 147 t.Errorf("unexpected result for Cosh(%v): got:%v want:%v", test.q, got, test.want) 148 } 149 } 150 } 151 152 var tanTests = []struct { 153 q Number 154 want Number 155 }{ 156 {q: Number{}, want: Number{}}, 157 {q: Number{Real: math.Pi / 4}, want: Number{Real: math.Tan(math.Pi / 4)}}, 158 {q: Number{Imag: math.Pi / 4}, want: Number{Imag: imag(cmplx.Tan(complex(0, math.Pi/4)))}}, 159 {q: Number{Jmag: math.Pi / 4}, want: Number{Jmag: imag(cmplx.Tan(complex(0, math.Pi/4)))}}, 160 {q: Number{Kmag: math.Pi / 4}, want: Number{Kmag: imag(cmplx.Tan(complex(0, math.Pi/4)))}}, 161 162 // From exercise from Real Numberernionic Calculus Handbook doi:10.1007/978-3-0348-0622-0 163 {q: Number{Imag: 1}, want: Mul(Sin(Number{Imag: 1}), Inv(Cos(Number{Imag: 1})))}, 164 {q: Number{1, 1, 1, 1}, want: Mul(Sin(Number{1, 1, 1, 1}), Inv(Cos(Number{1, 1, 1, 1})))}, 165 } 166 167 func TestTan(t *testing.T) { 168 t.Parallel() 169 const tol = 1e-14 170 for _, test := range tanTests { 171 got := Tan(test.q) 172 if !equalApprox(got, test.want, tol) { 173 t.Errorf("unexpected result for Tan(%v): got:%v want:%v", test.q, got, test.want) 174 } 175 } 176 } 177 178 var tanhTests = []struct { 179 q Number 180 want Number 181 }{ 182 {q: Number{}, want: Number{}}, 183 {q: Number{Real: math.Pi / 4}, want: Number{Real: math.Tanh(math.Pi / 4)}}, 184 {q: Number{Imag: math.Pi / 4}, want: Number{Imag: imag(cmplx.Tanh(complex(0, math.Pi/4)))}}, 185 {q: Number{Jmag: math.Pi / 4}, want: Number{Jmag: imag(cmplx.Tanh(complex(0, math.Pi/4)))}}, 186 {q: Number{Kmag: math.Pi / 4}, want: Number{Kmag: imag(cmplx.Tanh(complex(0, math.Pi/4)))}}, 187 {q: Number{Imag: 1}, want: Mul(Sinh(Number{Imag: 1}), Inv(Cosh(Number{Imag: 1})))}, 188 {q: Number{1, 1, 1, 1}, want: Mul(Sinh(Number{1, 1, 1, 1}), Inv(Cosh(Number{1, 1, 1, 1})))}, 189 {q: Number{Real: math.Inf(1)}, want: Number{Real: 1}}, 190 {q: Number{Real: math.Inf(1), Imag: math.Pi / 4}, want: Number{Real: 1, Imag: 0 * math.Sin(math.Pi/2)}}, 191 {q: Number{Real: math.Inf(1), Imag: math.Pi / 2}, want: Number{Real: 1, Imag: 0 * math.Sin(math.Pi)}}, 192 {q: Number{Real: math.Inf(1), Imag: 3 * math.Pi / 4}, want: Number{Real: 1, Imag: 0 * math.Sin(3*math.Pi/2)}}, 193 {q: Number{Real: math.Inf(1), Imag: math.Pi}, want: Number{Real: 1, Imag: 0 * math.Sin(2*math.Pi)}}, 194 } 195 196 func TestTanh(t *testing.T) { 197 t.Parallel() 198 const tol = 1e-14 199 for _, test := range tanhTests { 200 got := Tanh(test.q) 201 if !sameApprox(got, test.want, tol) { 202 t.Errorf("unexpected result for Tanh(%v): got:%v want:%v", test.q, got, test.want) 203 } 204 } 205 } 206 207 var asinTests = []struct { 208 q Number 209 want Number 210 }{ 211 {q: Number{}, want: Number{}}, 212 {q: Number{Real: 1}, want: Number{Real: math.Pi / 2}}, 213 {q: Number{Imag: 1}, want: Number{Imag: real(cmplx.Asinh(1))}}, 214 {q: Number{Jmag: 1}, want: Number{Jmag: real(cmplx.Asinh(1))}}, 215 {q: Number{Kmag: 1}, want: Number{Kmag: real(cmplx.Asinh(1))}}, 216 {q: Sin(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 217 } 218 219 func TestAsin(t *testing.T) { 220 t.Parallel() 221 const tol = 1e-14 222 for _, test := range asinTests { 223 got := Asin(test.q) 224 if !equalApprox(got, test.want, tol) { 225 t.Errorf("unexpected result for Asin(%v): got:%v want:%v", test.q, got, test.want) 226 } 227 } 228 } 229 230 var asinhTests = []struct { 231 q Number 232 want Number 233 }{ 234 {q: Number{}, want: Number{}}, 235 {q: Number{Real: 1}, want: Number{Real: math.Asinh(1)}}, 236 {q: Number{Imag: 1}, want: Number{Imag: math.Pi / 2}}, 237 {q: Number{Jmag: 1}, want: Number{Jmag: math.Pi / 2}}, 238 {q: Number{Kmag: 1}, want: Number{Kmag: math.Pi / 2}}, 239 {q: Number{1, 1, 1, 1}, want: func() Number { 240 q := Number{1, 1, 1, 1} 241 return Log(Add(q, Sqrt(Add(Mul(q, q), Number{Real: 1})))) 242 }()}, 243 {q: Sinh(Number{Real: 1}), want: Number{Real: 1}}, 244 {q: Sinh(Number{Imag: 1}), want: Number{Imag: 1}}, 245 {q: Sinh(Number{Imag: 1, Jmag: 1}), want: Number{Imag: 1, Jmag: 1}}, 246 {q: Sinh(Number{Real: 1, Imag: 1, Jmag: 1}), want: Number{Real: 1, Imag: 1, Jmag: 1}}, 247 // The following fails: 248 // {q: Sinh(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 249 // but this passes... 250 {q: Sinh(Number{1, 1, 1, 1}), want: func() Number { 251 q := Sinh(Number{1, 1, 1, 1}) 252 return Log(Add(q, Sqrt(Add(Mul(q, q), Number{Real: 1})))) 253 }()}, 254 // And see the Sinh tests that do the reciprocal operation. 255 } 256 257 func TestAsinh(t *testing.T) { 258 t.Parallel() 259 const tol = 1e-14 260 for _, test := range asinhTests { 261 got := Asinh(test.q) 262 if !equalApprox(got, test.want, tol) { 263 t.Errorf("unexpected result for Asinh(%v): got:%v want:%v", test.q, got, test.want) 264 } 265 } 266 } 267 268 var acosTests = []struct { 269 q Number 270 want Number 271 }{ 272 {q: Number{}, want: Number{Real: math.Pi / 2}}, 273 {q: Number{Real: 1}, want: Number{Real: 0}}, 274 {q: Number{Imag: 1}, want: Number{Real: real(cmplx.Acos(1i)), Imag: imag(cmplx.Acos(1i))}}, 275 {q: Number{Jmag: 1}, want: Number{Real: real(cmplx.Acos(1i)), Jmag: imag(cmplx.Acos(1i))}}, 276 {q: Number{Kmag: 1}, want: Number{Real: real(cmplx.Acos(1i)), Kmag: imag(cmplx.Acos(1i))}}, 277 {q: Cos(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 278 } 279 280 func TestAcos(t *testing.T) { 281 t.Parallel() 282 const tol = 1e-14 283 for _, test := range acosTests { 284 got := Acos(test.q) 285 if !equalApprox(got, test.want, tol) { 286 t.Errorf("unexpected result for Acos(%v): got:%v want:%v", test.q, got, test.want) 287 } 288 } 289 } 290 291 var acoshTests = []struct { 292 q Number 293 want Number 294 }{ 295 {q: Number{}, want: Number{Real: math.Pi / 2}}, 296 {q: Number{Real: 1}, want: Number{Real: math.Acosh(1)}}, 297 {q: Number{Imag: 1}, want: Number{Real: real(cmplx.Acosh(1i)), Imag: imag(cmplx.Acosh(1i))}}, 298 {q: Number{Jmag: 1}, want: Number{Real: real(cmplx.Acosh(1i)), Jmag: imag(cmplx.Acosh(1i))}}, 299 {q: Number{Kmag: 1}, want: Number{Real: real(cmplx.Acosh(1i)), Kmag: imag(cmplx.Acosh(1i))}}, 300 {q: Cosh(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 301 {q: Number{1, 1, 1, 1}, want: func() Number { 302 q := Number{1, 1, 1, 1} 303 return Log(Add(q, Sqrt(Sub(Mul(q, q), Number{Real: 1})))) 304 }()}, 305 // The following fails by a factor of -1. 306 // {q: Cosh(Number{1, 1, 1, 1}), want: func() Number { 307 // q := Cosh(Number{1, 1, 1, 1}) 308 // return Log(Add(q, Sqrt(Sub(Mul(q, q), Number{Real: 1})))) 309 // }()}, 310 } 311 312 func TestAcosh(t *testing.T) { 313 t.Parallel() 314 const tol = 1e-14 315 for _, test := range acoshTests { 316 got := Acosh(test.q) 317 if !equalApprox(got, test.want, tol) { 318 t.Errorf("unexpected result for Acosh(%v): got:%v want:%v", test.q, got, test.want) 319 } 320 } 321 } 322 323 var atanTests = []struct { 324 q Number 325 want Number 326 }{ 327 {q: Number{}, want: Number{}}, 328 {q: Number{Real: 1}, want: Number{Real: math.Pi / 4}}, 329 {q: Number{Imag: 0.5}, want: Number{Real: real(cmplx.Atan(0.5i)), Imag: imag(cmplx.Atan(0.5i))}}, 330 {q: Number{Jmag: 0.5}, want: Number{Real: real(cmplx.Atan(0.5i)), Jmag: imag(cmplx.Atan(0.5i))}}, 331 {q: Number{Kmag: 0.5}, want: Number{Real: real(cmplx.Atan(0.5i)), Kmag: imag(cmplx.Atan(0.5i))}}, 332 {q: Tan(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 333 } 334 335 func TestAtan(t *testing.T) { 336 t.Parallel() 337 const tol = 1e-14 338 for _, test := range atanTests { 339 got := Atan(test.q) 340 if !equalApprox(got, test.want, tol) { 341 t.Errorf("unexpected result for Atan(%v): got:%v want:%v", test.q, got, test.want) 342 } 343 } 344 } 345 346 var atanhTests = []struct { 347 q Number 348 want Number 349 }{ 350 {q: Number{}, want: Number{}}, 351 {q: Number{Real: 1}, want: Number{Real: math.Atanh(1)}}, 352 {q: Number{Imag: 0.5}, want: Number{Real: real(cmplx.Atanh(0.5i)), Imag: imag(cmplx.Atanh(0.5i))}}, 353 {q: Number{Jmag: 0.5}, want: Number{Real: real(cmplx.Atanh(0.5i)), Jmag: imag(cmplx.Atanh(0.5i))}}, 354 {q: Number{Kmag: 0.5}, want: Number{Real: real(cmplx.Atanh(0.5i)), Kmag: imag(cmplx.Atanh(0.5i))}}, 355 {q: Number{1, 1, 1, 1}, want: func() Number { 356 q := Number{1, 1, 1, 1} 357 return Scale(0.5, Sub(Log(Add(Number{Real: 1}, q)), Log(Sub(Number{Real: 1}, q)))) 358 }()}, 359 {q: Tanh(Number{Real: 1}), want: Number{Real: 1}}, 360 {q: Tanh(Number{Imag: 1}), want: Number{Imag: 1}}, 361 {q: Tanh(Number{Imag: 1, Jmag: 1}), want: Number{Imag: 1, Jmag: 1}}, 362 {q: Tanh(Number{Real: 1, Imag: 1, Jmag: 1}), want: Number{Real: 1, Imag: 1, Jmag: 1}}, 363 // The following fails 364 // {q: Tanh(Number{1, 1, 1, 1}), want: Number{1, 1, 1, 1}}, 365 // but... 366 {q: Tanh(Number{1, 1, 1, 1}), want: func() Number { 367 q := Tanh(Number{1, 1, 1, 1}) 368 return Scale(0.5, Sub(Log(Add(Number{Real: 1}, q)), Log(Sub(Number{Real: 1}, q)))) 369 }()}, 370 } 371 372 func TestAtanh(t *testing.T) { 373 t.Parallel() 374 const tol = 1e-14 375 for _, test := range atanhTests { 376 got := Atanh(test.q) 377 if !equalApprox(got, test.want, tol) { 378 t.Errorf("unexpected result for Atanh(%v): got:%v want:%v", test.q, got, test.want) 379 } 380 } 381 }