func (m1 Mat3x2) ApproxEqualThreshold(m2 Mat3x2, threshold float64) bool {
	for i := range m1 {
		if !FloatEqualThreshold(m1[i], m2[i], threshold) {
			return false
		}
	}
	return true
}
ApproxEqualThreshold performs an element-wise approximate equality test between two matrices
with a given epsilon threshold, as if FloatEqualThreshold had been used.
package mgl64
import (
	"bytes"
	"fmt"
	"text/tabwriter"
	"golang.org/x/image/math/f64"
)
type Mat2 [4]float64
type Mat2x3 [6]float64
type Mat2x4 [8]float64
type Mat3x2 [6]float64
type Mat3 f64.Mat3
type Mat3x4 [12]float64
type Mat4x2 [8]float64
type Mat4x3 [12]float64
type Mat4 f64.Mat4
func (m Mat2) Mat3() Mat3 {
	col0, col1 := m.Cols()
	return Mat3FromCols(
		col0.Vec3(0),
		col1.Vec3(0),
		Vec3{0, 0, 1},
	)
}
func (m Mat2) Mat4() Mat4 {
	col0, col1 := m.Cols()
	return Mat4FromCols(
		col0.Vec4(0, 0),
		col1.Vec4(0, 0),
		Vec4{0, 0, 1, 0},
		Vec4{0, 0, 0, 1},
	)
}
func (m Mat3) Mat2() Mat2 {
	col0, col1, _ := m.Cols()
	return Mat2FromCols(
		col0.Vec2(),
		col1.Vec2(),
	)
}
func (m Mat3) Mat4() Mat4 {
	col0, col1, col2 := m.Cols()
	return Mat4FromCols(
		col0.Vec4(0),
		col1.Vec4(0),
		col2.Vec4(0),
		Vec4{0, 0, 0, 1},
	)
}
func (m Mat4) Mat2() Mat2 {
	col0, col1, _, _ := m.Cols()
	return Mat2FromCols(
		col0.Vec2(),
		col1.Vec2(),
	)
}
func (m Mat4) Mat3() Mat3 {
	col0, col1, col2, _ := m.Cols()
	return Mat3FromCols(
		col0.Vec3(),
		col1.Vec3(),
		col2.Vec3(),
	)
}
func (m *Mat2) SetCol(col int, v Vec2) {
	m[col*2+0], m[col*2+1] = v[0], v[1]
}
func (m *Mat2) SetRow(row int, v Vec2) {
	m[row+0], m[row+2] = v[0], v[1]
}
func (m Mat2) Diag() Vec2 {
	return Vec2{m[0], m[3]}
}
func Ident2() Mat2 {
	return Mat2{1, 0, 0, 1}
}
func Diag2(v Vec2) Mat2 {
	var m Mat2
	m[0], m[3] = v[0], v[1]
	return m
}
func Mat2FromRows(row0, row1 Vec2) Mat2 {
	return Mat2{row0[0], row1[0], row0[1], row1[1]}
}
func Mat2FromCols(col0, col1 Vec2) Mat2 {
	return Mat2{col0[0], col0[1], col1[0], col1[1]}
}
func (m1 Mat2) Add(m2 Mat2) Mat2 {
	return Mat2{m1[0] + m2[0], m1[1] + m2[1], m1[2] + m2[2], m1[3] + m2[3]}
}
func (m1 Mat2) Sub(m2 Mat2) Mat2 {
	return Mat2{m1[0] - m2[0], m1[1] - m2[1], m1[2] - m2[2], m1[3] - m2[3]}
}
func (m1 Mat2) Mul(c float64) Mat2 {
	return Mat2{m1[0] * c, m1[1] * c, m1[2] * c, m1[3] * c}
}
func (m1 Mat2) Mul2x1(m2 Vec2) Vec2 {
	return Vec2{
		m1[0]*m2[0] + m1[2]*m2[1],
		m1[1]*m2[0] + m1[3]*m2[1],
	}
}
func (m1 Mat2) Mul2(m2 Mat2) Mat2 {
	return Mat2{
		m1[0]*m2[0] + m1[2]*m2[1],
		m1[1]*m2[0] + m1[3]*m2[1],
		m1[0]*m2[2] + m1[2]*m2[3],
		m1[1]*m2[2] + m1[3]*m2[3],
	}
}
func (m1 Mat2) Mul2x3(m2 Mat2x3) Mat2x3 {
	return Mat2x3{
		m1[0]*m2[0] + m1[2]*m2[1],
		m1[1]*m2[0] + m1[3]*m2[1],
		m1[0]*m2[2] + m1[2]*m2[3],
		m1[1]*m2[2] + m1[3]*m2[3],
		m1[0]*m2[4] + m1[2]*m2[5],
		m1[1]*m2[4] + m1[3]*m2[5],
	}
}
func (m1 Mat2) Mul2x4(m2 Mat2x4) Mat2x4 {
	return Mat2x4{
		m1[0]*m2[0] + m1[2]*m2[1],
		m1[1]*m2[0] + m1[3]*m2[1],
		m1[0]*m2[2] + m1[2]*m2[3],
		m1[1]*m2[2] + m1[3]*m2[3],
		m1[0]*m2[4] + m1[2]*m2[5],
		m1[1]*m2[4] + m1[3]*m2[5],
		m1[0]*m2[6] + m1[2]*m2[7],
		m1[1]*m2[6] + m1[3]*m2[7],
	}
}
func (m1 Mat2) Transpose() Mat2 {
	return Mat2{m1[0], m1[2], m1[1], m1[3]}
}
func (m Mat2) Det() float64 {
	return m[0]*m[3] - m[1]*m[2]
}
func (m Mat2) Inv() Mat2 {
	det := m.Det()
	if FloatEqual(det, float64(0.0)) {
		return Mat2{}
	}
	retMat := Mat2{m[3], -m[1], -m[2], m[0]}
	return retMat.Mul(1 / det)
}
func (m1 Mat2) ApproxEqual(m2 Mat2) bool {
	for i := range m1 {
		if !FloatEqual(m1[i], m2[i]) {
			return false
		}
	}
	return true
}
func (m1 Mat2) ApproxEqualThreshold(m2 Mat2, threshold float64) bool {
	for i := range m1 {
		if !FloatEqualThreshold(m1[i], m2[i], threshold) {
			return false
		}
	}
	return true
}
func (m1 Mat2) ApproxFuncEqual(m2 Mat2, eq func(float64, float64) bool) bool {
	for i := range m1 {
		if !eq(m1[i], m2[i]) {
			return false
		}
	}
	return true
}
func (m Mat2) At(row, col int) float64 {
	return m[col*2+row]
}
func (m *Mat2) Set(row, col int, value float64) {
	m[col*2+row] = value
}
func (m Mat2) Index(row, col int) int {
	return col*2 + row
}
func (m Mat2) Row(row int) Vec2 {
	return Vec2{m[row+0], m[row+2]}
}
func (m Mat2) Rows() (row0, row1 Vec2) {
	return m.Row(0), m.Row(1)
}
func (m Mat2) Col(col int) Vec2 {
	return Vec2{m[col*2+0], m[col*2+1]}
}
func (m Mat2) Cols() (col0, col1 Vec2) {
	return m.Col(0), m.Col(1)
}
func (m Mat2) Trace() float64 {
	return m[0] + m[3]
}
func (m Mat2) Abs() Mat2 {
	return Mat2{Abs(m[0]), Abs(m[1]), Abs(m[2]), Abs(m[3])}
}
func (m Mat2) String() string {
	buf := new(bytes.Buffer)
	w := tabwriter.NewWriter(buf, 4, 4, 1, ' ', tabwriter.AlignRight)
	for i := 0; i < 2; i++ {
		for _, col := range m.Row(i) {
			fmt.Fprintf(w, "%f\t", col)
		}
		fmt.Fprintln(w, "")
	}
	w.Flush()
	return buf.String()
}
func (m *Mat2x3) SetCol(col int, v Vec2) {
	m[col*2+0], m[col*2+1] = v[0], v[1]
}
func (m *Mat2x3) SetRow(row int, v Vec3) {
	m[row+0], m[row+2], m[row+4] = v[0], v[1], v[2]
}
func Mat2x3FromRows(row0, row1 Vec3) Mat2x3 {
	return Mat2x3{row0[0], row1[0], row0[1], row1[1], row0[2], row1[2]}
}
func Mat2x3FromCols(col0, col1, col2 Vec2) Mat2x3 {
	return Mat2x3{col0[0], col0[1], col1[0], col1[1], col2[0], col2[1]}
}
func (m1 Mat2x3) Add(m2 Mat2x3) Mat2x3 {
	return Mat2x3{m1[0] + m2[0], m1[1] + m2[1], m1[2] + m2[2], m1[3] + m2[3], m1[4] + m2[4], m1[5] + m2[5]}
}
func (m1 Mat2x3) Sub(m2 Mat2x3) Mat2x3 {
	return Mat2x3{m1[0] - m2[0], m1[1] - m2[1], m1[2] - m2[2], m1[3] - m2[3], m1[4] - m2[4], m1[5] - m2[5]}
}
func (m1 Mat2x3) Mul(c float64) Mat2x3 {
	return Mat2x3{m1[0] * c, m1[1] * c, m1[2] * c, m1[3] * c, m1[4] * c, m1[5] * c}
}
func (m1 Mat2x3) Mul3x1(m2 Vec3) Vec2 {
	return Vec2{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2],
	}
}
func (m1 Mat2x3) Mul3x2(m2 Mat3x2) Mat2 {
	return Mat2{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2],
		m1[0]*m2[3] + m1[2]*m2[4] + m1[4]*m2[5],
		m1[1]*m2[3] + m1[3]*m2[4] + m1[5]*m2[5],
	}
}
func (m1 Mat2x3) Mul3(m2 Mat3) Mat2x3 {
	return Mat2x3{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2],
		m1[0]*m2[3] + m1[2]*m2[4] + m1[4]*m2[5],
		m1[1]*m2[3] + m1[3]*m2[4] + m1[5]*m2[5],
		m1[0]*m2[6] + m1[2]*m2[7] + m1[4]*m2[8],
		m1[1]*m2[6] + m1[3]*m2[7] + m1[5]*m2[8],
	}
}
func (m1 Mat2x3) Mul3x4(m2 Mat3x4) Mat2x4 {
	return Mat2x4{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2],
		m1[0]*m2[3] + m1[2]*m2[4] + m1[4]*m2[5],
		m1[1]*m2[3] + m1[3]*m2[4] + m1[5]*m2[5],
		m1[0]*m2[6] + m1[2]*m2[7] + m1[4]*m2[8],
		m1[1]*m2[6] + m1[3]*m2[7] + m1[5]*m2[8],
		m1[0]*m2[9] + m1[2]*m2[10] + m1[4]*m2[11],
		m1[1]*m2[9] + m1[3]*m2[10] + m1[5]*m2[11],
	}
}
func (m1 Mat2x3) Transpose() Mat3x2 {
	return Mat3x2{m1[0], m1[2], m1[4], m1[1], m1[3], m1[5]}
}
func (m1 Mat2x3) ApproxEqual(m2 Mat2x3) bool {
	for i := range m1 {
		if !FloatEqual(m1[i], m2[i]) {
			return false
		}
	}
	return true
}
func (m1 Mat2x3) ApproxEqualThreshold(m2 Mat2x3, threshold float64) bool {
	for i := range m1 {
		if !FloatEqualThreshold(m1[i], m2[i], threshold) {
			return false
		}
	}
	return true
}
func (m1 Mat2x3) ApproxFuncEqual(m2 Mat2x3, eq func(float64, float64) bool) bool {
	for i := range m1 {
		if !eq(m1[i], m2[i]) {
			return false
		}
	}
	return true
}
func (m Mat2x3) At(row, col int) float64 {
	return m[col*2+row]
}
func (m *Mat2x3) Set(row, col int, value float64) {
	m[col*2+row] = value
}
func (m Mat2x3) Index(row, col int) int {
	return col*2 + row
}
func (m Mat2x3) Row(row int) Vec3 {
	return Vec3{m[row+0], m[row+2], m[row+4]}
}
func (m Mat2x3) Rows() (row0, row1 Vec3) {
	return m.Row(0), m.Row(1)
}
func (m Mat2x3) Col(col int) Vec2 {
	return Vec2{m[col*2+0], m[col*2+1]}
}
func (m Mat2x3) Cols() (col0, col1, col2 Vec2) {
	return m.Col(0), m.Col(1), m.Col(2)
}
func (m Mat2x3) Abs() Mat2x3 {
	return Mat2x3{Abs(m[0]), Abs(m[1]), Abs(m[2]), Abs(m[3]), Abs(m[4]), Abs(m[5])}
}
func (m Mat2x3) String() string {
	buf := new(bytes.Buffer)
	w := tabwriter.NewWriter(buf, 4, 4, 1, ' ', tabwriter.AlignRight)
	for i := 0; i < 2; i++ {
		for _, col := range m.Row(i) {
			fmt.Fprintf(w, "%f\t", col)
		}
		fmt.Fprintln(w, "")
	}
	w.Flush()
	return buf.String()
}
func (m *Mat2x4) SetCol(col int, v Vec2) {
	m[col*2+0], m[col*2+1] = v[0], v[1]
}
func (m *Mat2x4) SetRow(row int, v Vec4) {
	m[row+0], m[row+2], m[row+4], m[row+6] = v[0], v[1], v[2], v[3]
}
func Mat2x4FromRows(row0, row1 Vec4) Mat2x4 {
	return Mat2x4{row0[0], row1[0], row0[1], row1[1], row0[2], row1[2], row0[3], row1[3]}
}
func Mat2x4FromCols(col0, col1, col2, col3 Vec2) Mat2x4 {
	return Mat2x4{col0[0], col0[1], col1[0], col1[1], col2[0], col2[1], col3[0], col3[1]}
}
func (m1 Mat2x4) Add(m2 Mat2x4) Mat2x4 {
	return Mat2x4{m1[0] + m2[0], m1[1] + m2[1], m1[2] + m2[2], m1[3] + m2[3], m1[4] + m2[4], m1[5] + m2[5], m1[6] + m2[6], m1[7] + m2[7]}
}
func (m1 Mat2x4) Sub(m2 Mat2x4) Mat2x4 {
	return Mat2x4{m1[0] - m2[0], m1[1] - m2[1], m1[2] - m2[2], m1[3] - m2[3], m1[4] - m2[4], m1[5] - m2[5], m1[6] - m2[6], m1[7] - m2[7]}
}
func (m1 Mat2x4) Mul(c float64) Mat2x4 {
	return Mat2x4{m1[0] * c, m1[1] * c, m1[2] * c, m1[3] * c, m1[4] * c, m1[5] * c, m1[6] * c, m1[7] * c}
}
func (m1 Mat2x4) Mul4x1(m2 Vec4) Vec2 {
	return Vec2{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2] + m1[6]*m2[3],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2] + m1[7]*m2[3],
	}
}
func (m1 Mat2x4) Mul4x2(m2 Mat4x2) Mat2 {
	return Mat2{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2] + m1[6]*m2[3],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2] + m1[7]*m2[3],
		m1[0]*m2[4] + m1[2]*m2[5] + m1[4]*m2[6] + m1[6]*m2[7],
		m1[1]*m2[4] + m1[3]*m2[5] + m1[5]*m2[6] + m1[7]*m2[7],
	}
}
func (m1 Mat2x4) Mul4x3(m2 Mat4x3) Mat2x3 {
	return Mat2x3{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2] + m1[6]*m2[3],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2] + m1[7]*m2[3],
		m1[0]*m2[4] + m1[2]*m2[5] + m1[4]*m2[6] + m1[6]*m2[7],
		m1[1]*m2[4] + m1[3]*m2[5] + m1[5]*m2[6] + m1[7]*m2[7],
		m1[0]*m2[8] + m1[2]*m2[9] + m1[4]*m2[10] + m1[6]*m2[11],
		m1[1]*m2[8] + m1[3]*m2[9] + m1[5]*m2[10] + m1[7]*m2[11],
	}
}
func (m1 Mat2x4) Mul4(m2 Mat4) Mat2x4 {
	return Mat2x4{
		m1[0]*m2[0] + m1[2]*m2[1] + m1[4]*m2[2] + m1[6]*m2[3],
		m1[1]*m2[0] + m1[3]*m2[1] + m1[5]*m2[2] + m1[7]*m2[3],
		m1[0]*m2[4] + m1[2]*m2[5] + m1[4]*m2[6] + m1[6]*m2[7],
		m1[1]*m2[4] + m1[3]*m2[5] + m1[5]*m2[6] + m1[7]*m2[7],
		m1[0]*m2[8] + m1[2]*m2[9] + m1[4]*m2[10] + m1[6]*m2[11],
		m1[1]*m2[8] + m1[3]*m2[9] + m1[5]*m2[10] + m1[7]*m2[11],
		m1[0]*m2[12] + m1[2]*m2[13] + m1[4]*m2[14] + m1[6]*m2[15],
		m1[1]*m2[12] + m1[3]*m2[13] + m1[5]*m2[14] + m1[7]*m2[15],
	}
}
func (m1 Mat2x4) Transpose() Mat4x2 {
	return Mat4x2{m1[0], m1[2], m1[4], m1[6], m1[1], m1[3], m1[5], m1[7]}
}
func (m1 Mat2x4) ApproxEqual(m2 Mat2x4) bool {
	for i := range m1 {
		if !FloatEqual(m1[i], m2[i]) {
			return false
		}
	}
	return true
}
func (m1 Mat2x4) ApproxEqualThreshold(m2 Mat2x4, threshold float64) bool {
	for i := range m1 {
		if !FloatEqualThreshold(m1[i], m2[i], threshold) {
			return false
		}
	}
	return true
}
func (m1 Mat2x4) ApproxFuncEqual(m2 Mat2x4, eq func(float64, float64) bool) bool {
	for i := range m1 {
		if !eq(m1[i], m2[i]) {
			return false
		}
	}
	return true
}
func (m Mat2x4) At(row, col int) float64 {
	return m[col*2+row]
}
func (m *Mat2x4) Set(row, col int, value float64) {
	m[col*2+row] = value
}
func (m Mat2x4) Index(row, col int) int {
	return col*2 + row
}
func (m Mat2x4) Row(row int) Vec4 {
	return Vec4{m[row+0], m[row+2], m[row+4], m[row+6]}
}
func (m Mat2x4) Rows() (row0, row1 Vec4) {
	return m.Row(0), m.Row(1)
}
func (m Mat2x4) Col(col int) Vec2 {
	return Vec2{m[col*2+0], m[col*2+1]}
}
func (m Mat2x4) Cols() (col0, col1, col2, col3 Vec2) {
	return m.Col(0), m.Col(1), m.Col(2), m.Col(3)
}
func (m Mat2x4) Abs() Mat2x4 {
	return Mat2x4{Abs(m[0]), Abs(m[1]), Abs(m[2]), Abs(m[3]), Abs(m[4]), Abs(m[5]), Abs(m[6]), Abs(m[7])}
}
func (m Mat2x4) String() string {
	buf := new(bytes.Buffer)
	w := tabwriter.NewWriter(buf, 4, 4, 1, ' ', tabwriter.AlignRight)
	for i := 0; i < 2; i++ {
		for _, col := range m.Row(i) {
			fmt.Fprintf(w, "%f\t", col)
		}
		fmt.Fprintln(w, "")
	}
	w.Flush()
	return buf.String()
}
func (m *Mat3x2) SetCol(col int, v Vec3) {
	m[col*3+0], m[col*3+1], m[col*3+2] = v[0], v[1], v[2]
}
func (m *Mat3x2) SetRow(row int, v Vec2) {
	m[row+0], m[row+3] = v[0], v[1]
}
func Mat3x2FromRows(row0, row1, row2 Vec2) Mat3x2 {
	return Mat3x2{row0[0], row1[0], row2[0], row0[1], row1[1], row2[1]}
}
func Mat3x2FromCols(col0, col1 Vec3) Mat3x2 {
	return Mat3x2{col0[0], col0[1], col0[2], col1[0], col1[1], col1[2]}
}
func (m1 Mat3x2) Add(m2 Mat3x2) Mat3x2 {
	return Mat3x2{m1[0] + m2[0], m1[1] + m2[1], m1[2] + m2[2], m1[3] + m2[3], m1[4] + m2[4], m1[5] + m2[5]}
}
func (m1 Mat3x2) Sub(m2 Mat3x2) Mat3x2 {
	return Mat3x2{m1[0] - m2[0], m1[1] - m2[1], m1[2] - m2[2], m1[3] - m2[3], m1[4] - m2[4], m1[5] - m2[5]}
}
func (m1 Mat3x2) Mul(c float64) Mat3x2 {
	return Mat3x2{m1[0] * c, m1[1] * c, m1[2] * c, m1[3] * c, m1[4] * c, m1[5] * c}
}
func (m1 Mat3x2) Mul2x1(m2 Vec2) Vec3 {
	return Vec3{
		m1[0]*m2[0] + m1[3]*m2[1],
		m1[1]*m2[0] + m1[4]*m2[1],
		m1[2]*m2[0] + m1[5]*m2[1],
	}
}
func (m1 Mat3x2) Mul2(m2 Mat2) Mat3x2 {
	return Mat3x2{
		m1[0]*m2[0] + m1[3]*m2[1],
		m1[1]*m2[0] + m1[4]*m2[1],
		m1[2]*m2[0] + m1[5]*m2[1],
		m1[0]*m2[2] + m1[3]*m2[3],
		m1[1]*m2[2] + m1[4]*m2[3],
		m1[2]*m2[2] + m1[5]*m2[3],
	}
}
func (m1 Mat3x2) Mul2x3(m2 Mat2x3) Mat3 {
	return Mat3{
		m1[0]*m2[0] + m1[3]*m2[1],
		m1[1]*m2[0] + m1[4]*m2[1],
		m1[2]*m2[0] + m1[5]*m2[1],
		m1[0]*m2[2] + m1[3]*m2[3],
		m1[1]*m2[2] + m1[4]*m2[3],
		m1[2]*m2[2] + m1[5]*m2[3],
		m1[0]*m2[4] + m1[3]*m2[5],
		m1[1]*m2[4] + m1[4]*m2[5],
		m1[2]*m2[4] + m1[5]*m2[5],
	}
}
func (m1 Mat3x2) Mul2x4(m2 Mat2x4) Mat3x4 {
	return Mat3x4{
		m1[0]*m2[0] + m1[3]*m2[1],
		m1[1]*m2[0] + m1[4]*m2[1],
		m1[2]*m2[0] + m1[5]*m2[1],
		m1[0]*m2[2] + m1[3]*m2[3],
		m1[1]*m2[2] + m1[4]*m2[3],
		m1[2]*m2[2] + m1[5]*m2[3],
		m1[0]*m2[4] + m1[3]*m2[5],
		m1[1]*m2[4] + m1[4]*m2[5],
		m1[2]*m2[4] + m1[5]*m2[5],
		m1[0]*m2[6] + m1[3]*m2[7],
		m1[1]*m2[6] + m1[4]*m2[7],
		m1[2]*m2[6] + m1[5]*m2[7],
	}
}
func (m1 Mat3x2) Transpose() Mat2x3 {
	return Mat2x3{m1[0], m1[3], m1[1], m1[4], m1[2], m1[5]}
}
func (m1 Mat3x2) ApproxEqual(m2 Mat3x2) bool {
	for i := range m1 {
		if !FloatEqual(m1[i], m2[i]) {
			return false
		}
	}
	return true
}