forked from templexxx/reedsolomon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatrix.go
147 lines (134 loc) · 3.13 KB
/
matrix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package reedsolomon
import (
"errors"
)
type matrix [][]byte // byte[row][col]
func NewMatrix(rows, cols int) matrix {
m := matrix(make([][]byte, rows))
for i := range m {
m[i] = make([]byte, cols)
}
return m
}
// return identity matrix(upper) cauchy matrix(lower)
func genEncodeMatrix(rows, cols int) matrix {
m := NewMatrix(rows, cols)
// identity matrix
for j := 0; j < cols; j++ {
m[j][j] = byte(1)
}
// cauchy matrix
for i := cols; i < rows; i++ {
for j := 0; j < cols; j++ {
d := i ^ j
a := inverseTable[d]
m[i][j] = byte(a)
}
}
return m
}
func (m matrix) invert() (matrix, error) {
size := len(m)
iM := identityMatrix(size)
mIM, _ := m.augIM(iM)
err := mIM.gaussJordan()
if err != nil {
return nil, err
}
return mIM.subMatrix(size), nil
}
// IN -> (IN|I)
func (m matrix) augIM(iM matrix) (matrix, error) {
result := NewMatrix(len(m), len(m[0])+len(iM[0]))
for r, row := range m {
for c := range row {
result[r][c] = m[r][c]
}
cols := len(m[0])
for c := range iM[0] {
result[r][cols+c] = iM[r][c]
}
}
return result, nil
}
var ErrSingular = errors.New("reedsolomon: matrix is singular")
// (IN|I) -> (I|OUT)
func (m matrix) gaussJordan() error {
rows := len(m)
columns := len(m[0])
// Clear out the part below the main diagonal and scale the main
// diagonal to be 1.
for r := 0; r < rows; r++ {
// If the element on the diagonal is 0, find a row below
// that has a non-zero and swap them.
if m[r][r] == 0 {
for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
if m[rowBelow][r] != 0 {
m.swapRows(r, rowBelow)
break
}
}
}
// After swap, if we find all elements in this column is 0, it means the matrix's det is 0
if m[r][r] == 0 {
return ErrSingular
}
// Scale to 1.
if m[r][r] != 1 {
d := m[r][r]
scale := inverseTable[d]
// every element(this column) * m[r][r]'s inverse
for c := 0; c < columns; c++ {
m[r][c] = gfMul(m[r][c], scale)
}
}
//Make everything below the 1 be a 0 by subtracting a multiple of it
for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
if m[rowBelow][r] != 0 {
// scale * m[r][r] = scale, scale + scale = 0
// makes m[r][r+1] = 0 , then calc left elements
scale := m[rowBelow][r]
for c := 0; c < columns; c++ {
m[rowBelow][c] ^= gfMul(scale, m[r][c])
}
}
}
}
// Now clear the part above the main diagonal.
// same logic with clean upper
for d := 0; d < rows; d++ {
for rowAbove := 0; rowAbove < d; rowAbove++ {
if m[rowAbove][d] != 0 {
scale := m[rowAbove][d]
for c := 0; c < columns; c++ {
m[rowAbove][c] ^= gfMul(scale, m[d][c])
}
}
}
}
return nil
}
func identityMatrix(n int) matrix {
m := NewMatrix(n, n)
for i := 0; i < n; i++ {
m[i][i] = byte(1)
}
return m
}
// (I|OUT) -> OUT
func (m matrix) subMatrix(size int) matrix {
result := NewMatrix(size, size)
for r := 0; r < size; r++ {
for c := size; c < size*2; c++ {
result[r][c-size] = m[r][c]
}
}
return result
}
// SwapRows Exchanges two rows in the matrix.
func (m matrix) swapRows(r1, r2 int) {
m[r2], m[r1] = m[r1], m[r2]
}
func gfMul(a, b byte) byte {
return mulTable[a][b]
}