gf.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package fec
  2. // Arithmetic over GF(2^8) with the reducing polynomial x^8+x^4+x^3+x^2+1 (0x11d).
  3. // Addition is XOR. Multiplication uses log/exp tables; the exp table is doubled so
  4. // the sum of two logs never needs reducing mod 255.
  5. const gfPoly = 0x11d
  6. var (
  7. gfExp [510]byte
  8. gfLog [256]byte
  9. )
  10. func init() {
  11. x := 1
  12. for i := range 255 {
  13. gfExp[i] = byte(x)
  14. gfExp[i+255] = byte(x)
  15. gfLog[x] = byte(i)
  16. x <<= 1
  17. if x&0x100 != 0 {
  18. x ^= gfPoly
  19. }
  20. }
  21. }
  22. func gfMul(a, b byte) byte {
  23. if a == 0 || b == 0 {
  24. return 0
  25. }
  26. return gfExp[int(gfLog[a])+int(gfLog[b])]
  27. }
  28. // gfInv returns the multiplicative inverse of a. a must be non-zero.
  29. func gfInv(a byte) byte {
  30. return gfExp[255-int(gfLog[a])]
  31. }
  32. // mulAdd computes dst ^= c*src element-wise. dst must be at least as long as src.
  33. func mulAdd(dst, src []byte, c byte) {
  34. switch c {
  35. case 0:
  36. return
  37. case 1:
  38. for i, s := range src {
  39. dst[i] ^= s
  40. }
  41. return
  42. }
  43. var t [256]byte
  44. lc := int(gfLog[c])
  45. for s := 1; s < 256; s++ {
  46. t[s] = gfExp[int(gfLog[s])+lc]
  47. }
  48. for i, s := range src {
  49. dst[i] ^= t[s]
  50. }
  51. }
  52. // coefficient returns the generator matrix entry for repair symbol index row and
  53. // data symbol index col. Repair rows are a Cauchy matrix 1/(x_row + y_col) with
  54. // x_row = row and y_col = col: rows are always >= k and columns < k so the two
  55. // sets are disjoint, which makes every square submatrix invertible and the
  56. // systematic code [I; C] MDS. A row depends only on its index, never on how many
  57. // repair symbols exist, so new repair symbols can be generated at any time.
  58. func coefficient(row, col int) byte {
  59. return gfInv(byte(row) ^ byte(col))
  60. }
  61. // invert inverts the n x n matrix m in place with Gauss-Jordan elimination.
  62. // It returns false if m is singular.
  63. func invert(m [][]byte) bool {
  64. n := len(m)
  65. inv := make([][]byte, n)
  66. for i := range inv {
  67. inv[i] = make([]byte, n)
  68. inv[i][i] = 1
  69. }
  70. for col := range n {
  71. pivot := -1
  72. for r := col; r < n; r++ {
  73. if m[r][col] != 0 {
  74. pivot = r
  75. break
  76. }
  77. }
  78. if pivot < 0 {
  79. return false
  80. }
  81. m[col], m[pivot] = m[pivot], m[col]
  82. inv[col], inv[pivot] = inv[pivot], inv[col]
  83. scale := gfInv(m[col][col])
  84. for c := range n {
  85. m[col][c] = gfMul(m[col][c], scale)
  86. inv[col][c] = gfMul(inv[col][c], scale)
  87. }
  88. for r := 0; r < n; r++ {
  89. if r == col || m[r][col] == 0 {
  90. continue
  91. }
  92. f := m[r][col]
  93. for c := range n {
  94. m[r][c] ^= gfMul(f, m[col][c])
  95. inv[r][c] ^= gfMul(f, inv[col][c])
  96. }
  97. }
  98. }
  99. copy(m, inv)
  100. return true
  101. }