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  }