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  }