gonum.org/v1/gonum@v0.14.0/spatial/r2/vector_test.go (about) 1 // Copyright ©2020 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 r2 6 7 import ( 8 "math" 9 "testing" 10 11 "golang.org/x/exp/rand" 12 "gonum.org/v1/gonum/floats/scalar" 13 ) 14 15 func TestAdd(t *testing.T) { 16 for _, test := range []struct { 17 v1, v2 Vec 18 want Vec 19 }{ 20 {Vec{0, 0}, Vec{0, 0}, Vec{0, 0}}, 21 {Vec{1, 0}, Vec{0, 0}, Vec{1, 0}}, 22 {Vec{1, 2}, Vec{3, 4}, Vec{4, 6}}, 23 {Vec{1, -3}, Vec{1, -6}, Vec{2, -9}}, 24 {Vec{1, 2}, Vec{-1, -2}, Vec{}}, 25 } { 26 got := Add(test.v1, test.v2) 27 if got != test.want { 28 t.Errorf( 29 "error: %v + %v: got=%v, want=%v", 30 test.v1, test.v2, got, test.want, 31 ) 32 } 33 } 34 } 35 36 func TestSub(t *testing.T) { 37 for _, test := range []struct { 38 v1, v2 Vec 39 want Vec 40 }{ 41 {Vec{0, 0}, Vec{0, 0}, Vec{0, 0}}, 42 {Vec{1, 0}, Vec{0, 0}, Vec{1, 0}}, 43 {Vec{1, 2}, Vec{3, 4}, Vec{-2, -2}}, 44 {Vec{1, -3}, Vec{1, -6}, Vec{0, 3}}, 45 {Vec{1, 2}, Vec{1, 2}, Vec{}}, 46 } { 47 got := Sub(test.v1, test.v2) 48 if got != test.want { 49 t.Errorf( 50 "error: %v - %v: got=%v, want=%v", 51 test.v1, test.v2, got, test.want, 52 ) 53 } 54 } 55 } 56 57 func TestScale(t *testing.T) { 58 for _, test := range []struct { 59 a float64 60 v Vec 61 want Vec 62 }{ 63 {3, Vec{0, 0}, Vec{0, 0}}, 64 {1, Vec{1, 0}, Vec{1, 0}}, 65 {0, Vec{1, 0}, Vec{0, 0}}, 66 {3, Vec{1, 0}, Vec{3, 0}}, 67 {-1, Vec{1, -3}, Vec{-1, 3}}, 68 {2, Vec{1, -3}, Vec{2, -6}}, 69 {10, Vec{1, 2}, Vec{10, 20}}, 70 } { 71 got := Scale(test.a, test.v) 72 if got != test.want { 73 t.Errorf( 74 "error: %v * %v: got=%v, want=%v", 75 test.a, test.v, got, test.want) 76 } 77 } 78 } 79 80 func TestDot(t *testing.T) { 81 for _, test := range []struct { 82 u, v Vec 83 want float64 84 }{ 85 {Vec{1, 2}, Vec{1, 2}, 5}, 86 {Vec{1, 0}, Vec{1, 0}, 1}, 87 {Vec{1, 0}, Vec{0, 1}, 0}, 88 {Vec{1, 0}, Vec{0, 1}, 0}, 89 {Vec{1, 1}, Vec{-1, -1}, -2}, 90 {Vec{1, 2}, Vec{-0.3, 0.4}, 0.5}, 91 } { 92 { 93 got := Dot(test.u, test.v) 94 if got != test.want { 95 t.Errorf( 96 "error: %v · %v: got=%v, want=%v", 97 test.u, test.v, got, test.want, 98 ) 99 } 100 } 101 { 102 got := Dot(test.v, test.u) 103 if got != test.want { 104 t.Errorf( 105 "error: %v · %v: got=%v, want=%v", 106 test.v, test.u, got, test.want, 107 ) 108 } 109 } 110 } 111 } 112 113 func TestCross(t *testing.T) { 114 for _, test := range []struct { 115 v1, v2 Vec 116 want float64 117 }{ 118 {Vec{1, 0}, Vec{1, 0}, 0}, 119 {Vec{1, 0}, Vec{0, 1}, 1}, 120 {Vec{0, 1}, Vec{1, 0}, -1}, 121 {Vec{1, 2}, Vec{-4, 5}, 13}, 122 {Vec{1, 2}, Vec{2, 3}, -1}, 123 } { 124 got := Cross(test.v1, test.v2) 125 if got != test.want { 126 t.Errorf( 127 "error: %v × %v = %v, want %v", 128 test.v1, test.v2, got, test.want, 129 ) 130 } 131 } 132 } 133 134 func TestNorm(t *testing.T) { 135 for _, test := range []struct { 136 v Vec 137 want float64 138 }{ 139 {Vec{0, 0}, 0}, 140 {Vec{0, 1}, 1}, 141 {Vec{1, 1}, math.Sqrt2}, 142 {Vec{1, 2}, math.Sqrt(5)}, 143 {Vec{3, -4}, 5}, 144 {Vec{1, 1e-16}, 1}, 145 {Vec{4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 6.101625315155041e-196}, 146 } { 147 if got, want := Norm(test.v), test.want; got != want { 148 t.Errorf("|%v| = %v, want %v", test.v, got, want) 149 } 150 } 151 } 152 153 func TestNorm2(t *testing.T) { 154 for _, test := range []struct { 155 v Vec 156 want float64 157 }{ 158 {Vec{0, 0}, 0}, 159 {Vec{0, 1}, 1}, 160 {Vec{1, 1}, 2}, 161 {Vec{1, 2}, 5}, 162 {Vec{3, -4}, 25}, 163 {Vec{1, 1e-16}, 1}, 164 // This will underflow and return zero. 165 {Vec{4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 0}, 166 } { 167 if got, want := Norm2(test.v), test.want; got != want { 168 t.Errorf("|%v|^2 = %v, want %v", test.v, got, want) 169 } 170 } 171 } 172 173 func TestUnit(t *testing.T) { 174 const tol = 1e-14 175 for _, test := range []struct { 176 v, want Vec 177 }{ 178 {Vec{}, Vec{math.NaN(), math.NaN()}}, 179 {Vec{1, 0}, Vec{1, 0}}, 180 {Vec{0, 1}, Vec{0, 1}}, 181 {Vec{-1, 0}, Vec{-1, 0}}, 182 {Vec{3, 4}, Vec{0.6, 0.8}}, 183 {Vec{3, -4}, Vec{0.6, -0.8}}, 184 {Vec{1, 1}, Vec{1. / math.Sqrt(2), 1. / math.Sqrt(2)}}, 185 {Vec{1, 1e-16}, Vec{1, 1e-16}}, 186 {Vec{1, 1e16}, Vec{1e-16, 1}}, 187 {Vec{1e4, math.MaxFloat32 - 1}, Vec{0, 1}}, 188 } { 189 got := Unit(test.v) 190 if !vecApproxEqual(got, test.want, tol) { 191 t.Errorf( 192 "Unit(%v) = %v, want %v", 193 test.v, got, test.want, 194 ) 195 } 196 if vecIsNaN(got) { 197 return 198 } 199 if n, want := Norm(got), 1.0; n != want { 200 t.Errorf("|%v| = %v, want 1", got, n) 201 } 202 } 203 } 204 205 func TestCos(t *testing.T) { 206 const tol = 1e-14 207 for _, test := range []struct { 208 v1, v2 Vec 209 want float64 210 }{ 211 {Vec{1, 1}, Vec{1, 1}, 1}, 212 {Vec{1, 1}, Vec{-1, -1}, -1}, 213 {Vec{1, 0}, Vec{1, 0}, 1}, 214 {Vec{1, 0}, Vec{0, 1}, 0}, 215 {Vec{1, 0}, Vec{-1, 0}, -1}, 216 } { 217 got := Cos(test.v1, test.v2) 218 if !scalar.EqualWithinAbs(got, test.want, tol) { 219 t.Errorf("cos(%v, %v)= %v, want %v", 220 test.v1, test.v2, got, test.want, 221 ) 222 } 223 } 224 } 225 226 func TestRotate(t *testing.T) { 227 const tol = 1e-14 228 for _, test := range []struct { 229 v, q Vec 230 alpha float64 231 want Vec 232 }{ 233 {Vec{1, 0}, Vec{0, 0}, math.Pi / 2, Vec{0, 1}}, 234 {Vec{1, 0}, Vec{0, 0}, 3 * math.Pi / 2, Vec{0, -1}}, 235 {Vec{1, 0}, Vec{0, 1}, 0, Vec{1, 0}}, 236 {Vec{1, 0}, Vec{0, 1}, 2 * math.Pi, Vec{1, 0}}, 237 {Vec{1, 1}, Vec{0, 0}, math.Pi, Vec{-1, -1}}, 238 {Vec{2, 2}, Vec{1, 1}, math.Pi / 2, Vec{0, 2}}, 239 {Vec{2, 2}, Vec{1, 1}, math.Pi, Vec{0, 0}}, 240 {Vec{2, 2}, Vec{2, 0}, math.Pi, Vec{2, -2}}, 241 } { 242 got := Rotate(test.v, test.alpha, test.q) 243 if !vecApproxEqual(got, test.want, tol) { 244 t.Errorf( 245 "rotate(%v, %v, %v)= %v, want=%v", 246 test.v, test.alpha, test.q, got, test.want, 247 ) 248 } 249 } 250 } 251 252 func vecIsNaN(v Vec) bool { 253 return math.IsNaN(v.X) && math.IsNaN(v.Y) 254 } 255 256 func vecIsNaNAny(v Vec) bool { 257 return math.IsNaN(v.X) || math.IsNaN(v.Y) 258 } 259 260 func vecApproxEqual(a, b Vec, tol float64) bool { 261 if tol == 0 { 262 return vecEqual(a, b) 263 } 264 if vecIsNaNAny(a) || vecIsNaNAny(b) { 265 return vecIsNaN(a) && vecIsNaN(b) 266 } 267 return scalar.EqualWithinAbs(a.X, b.X, tol) && 268 scalar.EqualWithinAbs(a.Y, b.Y, tol) 269 } 270 271 func randomVec(rnd *rand.Rand) (v Vec) { 272 v.X = (rnd.Float64() - 0.5) * 20 273 v.Y = (rnd.Float64() - 0.5) * 20 274 return v 275 } 276 277 func vecEqual(a, b Vec) bool { 278 if vecIsNaNAny(a) || vecIsNaNAny(b) { 279 return vecIsNaN(a) && vecIsNaN(b) 280 } 281 return a == b 282 }