Repository: gonum/gonum
Pull request #1554 (2021-01-28 09:21:41)

spatial/r3: harmonize Vec API with num/quat.Number
Migration to the new API can be achieved with this rsc.io/rf script:

```
rf ex {
	import "gonum.org/v1/gonum/spatial/r3";
	var p,q r3.Vec;
	var f float64;

	p.Add(q) -> r3.Add(p, q);
	p.Sub(q) -> r3.Sub(p, q);
	p.Scale(f) -> r3.Scale(f, p);
	p.Dot(q) -> r3.Dot(p, q);
	p.Cross(q) -> r3.Cross(p, q);
        p.Rotate(f, q) -> r3.Rotate(p, f, q);
}
```

and, similarly, for `r2`:
```
    rf ex {
            import "gonum.org/v1/gonum/spatial/r2";
            var p,q r2.Vec;
            var f float64;
    
            p.Add(q) -> r2.Add(p, q);
            p.Sub(q) -> r2.Sub(p, q);
            p.Scale(f) -> r2.Scale(f, p);
            p.Dot(q) -> r2.Dot(p, q);
            p.Cross(q) -> r2.Cross(p, q);
            p.Rotate(f, q) -> r2.Rotate(p, f, q);
    }
```

Fixes gonum/gonum#1553.

Please take a look.

<!--
Checklist:

- API changes have been discussed
- code is goformated correctly (goimports)
- packages with generated code have had code generation run
- tests pass locally
- linked to relevant issues

Please make sure your commit message summary line and pull request
title match the Go convention; a one-line summary of the change,
prefixed by the primary affected package that should complete the
sentence, "This change modifies Gonum to _____."
-->


codecov-io: # [Codecov](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=h1) Report
> Merging [#1554](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=desc) (da1d030) into [master](https://codecov.io/gh/gonum/gonum/commit/090a5d652c892e8d5460a07eca1c1ddf480b04d9?el=desc) (090a5d6) will **decrease** coverage by `0.00%`.
> The diff coverage is `68.42%`.

[![Impacted file tree graph](https://codecov.io/gh/gonum/gonum/pull/1554/graphs/tree.svg?width=650&height=150&src=pr&token=B7qHnqSPNL)](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1554      +/-   ##
==========================================
- Coverage   72.67%   72.67%   -0.01%     
==========================================
  Files         479      479              
  Lines       57913    57918       +5     
==========================================
- Hits        42091    42090       -1     
- Misses      13421    13427       +6     
  Partials     2401     2401              
```


| [Impacted Files](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [spatial/r3/vector.go](https://codecov.io/gh/gonum/gonum/pull/1554/diff?src=pr&el=tree#diff-c3BhdGlhbC9yMy92ZWN0b3IuZ28=) | `86.66% <68.42%> (-10.84%)` | :arrow_down: |
| [optimize/listsearch.go](https://codecov.io/gh/gonum/gonum/pull/1554/diff?src=pr&el=tree#diff-b3B0aW1pemUvbGlzdHNlYXJjaC5nbw==) | `72.22% <0.00%> (-1.86%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=footer). Last update [090a5d6...3d8794a](https://codecov.io/gh/gonum/gonum/pull/1554?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

sbinet: done.

note that I've left `func (p Vec) Rotate(angle float64, axis Vec) Vec { ... }` alone.

should I also migrate it to `func Rotate(p Vec, angle float64, axis Vec) Vec { ...}` ?

Patch:
diff --git a/graph/layout/eades.go b/graph/layout/eades.go
index a8e35ac6..7a83d165 100644
--- a/graph/layout/eades.go
+++ b/graph/layout/eades.go
@@ -84,7 +84,7 @@ func (u *EadesR2) Update(g graph.Graph, layout LayoutR2) bool {
 	}
 	var updated bool
 	for i, p := range u.particles {
-		f := plane.ForceOn(p, u.Theta, barneshut.Gravity2).Scale(-u.Repulsion)
+		f := r2.Scale(-u.Repulsion, plane.ForceOn(p, u.Theta, barneshut.Gravity2))
 		// Prevent marginal updates that can be caused by
 		// floating point error when nodes are very far apart.
 		if math.Hypot(f.X, f.Y) > 1e-12 {
@@ -137,16 +137,16 @@ func (u *EadesR2) Update(g graph.Graph, layout LayoutR2) bool {
 			yidx := u.indexOf[yid]
 
 			// Apply adjacent node attraction.
-			v := u.particles[yidx].Coord2().Sub(u.particles[xidx].Coord2())
-			f := v.Scale(weight(xid, yid) * math.Log(math.Hypot(v.X, v.Y)))
+			v := r2.Sub(u.particles[yidx].Coord2(), u.particles[xidx].Coord2())
+			f := r2.Scale(weight(xid, yid)*math.Log(math.Hypot(v.X, v.Y)), v)
 			if math.IsInf(f.X, 0) || math.IsInf(f.Y, 0) {
 				return false
 			}
 			if math.Hypot(f.X, f.Y) > 1e-12 {
 				updated = true
 			}
-			u.forces[xidx] = u.forces[xidx].Add(f)
-			u.forces[yidx] = u.forces[yidx].Sub(f)
+			u.forces[xidx] = r2.Add(u.forces[xidx], f)
+			u.forces[yidx] = r2.Sub(u.forces[yidx], f)
 		}
 	}
 
@@ -160,7 +160,7 @@ func (u *EadesR2) Update(g graph.Graph, layout LayoutR2) bool {
 	}
 	for i, f := range u.forces {
 		n := u.particles[i].(eadesR2Node)
-		n.pos = n.pos.Add(f.Scale(rate))
+		n.pos = r2.Add(n.pos, r2.Scale(rate, f))
 		u.particles[i] = n
 		layout.SetCoord2(n.id, n.pos)
 	}
diff --git a/spatial/barneshut/barneshut2.go b/spatial/barneshut/barneshut2.go
index 08d52396..00a54b8e 100644
--- a/spatial/barneshut/barneshut2.go
+++ b/spatial/barneshut/barneshut2.go
@@ -37,7 +37,7 @@ func Gravity2(_, _ Particle2, m1, m2 float64, v r2.Vec) r2.Vec {
 	if d2 == 0 {
 		return r2.Vec{}
 	}
-	return v.Scale((m1 * m2) / (d2 * math.Sqrt(d2)))
+	return r2.Scale((m1*m2)/(d2*math.Sqrt(d2)), v)
 }
 
 // Plane implements Barnes-Hut force approximation calculations.
@@ -135,7 +135,7 @@ func (q *Plane) ForceOn(p Particle2, theta float64, f Force2) (force r2.Vec) {
 	m := p.Mass()
 	pv := p.Coord2()
 	for _, e := range q.Particles {
-		v = v.Add(f(p, e, m, e.Mass(), e.Coord2().Sub(pv)))
+		v = r2.Add(v, f(p, e, m, e.Mass(), r2.Sub(e.Coord2(), pv)))
 	}
 	return v
 }
@@ -260,7 +260,7 @@ func (t *tile) forceOn(p Particle2, pt r2.Vec, m, theta float64, f Force2) (vect
 	s := ((t.bounds.Max.X - t.bounds.Min.X) + (t.bounds.Max.Y - t.bounds.Min.Y)) / 2
 	d := math.Hypot(pt.X-t.center.X, pt.Y-t.center.Y)
 	if s/d < theta || t.particle != nil {
-		return f(p, t.particle, m, t.mass, t.center.Sub(pt))
+		return f(p, t.particle, m, t.mass, r2.Sub(t.center, pt))
 	}
 
 	var v r2.Vec
@@ -268,7 +268,7 @@ func (t *tile) forceOn(p Particle2, pt r2.Vec, m, theta float64, f Force2) (vect
 		if d == nil {
 			continue
 		}
-		v = v.Add(d.forceOn(p, pt, m, theta, f))
+		v = r2.Add(v, d.forceOn(p, pt, m, theta, f))
 	}
 	return v
 }
diff --git a/spatial/r2/deprecated.go b/spatial/r2/deprecated.go
new file mode 100644
index 00000000..01141b62
--- /dev/null
+++ b/spatial/r2/deprecated.go
@@ -0,0 +1,49 @@
+// Copyright ©2021 The Gonum Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package r2
+
+// TODO(sbinet): remove this file for Gonum-v0.10.0.
+
+// Add returns the vector sum of p and q.
+//
+// DEPRECATED: use r2.Add.
+func (p Vec) Add(q Vec) Vec {
+	return Add(p, q)
+}
+
+// Sub returns the vector sum of p and -q.
+//
+// DEPRECATED: use r2.Sub.
+func (p Vec) Sub(q Vec) Vec {
+	return Sub(p, q)
+}
+
+// Scale returns the vector p scaled by f.
+//
+// DEPRECATED: use r2.Scale.
+func (p Vec) Scale(f float64) Vec {
+	return Scale(f, p)
+}
+
+// Dot returns the dot product p·q.
+//
+// DEPRECATED: use r2.Dot.
+func (p Vec) Dot(q Vec) float64 {
+	return Dot(p, q)
+}
+
+// Cross returns the cross product p×q.
+//
+// DEPRECATED: use r2.Cross.
+func (p Vec) Cross(q Vec) float64 {
+	return Cross(p, q)
+}
+
+// Rotate returns a new vector, rotated by alpha around the provided point, q.
+//
+// DEPRECATED: use r2.Rotate.
+func (p Vec) Rotate(alpha float64, q Vec) Vec {
+	return Rotate(p, alpha, q)
+}
diff --git a/spatial/r2/vector.go b/spatial/r2/vector.go
index 0a19c196..18c1f390 100644
--- a/spatial/r2/vector.go
+++ b/spatial/r2/vector.go
@@ -12,38 +12,41 @@ type Vec struct {
 }
 
 // Add returns the vector sum of p and q.
-func (p Vec) Add(q Vec) Vec {
-	p.X += q.X
-	p.Y += q.Y
-	return p
+func Add(p, q Vec) Vec {
+	return Vec{
+		X: p.X + q.X,
+		Y: p.Y + q.Y,
+	}
 }
 
 // Sub returns the vector sum of p and -q.
-func (p Vec) Sub(q Vec) Vec {
-	p.X -= q.X
-	p.Y -= q.Y
-	return p
+func Sub(p, q Vec) Vec {
+	return Vec{
+		X: p.X - q.X,
+		Y: p.Y - q.Y,
+	}
 }
 
 // Scale returns the vector p scaled by f.
-func (p Vec) Scale(f float64) Vec {
-	p.X *= f
-	p.Y *= f
-	return p
+func Scale(f float64, p Vec) Vec {
+	return Vec{
+		X: f * p.X,
+		Y: f * p.Y,
+	}
 }
 
 // Dot returns the dot product p·q.
-func (p Vec) Dot(q Vec) float64 {
+func Dot(p, q Vec) float64 {
 	return p.X*q.X + p.Y*q.Y
 }
 
 // Cross returns the cross product p×q.
-func (p Vec) Cross(q Vec) float64 {
+func Cross(p, q Vec) float64 {
 	return p.X*q.Y - p.Y*q.X
 }
 
 // Rotate returns a new vector, rotated by alpha around the provided point, q.
-func (p Vec) Rotate(alpha float64, q Vec) Vec {
+func Rotate(p Vec, alpha float64, q Vec) Vec {
 	return NewRotation(alpha, q).Rotate(p)
 }
 
@@ -65,12 +68,12 @@ func Unit(p Vec) Vec {
 	if p.X == 0 && p.Y == 0 {
 		return Vec{X: math.NaN(), Y: math.NaN()}
 	}
-	return p.Scale(1 / Norm(p))
+	return Scale(1/Norm(p), p)
 }
 
 // Cos returns the cosine of the opening angle between p and q.
 func Cos(p, q Vec) float64 {
-	return p.Dot(q) / (Norm(p) * Norm(q))
+	return Dot(p, q) / (Norm(p) * Norm(q))
 }
 
 // Box is a 2D bounding box.
@@ -98,11 +101,11 @@ func (r Rotation) Rotate(p Vec) Vec {
 	if r.isIdentity() {
 		return p
 	}
-	o := p.Sub(r.p)
-	return Vec{
+	o := Sub(p, r.p)
+	return Add(Vec{
 		X: (o.X*r.cos - o.Y*r.sin),
 		Y: (o.X*r.sin + o.Y*r.cos),
-	}.Add(r.p)
+	}, r.p)
 }
 
 func (r Rotation) isIdentity() bool {
diff --git a/spatial/r3/deprecated.go b/spatial/r3/deprecated.go
new file mode 100644
index 00000000..10c83993
--- /dev/null
+++ b/spatial/r3/deprecated.go
@@ -0,0 +1,49 @@
+// Copyright ©2021 The Gonum Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package r3
+
+// TODO(sbinet): remove this file for Gonum-v0.10.0.
+
+// Add returns the vector sum of p and q.
+//
+// DEPRECATED: use r3.Add.
+func (p Vec) Add(q Vec) Vec {
+	return Add(p, q)
+}
+
+// Sub returns the vector sum of p and -q.
+//
+// DEPRECATED: use r3.Sub.
+func (p Vec) Sub(q Vec) Vec {
+	return Sub(p, q)
+}
+
+// Scale returns the vector p scaled by f.
+//
+// DEPRECATED: use r3.Scale.
+func (p Vec) Scale(f float64) Vec {
+	return Scale(f, p)
+}
+
+// Dot returns the dot product p·q.
+//
+// DEPRECATED: use r3.Dot.
+func (p Vec) Dot(q Vec) float64 {
+	return Dot(p, q)
+}
+
+// Cross returns the cross product p×q.
+//
+// DEPRECATED: use r3.Cross.
+func (p Vec) Cross(q Vec) Vec {
+	return Cross(p, q)
+}
+
+// Rotate returns a new vector, rotated by alpha around the provided axis.
+//
+// DEPRECATED: use r3.Rotate
+func (p Vec) Rotate(alpha float64, axis Vec) Vec {
+	return Rotate(p, alpha, axis)
+}
diff --git a/spatial/r3/vector.go b/spatial/r3/vector.go
index d87bc2a3..3aff8a73 100644
--- a/spatial/r3/vector.go
+++ b/spatial/r3/vector.go
@@ -16,36 +16,39 @@ type Vec struct {
 }
 
 // Add returns the vector sum of p and q.
-func (p Vec) Add(q Vec) Vec {
-	p.X += q.X
-	p.Y += q.Y
-	p.Z += q.Z
-	return p
+func Add(p, q Vec) Vec {
+	return Vec{
+		X: p.X + q.X,
+		Y: p.Y + q.Y,
+		Z: p.Z + q.Z,
+	}
 }
 
 // Sub returns the vector sum of p and -q.
-func (p Vec) Sub(q Vec) Vec {
-	p.X -= q.X
-	p.Y -= q.Y
-	p.Z -= q.Z
-	return p
+func Sub(p, q Vec) Vec {
+	return Vec{
+		X: p.X - q.X,
+		Y: p.Y - q.Y,
+		Z: p.Z - q.Z,
+	}
 }
 
 // Scale returns the vector p scaled by f.
-func (p Vec) Scale(f float64) Vec {
-	p.X *= f
-	p.Y *= f
-	p.Z *= f
-	return p
+func Scale(f float64, p Vec) Vec {
+	return Vec{
+		X: f * p.X,
+		Y: f * p.Y,
+		Z: f * p.Z,
+	}
 }
 
 // Dot returns the dot product p·q.
-func (p Vec) Dot(q Vec) float64 {
+func Dot(p, q Vec) float64 {
 	return p.X*q.X + p.Y*q.Y + p.Z*q.Z
 }
 
 // Cross returns the cross product p×q.
-func (p Vec) Cross(q Vec) Vec {
+func Cross(p, q Vec) Vec {
 	return Vec{
 		p.Y*q.Z - p.Z*q.Y,
 		p.Z*q.X - p.X*q.Z,
@@ -54,7 +57,7 @@ func (p Vec) Cross(q Vec) Vec {
 }
 
 // Rotate returns a new vector, rotated by alpha around the provided axis.
-func (p Vec) Rotate(alpha float64, axis Vec) Vec {
+func Rotate(p Vec, alpha float64, axis Vec) Vec {
 	return NewRotation(alpha, axis).Rotate(p)
 }
 
@@ -76,12 +79,12 @@ func Unit(p Vec) Vec {
 	if p.X == 0 && p.Y == 0 && p.Z == 0 {
 		return Vec{X: math.NaN(), Y: math.NaN(), Z: math.NaN()}
 	}
-	return p.Scale(1 / Norm(p))
+	return Scale(1/Norm(p), p)
 }
 
 // Cos returns the cosine of the opening angle between p and q.
 func Cos(p, q Vec) float64 {
-	return p.Dot(q) / (Norm(p) * Norm(q))
+	return Dot(p, q) / (Norm(p) * Norm(q))
 }
 
 // Box is a 3D bounding box.


Test patch:
diff --git a/spatial/barneshut/barneshut2_test.go b/spatial/barneshut/barneshut2_test.go
index bca0d472..2bbb4303 100644
--- a/spatial/barneshut/barneshut2_test.go
+++ b/spatial/barneshut/barneshut2_test.go
@@ -427,9 +427,9 @@ func TestPlaneForceOn(t *testing.T) {
 			m := p.Mass()
 			pv := p.Coord2()
 			for _, e := range particles {
-				v = v.Add(Gravity2(p, e, m, e.Mass(), e.Coord2().Sub(pv)))
+				v = r2.Add(v, Gravity2(p, e, m, e.Mass(), r2.Sub(e.Coord2(), pv)))
 			}
-			moved[i] = p.Coord2().Add(v)
+			moved[i] = r2.Add(p.Coord2(), v)
 		}
 
 		plane, err := NewPlane(particles)
@@ -448,8 +448,8 @@ func TestPlaneForceOn(t *testing.T) {
 						calls++
 						return Gravity2(p1, p2, m1, m2, v)
 					})
-					pos := p.Coord2().Add(v)
-					d := moved[i].Sub(pos)
+					pos := r2.Add(p.Coord2(), v)
+					d := r2.Sub(moved[i], pos)
 					ssd += d.X*d.X + d.Y*d.Y
 					sd += math.Hypot(d.X, d.Y)
 				}
diff --git a/spatial/r2/vector_test.go b/spatial/r2/vector_test.go
index ed16dc37..18f9014b 100644
--- a/spatial/r2/vector_test.go
+++ b/spatial/r2/vector_test.go
@@ -22,7 +22,7 @@ func TestAdd(t *testing.T) {
 		{Vec{1, -3}, Vec{1, -6}, Vec{2, -9}},
 		{Vec{1, 2}, Vec{-1, -2}, Vec{}},
 	} {
-		got := test.v1.Add(test.v2)
+		got := Add(test.v1, test.v2)
 		if got != test.want {
 			t.Errorf(
 				"error: %v + %v: got=%v, want=%v",
@@ -43,7 +43,7 @@ func TestSub(t *testing.T) {
 		{Vec{1, -3}, Vec{1, -6}, Vec{0, 3}},
 		{Vec{1, 2}, Vec{1, 2}, Vec{}},
 	} {
-		got := test.v1.Sub(test.v2)
+		got := Sub(test.v1, test.v2)
 		if got != test.want {
 			t.Errorf(
 				"error: %v - %v: got=%v, want=%v",
@@ -67,7 +67,7 @@ func TestScale(t *testing.T) {
 		{2, Vec{1, -3}, Vec{2, -6}},
 		{10, Vec{1, 2}, Vec{10, 20}},
 	} {
-		got := test.v.Scale(test.a)
+		got := Scale(test.a, test.v)
 		if got != test.want {
 			t.Errorf(
 				"error: %v * %v: got=%v, want=%v",
@@ -89,7 +89,7 @@ func TestDot(t *testing.T) {
 		{Vec{1, 2}, Vec{-0.3, 0.4}, 0.5},
 	} {
 		{
-			got := test.u.Dot(test.v)
+			got := Dot(test.u, test.v)
 			if got != test.want {
 				t.Errorf(
 					"error: %v · %v: got=%v, want=%v",
@@ -98,7 +98,7 @@ func TestDot(t *testing.T) {
 			}
 		}
 		{
-			got := test.v.Dot(test.u)
+			got := Dot(test.v, test.u)
 			if got != test.want {
 				t.Errorf(
 					"error: %v · %v: got=%v, want=%v",
@@ -120,7 +120,7 @@ func TestCross(t *testing.T) {
 		{Vec{1, 2}, Vec{-4, 5}, 13},
 		{Vec{1, 2}, Vec{2, 3}, -1},
 	} {
-		got := test.v1.Cross(test.v2)
+		got := Cross(test.v1, test.v2)
 		if got != test.want {
 			t.Errorf(
 				"error: %v × %v = %v, want %v",
@@ -236,7 +236,7 @@ func TestRotate(t *testing.T) {
 		{Vec{2, 2}, Vec{1, 1}, math.Pi, Vec{0, 0}},
 		{Vec{2, 2}, Vec{2, 0}, math.Pi, Vec{2, -2}},
 	} {
-		got := test.v.Rotate(test.alpha, test.q)
+		got := Rotate(test.v, test.alpha, test.q)
 		if !vecApproxEqual(got, test.want) {
 			t.Errorf(
 				"rotate(%v, %v, %v)= %v, want=%v",
diff --git a/spatial/r3/vector_test.go b/spatial/r3/vector_test.go
index 864a4cc1..d9a85988 100644
--- a/spatial/r3/vector_test.go
+++ b/spatial/r3/vector_test.go
@@ -22,7 +22,7 @@ func TestAdd(t *testing.T) {
 		{Vec{1, -3, 5}, Vec{1, -6, -6}, Vec{2, -9, -1}},
 		{Vec{1, 2, 3}, Vec{-1, -2, -3}, Vec{}},
 	} {
-		got := test.v1.Add(test.v2)
+		got := Add(test.v1, test.v2)
 		if got != test.want {
 			t.Errorf(
 				"error: %v + %v: got=%v, want=%v",
@@ -43,7 +43,7 @@ func TestSub(t *testing.T) {
 		{Vec{1, -3, 5}, Vec{1, -6, -6}, Vec{0, 3, 11}},
 		{Vec{1, 2, 3}, Vec{1, 2, 3}, Vec{}},
 	} {
-		got := test.v1.Sub(test.v2)
+		got := Sub(test.v1, test.v2)
 		if got != test.want {
 			t.Errorf(
 				"error: %v - %v: got=%v, want=%v",
@@ -67,7 +67,7 @@ func TestScale(t *testing.T) {
 		{2, Vec{1, -3, 5}, Vec{2, -6, 10}},
 		{10, Vec{1, 2, 3}, Vec{10, 20, 30}},
 	} {
-		got := test.v.Scale(test.a)
+		got := Scale(test.a, test.v)
 		if got != test.want {
 			t.Errorf(
 				"error: %v * %v: got=%v, want=%v",
@@ -89,7 +89,7 @@ func TestDot(t *testing.T) {
 		{Vec{1, 2, 2}, Vec{-0.3, 0.4, -1.2}, -1.9},
 	} {
 		{
-			got := test.u.Dot(test.v)
+			got := Dot(test.u, test.v)
 			if got != test.want {
 				t.Errorf(
 					"error: %v · %v: got=%v, want=%v",
@@ -98,7 +98,7 @@ func TestDot(t *testing.T) {
 			}
 		}
 		{
-			got := test.v.Dot(test.u)
+			got := Dot(test.v, test.u)
 			if got != test.want {
 				t.Errorf(
 					"error: %v · %v: got=%v, want=%v",
@@ -120,7 +120,7 @@ func TestCross(t *testing.T) {
 		{Vec{1, 2, 3}, Vec{1, 2, 3}, Vec{}},
 		{Vec{1, 2, 3}, Vec{2, 3, 4}, Vec{-1, 2, -1}},
 	} {
-		got := test.v1.Cross(test.v2)
+		got := Cross(test.v1, test.v2)
 		if got != test.want {
 			t.Errorf(
 				"error: %v × %v = %v, want %v",
@@ -233,7 +233,7 @@ func TestRotate(t *testing.T) {
 		{Vec{2, 0, 0}, Vec{0, 1, 0}, math.Pi, Vec{-2, 0, 0}},
 		{Vec{1, 2, 3}, Vec{1, 1, 1}, 2. / 3. * math.Pi, Vec{3, 1, 2}},
 	} {
-		got := test.v.Rotate(test.alpha, test.axis)
+		got := Rotate(test.v, test.alpha, test.axis)
 		if !vecApproxEqual(got, test.want, tol) {
 			t.Errorf(
 				"rotate(%v, %v, %v)= %v, want=%v",