-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathmatrix.go
159 lines (140 loc) · 3.88 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
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (c) 2017 Temple3x ([email protected])
//
// Use of this source code is governed by the MIT License
// that can be found in the LICENSE file.
package reedsolomon
import (
"errors"
)
// matrix: row*column bytes,
// I use one slice but not 2D slice,
// because type matrix is only as encoding/generator matrix's
// container, hundreds bytes at most,
// so it maybe more cache-friendly and GC-friendly.
type matrix []byte
// makeSurvivedMatrix makes an encoding matrix.
// High portion: Identity Matrix;
// Lower portion: Cauchy Matrix.
// The Encoding Matrix is as same as Intel ISA-L's gf_gen_cauchy1_matrix:
// https://github.com/intel/isa-l/blob/master/erasure_code/ec_base.c
//
// Warn:
// It maybe not the common way to make an encoding matrix,
// so it may corrupt when mix this lib with other erasure codes libs.
//
// The common way to make an encoding matrix is using a
// Vandermonde Matrix, then use elementary transformation
// to make an identity matrix in the high portion of the matrix.
// But it's a little complicated.
//
// And there is a wrong way to use Vandermonde Matrix
// (see Intel ISA-L, and this lib's document warn the issue),
// in the wrong way, they use an identity matrix in the high portion,
// and a Vandermonde matrix in the lower directly,
// and this encoding matrix's sub-matrix maybe singular.
// You can find a proof in invertible.jpeg.
func makeEncodeMatrix(d, p int) matrix {
r := d + p
m := make([]byte, r*d)
// Create identity matrix upper.
for i := 0; i < d; i++ {
m[i*d+i] = 1
}
// Create cauchy matrix below. (1/(i + j), 0 <= j < d, d <= i < 2*d)
off := d * d // Skip the identity matrix.
for i := d; i < r; i++ {
for j := 0; j < d; j++ {
m[off] = inverseTbl[i^j]
off++
}
}
return m
}
func (m matrix) makeReconstMatrix(survived, needReconst []int) (rm matrix, err error) {
d, nn := len(survived), len(needReconst)
rm = make([]byte, nn*d)
for i, l := range needReconst {
copy(rm[i*d:i*d+d], m[l*d:l*d+d])
}
return
}
// makeEncMatrixForReconst makes an encoding matrix by calculating
// the inverse matrix of survived encoding matrix.
func (m matrix) makeEncMatrixForReconst(survived []int) (em matrix, err error) {
d := len(survived)
m2 := make([]byte, d*d)
for i, l := range survived {
copy(m2[i*d:i*d+d], m[l*d:l*d+d])
}
em, err = matrix(m2).invert(len(survived))
if err != nil {
return
}
return
}
var ErrNotSquare = errors.New("not a square matrix")
var ErrSingularMatrix = errors.New("matrix is singular")
// invert calculates m's inverse matrix,
// and return it or any error.
func (m matrix) invert(n int) (inv matrix, err error) {
if n*n != len(m) {
err = ErrNotSquare
return
}
mm := make([]byte, 2*n*n)
left := mm[:n*n]
copy(left, m) // Copy m, avoiding side effect.
// Make an identity matrix.
inv = mm[n*n:]
for i := 0; i < n; i++ {
inv[i*n+i] = 1
}
for i := 0; i < n; i++ {
// Pivoting.
if left[i*n+i] == 0 {
// Find a row with non-zero in current column and swap.
// If there is no one, means it's a singular matrix.
var j int
for j = i + 1; j < n; j++ {
if left[j*n+i] != 0 {
break
}
}
if j == n {
return nil, ErrSingularMatrix
}
matrix(left).swap(i, j, n)
inv.swap(i, j, n)
}
if left[i*n+i] != 1 {
v := inverseTbl[left[i*n+i]] // 1/pivot
// Scale row.
for j := 0; j < n; j++ {
left[i*n+j] = gfMul(left[i*n+j], v)
inv[i*n+j] = gfMul(inv[i*n+j], v)
}
}
// Use elementary transformation to
// make all elements(except pivot) in the left matrix
// become 0.
for j := 0; j < n; j++ {
if j == i {
continue
}
v := left[j*n+i]
if v != 0 {
for k := 0; k < n; k++ {
left[j*n+k] ^= gfMul(v, left[i*n+k])
inv[j*n+k] ^= gfMul(v, inv[i*n+k])
}
}
}
}
return
}
// swap square matrix row[i] & row[j], col = n
func (m matrix) swap(i, j, n int) {
for k := 0; k < n; k++ {
m[i*n+k], m[j*n+k] = m[j*n+k], m[i*n+k]
}
}