add matrix_test.go

This commit is contained in:
Josh Deprez 2021-09-28 11:39:07 +10:00
parent 83d6b23f51
commit cdc1bf25fc
2 changed files with 143 additions and 1 deletions

View file

@ -5,6 +5,8 @@ import (
"image" "image"
) )
var errSingularMatrix = errors.New("matrix is singular")
// IntMatrix3 implements a 3x3 integer matrix. // IntMatrix3 implements a 3x3 integer matrix.
type IntMatrix3 [3][3]int type IntMatrix3 [3][3]int
@ -138,7 +140,28 @@ func (a RatMatrix3) Inverse() (RatMatrix3, error) {
adj := a.Adjugate() adj := a.Adjugate()
det := a[0][0].Mul(adj[0][0]).Add(a[0][1].Mul(adj[1][0])).Add(a[0][2].Mul(adj[2][0])) det := a[0][0].Mul(adj[0][0]).Add(a[0][1].Mul(adj[1][0])).Add(a[0][2].Mul(adj[2][0]))
if det.N == 0 { if det.N == 0 {
return RatMatrix3{}, errors.New("matrix is singular") return RatMatrix3{}, errSingularMatrix
} }
return adj.Mul(det.Invert()), nil return adj.Mul(det.Invert()), nil
} }
// Concat returns the matrix equivalent to applying matrix a and then b.
func (a RatMatrix3) Concat(b RatMatrix3) RatMatrix3 {
return RatMatrix3{
0: [3]Rat{
a[0][0].Mul(b[0][0]).Add(a[0][1].Mul(b[1][0])).Add(a[0][2].Mul(b[2][0])),
a[0][0].Mul(b[0][1]).Add(a[0][1].Mul(b[1][1])).Add(a[0][2].Mul(b[2][1])),
a[0][0].Mul(b[0][2]).Add(a[0][1].Mul(b[1][2])).Add(a[0][2].Mul(b[2][2])),
},
1: [3]Rat{
a[1][0].Mul(b[0][0]).Add(a[1][1].Mul(b[1][0])).Add(a[1][2].Mul(b[2][0])),
a[1][0].Mul(b[0][1]).Add(a[1][1].Mul(b[1][1])).Add(a[1][2].Mul(b[2][1])),
a[1][0].Mul(b[0][2]).Add(a[1][1].Mul(b[1][2])).Add(a[1][2].Mul(b[2][2])),
},
2: [3]Rat{
a[2][0].Mul(b[0][0]).Add(a[2][1].Mul(b[1][0])).Add(a[2][2].Mul(b[2][0])),
a[2][0].Mul(b[0][1]).Add(a[2][1].Mul(b[1][1])).Add(a[2][2].Mul(b[2][1])),
a[2][0].Mul(b[0][2]).Add(a[2][1].Mul(b[1][2])).Add(a[2][2].Mul(b[2][2])),
},
}
}

119
geom/matrix_test.go Normal file
View file

@ -0,0 +1,119 @@
package geom
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func TestRatMatrix3InverseAndConcat(t *testing.T) {
identity := RatMatrix3{
0: [3]Rat{{1, 1}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {1, 1}, {0, 1}},
2: [3]Rat{{0, 1}, {0, 1}, {1, 1}},
}
tests := []struct {
in, want RatMatrix3
}{
{ // identity
in: identity,
want: identity,
},
{ // diagonal
in: RatMatrix3{
0: [3]Rat{{3, 1}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {1, 2}, {0, 1}},
2: [3]Rat{{0, 1}, {0, 1}, {7, 13}},
},
want: RatMatrix3{
0: [3]Rat{{1, 3}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {2, 1}, {0, 1}},
2: [3]Rat{{0, 1}, {0, 1}, {13, 7}},
},
},
{ // hexagonal prism layout
in: RatMatrix3{
0: [3]Rat{{24, 1}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {16, 1}, {0, 1}},
2: [3]Rat{{8, 1}, {0, 1}, {16, 1}},
},
want: RatMatrix3{
0: [3]Rat{{1, 24}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {1, 16}, {0, 1}},
2: [3]Rat{{-1, 48}, {0, 1}, {1, 16}},
},
},
{ // random small positive integer matrix
in: RatMatrix3{
0: [3]Rat{{90, 1}, {40, 1}, {15, 1}},
1: [3]Rat{{28, 1}, {54, 1}, {77, 1}},
2: [3]Rat{{35, 1}, {99, 1}, {9, 1}},
},
want: RatMatrix3{
0: [3]Rat{{7137, 531380}, {-225, 106276}, {-227, 53138}},
1: [3]Rat{{-2443, 531380}, {-57, 106276}, {651, 53138}},
2: [3]Rat{{-441, 265690}, {751, 53138}, {-187, 26569}},
},
},
{ // random small integer matrix
in: RatMatrix3{
0: [3]Rat{{-38, 1}, {-23, 1}, {60, 1}},
1: [3]Rat{{-75, 1}, {-22, 1}, {33, 1}},
2: [3]Rat{{-5, 1}, {-6, 1}, {-14, 1}},
},
want: RatMatrix3{
0: [3]Rat{{46, 2647}, {-62, 2647}, {51, 2647}},
1: [3]Rat{{-1215, 29117}, {832, 29117}, {-3246, 29117}},
2: [3]Rat{{340, 29117}, {-113, 29117}, {-889, 29117}},
},
},
}
for _, test := range tests {
got, err := test.in.Inverse()
if err != nil {
t.Errorf("(%v).Inverse() error = %v, want nil", test.in, err)
}
if diff := cmp.Diff(got, test.want); diff != "" {
t.Errorf("(%v).Inverse() diff:\n%s", test.in, diff)
}
got2 := test.in.Concat(got)
if diff := cmp.Diff(got2, identity); diff != "" {
t.Errorf("(%v).Concat(%v) diff:\n%s", test.in, got2, diff)
}
got3 := got.Concat(test.in)
if diff := cmp.Diff(got3, identity); diff != "" {
t.Errorf("(%v).Concat(%v) diff\n%s", got, test.in, diff)
}
}
}
func TestRatMatrix3InvertSingular(t *testing.T) {
tests := []RatMatrix3{
{ // zero row and column
0: [3]Rat{{1, 1}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {1, 1}, {0, 1}},
2: [3]Rat{{0, 1}, {0, 1}, {0, 1}},
},
{ // zero row
0: [3]Rat{{1, 1}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {1, 1}, {1, 1}},
2: [3]Rat{{0, 1}, {0, 1}, {0, 1}},
},
{ // zero column
0: [3]Rat{{1, 1}, {0, 1}, {0, 1}},
1: [3]Rat{{0, 1}, {1, 1}, {0, 1}},
2: [3]Rat{{0, 1}, {1, 1}, {0, 1}},
},
{ // product of random 3x2 and 2x3 integer matrices
0: [3]Rat{{-4330, 1}, {1283, 1}, {2717, 1}},
1: [3]Rat{{1978, 1}, {-10171, 1}, {571, 1}},
2: [3]Rat{{-4962, 1}, {-3689, 1}, {4089, 1}},
},
}
for _, test := range tests {
if _, err := test.Inverse(); err != errSingularMatrix {
t.Errorf("(%v).Inverse() error = %v, want 'matrix is singular'", test, err)
}
}
}