func (m Matrix) FilterRows(f func(i int) bool) Matrix {
	rows, cols := 0, m.Cols()
	var data []float64
	for i := 0; i < m.rows; i++ {
		if f(i) {
			data = append(data, m.data[i*cols:(i+1)*m.cols]...)
			rows++
		}
	}
	return Matrix{rows, cols, data}
}
FilterRows returns a matrix where only the rows with an index for which f
returns true have been kept.
package mat
import (
	"bytes"
	"fmt"
	"sync"
	"gonum.org/v1/gonum/blas"
	"gonum.org/v1/gonum/blas/blas64"
)
type Matrix struct {
	rows, cols int
	data       []float64
}
func New(rows, cols int) Matrix {
	return Matrix{rows, cols, make([]float64, rows*cols)}
}
func FromSlice(rows, cols int, data []float64) Matrix {
	if len(data) != rows*cols {
		panicf("missmatched dimensions and data: %d x %d != %d", rows, cols, len(data))
	}
	c := make([]float64, len(data))
	copy(c, data)
	return Matrix{rows, cols, c}
}
func (m Matrix) ToSlice() []float64 { return m.data }
func FromFunc(rows, cols int, f func(i, j int) float64) Matrix {
	m := Matrix{rows, cols, make([]float64, rows*cols)}
	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			m.set(i, j, f(i, j))
		}
	}
	return m
}
func (m Matrix) String() string {
	buf := new(bytes.Buffer)
	for i := 0; i < m.rows; i++ {
		for j := 0; j < m.cols; j++ {
			fmt.Fprintf(buf, "%10.2f ", m.at(i, j))
		}
		fmt.Fprintln(buf)
	}
	return buf.String()
}
func (m Matrix) Rows() int { return m.rows }
func (m Matrix) Cols() int { return m.cols }
func (m Matrix) checkPos(i, j int) {
	if i < 0 || i >= m.rows || j < 0 || j >= m.cols {
		panic(fmt.Sprintf("element (%d, %d) is out of range", i, j))
	}
}
func (m Matrix) At(i, j int) float64 {
	m.checkPos(i, j)
	return m.at(i, j)
}
func (m Matrix) at(i, j int) float64 { return m.data[m.cols*i+j] }
func (m Matrix) Clone() Matrix {
	c := New(m.rows, m.cols)
	copy(c.data, m.data)
	return c
}
func (m Matrix) Apply(f func(i, j int) float64) {
	for i := 0; i < m.rows; i++ {
		for j := 0; j < m.cols; j++ {
			m.set(i, j, f(i, j))
		}
	}
}
func (m Matrix) Set(i, j int, x float64) Matrix {
	m.checkPos(i, j)
	r := m.Clone()
	r.set(i, j, x)
	return r
}
func (m *Matrix) set(i, j int, x float64) { m.data[m.cols*i+j] = x }
func (m Matrix) Scale(x float64) Matrix {
	return Map(func(v float64) float64 { return x * v }, m)
}
func (m Matrix) AddScalar(x float64) Matrix {
	return Map(func(v float64) float64 { return x + v }, m)
}
func Map(f func(float64) float64, m Matrix) Matrix {
	r := m.Clone()
	for i, v := range r.data {
		r.data[i] = f(v)
	}
	return r
}
func ConcatenateCols(ms ...Matrix) Matrix {
	if len(ms) == 0 {
		panic("can't concatenate an empty list of matrices")
	}
	rows, cols := ms[0].Rows(), 0
	for _, m := range ms {
		if rows != m.Rows() {
			panic("can't concatenate columns on matrices with different number of rows")
		}
		cols += m.Cols()
	}
	data := make([]float64, 0, rows*cols)
	for i := 0; i < rows; i++ {
		for _, m := range ms {
			data = append(data, m.data[i*m.cols:(i+1)*m.cols]...)
		}
	}
	return Matrix{rows, cols, data}
}
func ConcatenateRows(ms ...Matrix) Matrix {
	if len(ms) == 0 {
		panic("can't concatenate an empty list of matrices")
	}
	rows, cols := 0, ms[0].Cols()
	for _, m := range ms {
		if cols != m.Cols() {
			panic("can't concatenate rows on matrices with different number of cols")
		}
		rows += m.Rows()
	}
	data := make([]float64, 0, rows*cols)
	for _, m := range ms {
		data = append(data, m.data...)
	}
	return Matrix{rows, cols, data}
}
func (m Matrix) SliceCols(from, to int) Matrix {
	if from < 0 || to > m.cols || to < from {
		panic("bad row numbers")
	}
	data := make([]float64, 0, m.rows*(to-from))
	for i := 0; i < m.rows; i++ {
		data = append(data, m.data[i*m.cols+from:i*m.cols+to]...)
	}
	return Matrix{m.rows, to - from, data}
}
func (m Matrix) SliceRows(from, to int) Matrix {
	if from < 0 || to > m.rows || to < from {
		panic("bad row numbers")
	}
	data := make([]float64, m.cols*(to-from))
	copy(data, m.data[from*m.cols:to*m.cols])
	return Matrix{to - from, m.cols, data}
}
func (m Matrix) Sum() float64 {
	return m.Reduce(0, func(x, cum float64) float64 { return x + cum })
}
func Sum(m Matrix) float64 { return m.Sum() }
func (m Matrix) Reduce(zero float64, f func(x, cum float64) float64) float64 {
	cum := zero
	for _, x := range m.data {
		cum = f(x, cum)
	}
	return cum
}