func (c Coder) ReconstructData(data, parity [][]byte) error {
	var availableRows, missingRows []int
	var input [][]byte
	for i, dataShard := range data {
		if dataShard != nil {
			availableRows = append(availableRows, i)
			input = append(input, dataShard)
		} else {
			missingRows = append(missingRows, i)
		}
	}
	if len(missingRows) == 0 {
		return nil
	}
	var usedParityRows []int
	for i := 0; i < len(parity) && len(input) < c.dataShards; i++ {
		if parity[i] != nil {
			usedParityRows = append(usedParityRows, i)
			input = append(input, parity[i])
		}
	}
	if len(input) < c.dataShards {
		return NotEnoughParityShardsError{}
	}
	reconstructionMatrix, err := makeReconstructionMatrix(c.dataShards, availableRows, missingRows, usedParityRows, c.parityMatrix)
	if err != nil {
		return err
	}
	reconstructedData := make([][]byte, len(missingRows))
	for i := range reconstructedData {
		reconstructedData[i] = make([]byte, len(input[0]))
	}
	c.applyMatrix(reconstructionMatrix, input, reconstructedData)
	for i, r := range missingRows {
		data[r] = reconstructedData[i]
	}
	return nil
}
ReconstructData takes a list of data shards and parity shards, some
of which may be nil, and tries to reconstruct the missing data
shards. If successful, the nil rows of data are filled in and a nil
error is returned. Otherwise, an error is returned. In particular,
if there are missing data shards but there aren't enough parity
shards to reconstruct them, NotEnoughParityShardsError is returned.
package rsec16
import (
	"errors"
	"math"
	"runtime"
	"github.com/akalin/gopar/gf2p16"
	"github.com/klauspost/cpuid/v2"
)
func DefaultNumGoroutines() int {
	numGoroutines := runtime.GOMAXPROCS(0)
	physicalCores := cpuid.CPU.PhysicalCores
	if physicalCores < numGoroutines {
		numGoroutines = physicalCores
	}
	return numGoroutines
}
type Coder struct {
	dataShards, parityShards int
	numGoroutines            int
	parityMatrix             gf2p16.Matrix
}
func newCauchyParityMatrix(dataShards, parityShards int) gf2p16.Matrix {
	return newCauchyMatrix(parityShards, dataShards, func(i int) gf2p16.T {
		return gf2p16.T(dataShards + i)
	}, func(i int) gf2p16.T {
		return gf2p16.T(i)
	})
}
func NewCoderCauchy(dataShards, parityShards, numGoroutines int) (Coder, error) {
	if dataShards <= 0 {
		panic("invalid data shard count")
	}
	if parityShards <= 0 {
		panic("invalid parity shard count")
	}
	if numGoroutines <= 0 {
		panic("invalid goroutine count")
	}
	if dataShards+parityShards > math.MaxUint16 {
		return Coder{}, errors.New("too many shards")
	}
	parityMatrix := newCauchyParityMatrix(dataShards, parityShards)
	return Coder{dataShards, parityShards, numGoroutines, parityMatrix}, nil
}
var generators []gf2p16.T
func init() {
	for i := 0; i < (1 << 16); i++ {
		if i%3 == 0 || i%5 == 0 || i%17 == 0 || i%257 == 0 {
			continue
		}
		g := gf2p16.T(2).Pow(uint32(i))
		generators = append(generators, g)
	}
}
func newVandermondeParityMatrix(dataShards, parityShards int) gf2p16.Matrix {
	return newVandermondeMatrix(parityShards, dataShards, func(i int) gf2p16.T {
		return generators[i]
	})
}
func NewCoderPAR2Vandermonde(dataShards, parityShards, numGoroutines int) (Coder, error) {
	if dataShards <= 0 {
		panic("invalid data shard count")
	}
	if parityShards <= 0 {
		panic("invalid parity shard count")
	}
	if numGoroutines <= 0 {
		panic("invalid goroutine count")
	}
	if dataShards > len(generators) {
		return Coder{}, errors.New("too many data shards")
	}
	if parityShards > (1<<16)-1 {
		return Coder{}, errors.New("too many parity shards")
	}
	parityMatrix := newVandermondeParityMatrix(dataShards, parityShards)
	return Coder{dataShards, parityShards, numGoroutines, parityMatrix}, nil
}
func (c Coder) applyMatrix(m gf2p16.Matrix, in, out [][]byte) {
	applyMatrixParallelData(m, in, out, c.numGoroutines)
}
func (c Coder) GenerateParity(data [][]byte) [][]byte {
	parity := make([][]byte, c.parityShards)
	for i := range parity {
		parity[i] = make([]byte, len(data[0]))
	}
	c.applyMatrix(c.parityMatrix, data, parity)
	return parity
}
func makeReconstructionMatrix(dataShards int, availableRows, missingRows, usedParityRows []int, parityMatrix gf2p16.Matrix) (gf2p16.Matrix, error) {
	m := gf2p16.NewMatrixFromFunction(len(usedParityRows), len(usedParityRows), func(i, j int) gf2p16.T {
		k := usedParityRows[i]
		l := missingRows[j]
		return parityMatrix.At(k, l)
	})
	n := gf2p16.NewMatrixFromFunction(len(usedParityRows), dataShards, func(i, j int) gf2p16.T {
		if j < len(availableRows) {
			k := usedParityRows[i]
			l := availableRows[j]
			return parityMatrix.At(k, l)
		}
		if i == j-len(availableRows) {
			return 1
		}
		return 0
	})
	return m.RowReduceForInverse(n)
}
type NotEnoughParityShardsError struct{}
func (NotEnoughParityShardsError) Error() string {
	return "not enough parity shards"
}