Skip to content

Commit

Permalink
Implement ModScalar, Min, Max operations (#12)
Browse files Browse the repository at this point in the history
Closes #5 
Closes #10
Closes #11
  • Loading branch information
Xkonti authored Sep 15, 2023
1 parent d945341 commit b5699a1
Show file tree
Hide file tree
Showing 9 changed files with 580 additions and 5 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ go get github.com/xkonti/govec
| `Inv` | ✔️ | ✔️ | Computes the multiplicative inverse of each component in the vector. |
| `Len` | ✔️ | ✔️ | Calculates the length of the vector. Returns `float64` for integer vectors. |
| `LenSqr` | ✔️ | ✔️ | Calculates the squared length of the vector. Returns `float64` for integer vectors. |
| `Mod` | ✔️ | ✔️ | Computes the modulus of each component in the vector. |
| `Max` | ✔️ | ✔️ | Returns the maximum component values from two vectors. |
| `Min` | ✔️ | ✔️ | Returns the minimum component values from two vectors. |
| `Mod` | ✔️ | ✔️ | Computes the modulus of each component in the vector against another vector components. |
| `ModScalar` | ✔️ | ✔️ | Computes the modulus of each component in the vector against a single scalar. |
| `Mul` | ✔️ | ✔️ | Performs vector multiplication. |
| `MulScalar` | ✔️ | ✔️ | Multiplies each component of the vector by a scalar. |
| `Neg` | ✔️ | ✔️ | Negates each component of the vector. |
Expand Down Expand Up @@ -83,8 +86,6 @@ any of these implemented or if you'd like to propose a different operation.
| `IsUnit` | | Checks if the vector is a unit vector. |
| `IsZero` | Yes (v1.0) | Checks if the vector is a zero vector. |
| `Lerp` | Yes (v1.0) | Linearly interpolates between two vectors. |
| `Max` | Yes (v1.0) | Returns the maximum component values from two vectors. |
| `Min` | Yes (v1.0) | Returns the minimum component values from two vectors. |
| `Orthogonalize` | | Generates an orthogonal (or orthonormal) vector set. |
| `Project` | | Projects a 3D vector onto a plane. |
| `Random` | Yes (v1.0) | Generates a normal vector with random components. |
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
101 changes: 101 additions & 0 deletions max.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package govec

// V2F

// Max returns a vector with the maximum of each component of the two vectors.
func (v V2F[T]) Max(v2 V2F[T]) V2F[T] {
return V2F[T]{X: max(v.X, v2.X), Y: max(v.Y, v2.Y)}
}

// MaxInPlace updates the vector in-place with the maximum of each component of the two vectors.
func (v *V2F[T]) MaxInPlace(v2 V2F[T]) {
v.X = max(v.X, v2.X)
v.Y = max(v.Y, v2.Y)
}

// MaxComp returns a vector with the maximum of each component of the vector and the given components.
func (v V2F[T]) MaxComp(x T, y T) V2F[T] {
return V2F[T]{X: max(v.X, x), Y: max(v.Y, y)}
}

// MaxCompInPlace updates the vector in-place with the maximum of each component of the vector and the given components.
func (v *V2F[T]) MaxCompInPlace(x T, y T) {
v.X = max(v.X, x)
v.Y = max(v.Y, y)
}

// V3F

// Max returns a vector with the maximum of each component of the two vectors.
func (v V3F[T]) Max(v2 V3F[T]) V3F[T] {
return V3F[T]{X: max(v.X, v2.X), Y: max(v.Y, v2.Y), Z: max(v.Z, v2.Z)}
}

// MaxInPlace updates the vector in-place with the maximum of each component of the two vectors.
func (v *V3F[T]) MaxInPlace(v2 V3F[T]) {
v.X = max(v.X, v2.X)
v.Y = max(v.Y, v2.Y)
v.Z = max(v.Z, v2.Z)
}

// MaxComp returns a vector with the maximum of each component of the vector and the given components.
func (v V3F[T]) MaxComp(x T, y T, z T) V3F[T] {
return V3F[T]{X: max(v.X, x), Y: max(v.Y, y), Z: max(v.Z, z)}
}

// MaxCompInPlace updates the vector in-place with the maximum of each component of the vector and the given components.
func (v *V3F[T]) MaxCompInPlace(x T, y T, z T) {
v.X = max(v.X, x)
v.Y = max(v.Y, y)
v.Z = max(v.Z, z)
}

// V2I

// Max returns a vector with the maximum of each component of the two vectors.
func (v V2I[T]) Max(v2 V2I[T]) V2I[T] {
return V2I[T]{X: max(v.X, v2.X), Y: max(v.Y, v2.Y)}
}

// MaxInPlace updates the vector in-place with the maximum of each component of the two vectors.
func (v *V2I[T]) MaxInPlace(v2 V2I[T]) {
v.X = max(v.X, v2.X)
v.Y = max(v.Y, v2.Y)
}

// MaxComp returns a vector with the maximum of each component of the vector and the given components.
func (v V2I[T]) MaxComp(x T, y T) V2I[T] {
return V2I[T]{X: max(v.X, x), Y: max(v.Y, y)}
}

// MaxCompInPlace updates the vector in-place with the maximum of each component of the vector and the given components.
func (v *V2I[T]) MaxCompInPlace(x T, y T) {
v.X = max(v.X, x)
v.Y = max(v.Y, y)
}

// V3I

// Max returns a vector with the maximum of each component of the two vectors.
func (v V3I[T]) Max(v2 V3I[T]) V3I[T] {
return V3I[T]{X: max(v.X, v2.X), Y: max(v.Y, v2.Y), Z: max(v.Z, v2.Z)}
}

// MaxInPlace updates the vector in-place with the maximum of each component of the two vectors.
func (v *V3I[T]) MaxInPlace(v2 V3I[T]) {
v.X = max(v.X, v2.X)
v.Y = max(v.Y, v2.Y)
v.Z = max(v.Z, v2.Z)
}

// MaxComp returns a vector with the maximum of each component of the vector and the given components.
func (v V3I[T]) MaxComp(x T, y T, z T) V3I[T] {
return V3I[T]{X: max(v.X, x), Y: max(v.Y, y), Z: max(v.Z, z)}
}

// MaxCompInPlace updates the vector in-place with the maximum of each component of the vector and the given components.
func (v *V3I[T]) MaxCompInPlace(x T, y T, z T) {
v.X = max(v.X, x)
v.Y = max(v.Y, y)
v.Z = max(v.Z, z)
}
158 changes: 158 additions & 0 deletions max_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package govec

import (
"golang.org/x/exp/constraints"
"testing"
)

func runMaxTestsFloat[T constraints.Float](t *testing.T, unitName string) {
testCases := []struct {
name string
v1x, v1y, v1z T
v2x, v2y, v2z T
expectedX, expectedY, expectedZ T
}{
{"all same", 1, 1, 1, 1, 1, 1, 1, 1, 1},
{"v1 max", 2, 3, 4, 1, 2, 3, 2, 3, 4},
{"v2 max", 1, 2, 3, 2, 3, 4, 2, 3, 4},
{"v1 max neg", -1, -2, -3, -2, -3, -4, -1, -2, -3},
{"v2 max neg", -2, -3, -4, -1, -2, -3, -1, -2, -3},
{"v1 max mixed", -1, 2, -3, -2, 1, -4, -1, 2, -3},
{"v2 max mixed", -2, 1, -4, -1, 2, -3, -1, 2, -3},
{"rand 1", 3, 5, -3, 15, 2, 4, 15, 5, 4},
{"rand 2", 120, 127, -2, -51, 36, 1, 120, 127, 1},
}

for _, tc := range testCases {
t.Run(tc.name+" V2F["+unitName+"]", func(t *testing.T) {
// Bring to proper type
v1x, v1y := tc.v1x, tc.v1y
v2x, v2y := tc.v2x, tc.v2y
expectedX, expectedY := tc.expectedX, tc.expectedY

expected := V2F[T]{X: expectedX, Y: expectedY}

// Results of operation variants
vMax := V2F[T]{X: v1x, Y: v1y}.Max(V2F[T]{X: v2x, Y: v2y})
vMaxComp := V2F[T]{X: v1x, Y: v1y}.MaxComp(v2x, v2y)
vMaxInPlace := V2F[T]{X: v1x, Y: v1y}
vMaxInPlace.MaxInPlace(V2F[T]{X: v2x, Y: v2y})
vMaxCompInPlace := V2F[T]{X: v1x, Y: v1y}
vMaxCompInPlace.MaxCompInPlace(v2x, v2y)
results := []V2F[T]{vMax, vMaxComp, vMaxInPlace, vMaxCompInPlace}

// Check results
for i, result := range results {
if !almostEqual(result.X, expected.X, 1e-9) || !almostEqual(result.Y, expected.Y, 1e-9) {
t.Errorf("Expected %v, got %v when testing variant #%v", expected, result, i)
}
}
})

t.Run(tc.name+" V3F["+unitName+"]", func(t *testing.T) {
// Bring to proper type
v1x, v1y, v1z := tc.v1x, tc.v1y, tc.v1z
v2x, v2y, v2z := tc.v2x, tc.v2y, tc.v2z
expectedX, expectedY, expectedZ := tc.expectedX, tc.expectedY, tc.expectedZ

expected := V3F[T]{X: expectedX, Y: expectedY, Z: expectedZ}

// Results of operation variants
vMax := V3F[T]{X: v1x, Y: v1y, Z: v1z}.Max(V3F[T]{X: v2x, Y: v2y, Z: v2z})
vMaxComp := V3F[T]{X: v1x, Y: v1y, Z: v1z}.MaxComp(v2x, v2y, v2z)
vMaxInPlace := V3F[T]{X: v1x, Y: v1y, Z: v1z}
vMaxInPlace.MaxInPlace(V3F[T]{X: v2x, Y: v2y, Z: v2z})
vMaxCompInPlace := V3F[T]{X: v1x, Y: v1y, Z: v1z}
vMaxCompInPlace.MaxCompInPlace(v2x, v2y, v2z)
results := []V3F[T]{vMax, vMaxComp, vMaxInPlace, vMaxCompInPlace}

// Check results
for i, result := range results {
if !almostEqual(result.X, expected.X, 1e-9) || !almostEqual(result.Y, expected.Y, 1e-9) || !almostEqual(result.Z, expected.Z, 1e-9) {
t.Errorf("Expected %v, got %v when testing variant #%v", expected, result, i)
}
}
})
}
}

func runMaxTestsInteger[T constraints.Signed](t *testing.T, unitName string) {
testCases := []struct {
name string
v1x, v1y, v1z T
v2x, v2y, v2z T
expectedX, expectedY, expectedZ T
}{
{"all same", 1, 1, 1, 1, 1, 1, 1, 1, 1},
{"v1 max", 2, 3, 4, 1, 2, 3, 2, 3, 4},
{"v2 max", 1, 2, 3, 2, 3, 4, 2, 3, 4},
{"v1 max neg", -1, -2, -3, -2, -3, -4, -1, -2, -3},
{"v2 max neg", -2, -3, -4, -1, -2, -3, -1, -2, -3},
{"v1 max mixed", -1, 2, -3, -2, 1, -4, -1, 2, -3},
{"v2 max mixed", -2, 1, -4, -1, 2, -3, -1, 2, -3},
{"rand 1", 3, 5, -3, 15, 2, 4, 15, 5, 4},
{"rand 2", 120, 127, -2, -51, 36, 1, 120, 127, 1},
}

for _, tc := range testCases {
t.Run(tc.name+" V2I["+unitName+"]", func(t *testing.T) {
// Bring to proper type
v1x, v1y := tc.v1x, tc.v1y
v2x, v2y := tc.v2x, tc.v2y
expectedX, expectedY := tc.expectedX, tc.expectedY

expected := V2I[T]{X: expectedX, Y: expectedY}

// Results of operation variants
vMax := V2I[T]{X: v1x, Y: v1y}.Max(V2I[T]{X: v2x, Y: v2y})
vMaxComp := V2I[T]{X: v1x, Y: v1y}.MaxComp(v2x, v2y)
vMaxInPlace := V2I[T]{X: v1x, Y: v1y}
vMaxInPlace.MaxInPlace(V2I[T]{X: v2x, Y: v2y})
vMaxCompInPlace := V2I[T]{X: v1x, Y: v1y}
vMaxCompInPlace.MaxCompInPlace(v2x, v2y)
results := []V2I[T]{vMax, vMaxComp, vMaxInPlace, vMaxCompInPlace}

// Check results
for i, result := range results {
if result.X != expected.X || result.Y != expected.Y {
t.Errorf("Expected %v, got %v when testing variant #%v", expected, result, i)
}
}
})

t.Run(tc.name+" V3I["+unitName+"]", func(t *testing.T) {
// Bring to proper type
v1x, v1y, v1z := tc.v1x, tc.v1y, tc.v1z
v2x, v2y, v2z := tc.v2x, tc.v2y, tc.v2z
expectedX, expectedY, expectedZ := tc.expectedX, tc.expectedY, tc.expectedZ

expected := V3I[T]{X: expectedX, Y: expectedY, Z: expectedZ}

// Results of operation variants
vMax := V3I[T]{X: v1x, Y: v1y, Z: v1z}.Max(V3I[T]{X: v2x, Y: v2y, Z: v2z})
vMaxComp := V3I[T]{X: v1x, Y: v1y, Z: v1z}.MaxComp(v2x, v2y, v2z)
vMaxInPlace := V3I[T]{X: v1x, Y: v1y, Z: v1z}
vMaxInPlace.MaxInPlace(V3I[T]{X: v2x, Y: v2y, Z: v2z})
vMaxCompInPlace := V3I[T]{X: v1x, Y: v1y, Z: v1z}
vMaxCompInPlace.MaxCompInPlace(v2x, v2y, v2z)
results := []V3I[T]{vMax, vMaxComp, vMaxInPlace, vMaxCompInPlace}

// Check results
for i, result := range results {
if result.X != expected.X || result.Y != expected.Y || result.Z != expected.Z {
t.Errorf("Expected %v, got %v when testing variant #%v", expected, result, i)
}
}
})
}
}

func TestMax(t *testing.T) {
runMaxTestsFloat[float64](t, "float64")
runMaxTestsFloat[float32](t, "float32")
runMaxTestsInteger[int64](t, "int64")
runMaxTestsInteger[int32](t, "int32")
runMaxTestsInteger[int16](t, "int16")
runMaxTestsInteger[int8](t, "int8")
runMaxTestsInteger[int](t, "int")
}
101 changes: 101 additions & 0 deletions min.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package govec

// V2F

// Min returns a vector with the minimum of each component of the two vectors.
func (v V2F[T]) Min(v2 V2F[T]) V2F[T] {
return V2F[T]{X: min(v.X, v2.X), Y: min(v.Y, v2.Y)}
}

// MinInPlace updates the vector in-place with the minimum of each component of the two vectors.
func (v *V2F[T]) MinInPlace(v2 V2F[T]) {
v.X = min(v.X, v2.X)
v.Y = min(v.Y, v2.Y)
}

// MinComp returns a vector with the minimum of each component of the vector and the given components.
func (v V2F[T]) MinComp(x T, y T) V2F[T] {
return V2F[T]{X: min(v.X, x), Y: min(v.Y, y)}
}

// MinCompInPlace updates the vector in-place with the minimum of each component of the vector and the given components.
func (v *V2F[T]) MinCompInPlace(x T, y T) {
v.X = min(v.X, x)
v.Y = min(v.Y, y)
}

// V3F

// Min returns a vector with the minimum of each component of the two vectors.
func (v V3F[T]) Min(v2 V3F[T]) V3F[T] {
return V3F[T]{X: min(v.X, v2.X), Y: min(v.Y, v2.Y), Z: min(v.Z, v2.Z)}
}

// MinInPlace updates the vector in-place with the minimum of each component of the two vectors.
func (v *V3F[T]) MinInPlace(v2 V3F[T]) {
v.X = min(v.X, v2.X)
v.Y = min(v.Y, v2.Y)
v.Z = min(v.Z, v2.Z)
}

// MinComp returns a vector with the minimum of each component of the vector and the given components.
func (v V3F[T]) MinComp(x T, y T, z T) V3F[T] {
return V3F[T]{X: min(v.X, x), Y: min(v.Y, y), Z: min(v.Z, z)}
}

// MinCompInPlace updates the vector in-place with the minimum of each component of the vector and the given components.
func (v *V3F[T]) MinCompInPlace(x T, y T, z T) {
v.X = min(v.X, x)
v.Y = min(v.Y, y)
v.Z = min(v.Z, z)
}

// V2I

// Min returns a vector with the minimum of each component of the two vectors.
func (v V2I[T]) Min(v2 V2I[T]) V2I[T] {
return V2I[T]{X: min(v.X, v2.X), Y: min(v.Y, v2.Y)}
}

// MinInPlace updates the vector in-place with the minimum of each component of the two vectors.
func (v *V2I[T]) MinInPlace(v2 V2I[T]) {
v.X = min(v.X, v2.X)
v.Y = min(v.Y, v2.Y)
}

// MinComp returns a vector with the minimum of each component of the vector and the given components.
func (v V2I[T]) MinComp(x T, y T) V2I[T] {
return V2I[T]{X: min(v.X, x), Y: min(v.Y, y)}
}

// MinCompInPlace updates the vector in-place with the minimum of each component of the vector and the given components.
func (v *V2I[T]) MinCompInPlace(x T, y T) {
v.X = min(v.X, x)
v.Y = min(v.Y, y)
}

// V3I

// Min returns a vector with the minimum of each component of the two vectors.
func (v V3I[T]) Min(v2 V3I[T]) V3I[T] {
return V3I[T]{X: min(v.X, v2.X), Y: min(v.Y, v2.Y), Z: min(v.Z, v2.Z)}
}

// MinInPlace updates the vector in-place with the minimum of each component of the two vectors.
func (v *V3I[T]) MinInPlace(v2 V3I[T]) {
v.X = min(v.X, v2.X)
v.Y = min(v.Y, v2.Y)
v.Z = min(v.Z, v2.Z)
}

// MinComp returns a vector with the minimum of each component of the vector and the given components.
func (v V3I[T]) MinComp(x T, y T, z T) V3I[T] {
return V3I[T]{X: min(v.X, x), Y: min(v.Y, y), Z: min(v.Z, z)}
}

// MinCompInPlace updates the vector in-place with the minimum of each component of the vector and the given components.
func (v *V3I[T]) MinCompInPlace(x T, y T, z T) {
v.X = min(v.X, x)
v.Y = min(v.Y, y)
v.Z = min(v.Z, z)
}
Loading

0 comments on commit b5699a1

Please sign in to comment.