├── .travis.yml ├── LICENSE ├── README.md ├── addmul_amd64.go ├── addmul_amd64.s ├── addmul_noasm.go ├── addmul_tables_amd64.go ├── addmul_test.go ├── berlekamp_welch.go ├── berlekamp_welch_test.go ├── common.go ├── common_test.go ├── example_test.go ├── fec.go ├── fec_test.go ├── gf_alg.go ├── gf_alg_test.go ├── go.mod ├── go.sum ├── math.go └── tables.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.9.x 5 | - 1.10.x 6 | - tip 7 | 8 | matrix: 9 | allow_failures: 10 | - go: tip 11 | fast_finish: true 12 | 13 | script: 14 | - go build ./... 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ///////////////// 2 | // Copyright notices 3 | ///////////////// 4 | 5 | Copyright (C) 2016-2017 Vivint, Inc. 6 | Copyright (c) 2015 Klaus Post 7 | Copyright (c) 2015 Backblaze 8 | Copyright (C) 2011 Billy Brumley (billy.brumley@aalto.fi) 9 | Copyright (C) 2009-2010 Jack Lloyd (lloyd@randombit.net) 10 | Copyright (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it) 11 | 12 | Portions derived from code by Phil Karn (karn@ka9q.ampr.org), 13 | Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari 14 | Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 15 | 16 | ///////////////// 17 | // Portions of this project (labeled in each file) are licensed under this 18 | // license: 19 | ///////////////// 20 | 21 | Redistribution and use in source and binary forms, with or without 22 | modification, are permitted provided that the following conditions are 23 | met: 24 | 25 | 1. Redistributions of source code must retain the above copyright 26 | notice, this list of conditions and the following disclaimer. 27 | 28 | 2. Redistributions in binary form must reproduce the above copyright 29 | notice, this list of conditions and the following disclaimer in the 30 | documentation and/or other materials provided with the 31 | distribution. 32 | 33 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 34 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 35 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 36 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, 37 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 38 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 39 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 41 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 42 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 43 | POSSIBILITY OF SUCH DAMAGE. 44 | 45 | ///////////////// 46 | // All other portions of this project are licensed under this license: 47 | ///////////////// 48 | 49 | The MIT License (MIT) 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in all 59 | copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 67 | SOFTWARE. 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # infectious 2 | 3 | [![GoDoc](https://godoc.org/github.com/vivint/infectious?status.png)](https://godoc.org/github.com/vivint/infectious) 4 | 5 | Infectious implements 6 | [Reed-Solomon forward error correction](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction). 7 | It uses the 8 | [Berlekamp-Welch error correction algorithm](https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Welch_algorithm) 9 | to achieve the ability to actually correct errors. 10 | 11 | [We wrote a blog post about how this library works!](https://innovation.vivint.com/introduction-to-reed-solomon-bc264d0794f8) 12 | 13 | ### Example 14 | 15 | ```golang 16 | const ( 17 | required = 8 18 | total = 14 19 | ) 20 | 21 | // Create a *FEC, which will require required pieces for reconstruction at 22 | // minimum, and generate total total pieces. 23 | f, err := infectious.NewFEC(required, total) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | // Prepare to receive the shares of encoded data. 29 | shares := make([]infectious.Share, total) 30 | output := func(s infectious.Share) { 31 | // the memory in s gets reused, so we need to make a deep copy 32 | shares[s.Number] = s.DeepCopy() 33 | } 34 | 35 | // the data to encode must be padded to a multiple of required, hence the 36 | // underscores. 37 | err = f.Encode([]byte("hello, world! __"), output) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | // we now have total shares. 43 | for _, share := range shares { 44 | fmt.Printf("%d: %#v\n", share.Number, string(share.Data)) 45 | } 46 | 47 | // Let's reconstitute with two pieces missing and one piece corrupted. 48 | shares = shares[2:] // drop the first two pieces 49 | shares[2].Data[1] = '!' // mutate some data 50 | 51 | result, err := f.Decode(nil, shares) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | // we have the original data! 57 | fmt.Printf("got: %#v\n", string(result)) 58 | ``` 59 | 60 | **Caution:** this package API leans toward providing the user more power and 61 | performance at the expense of having some really sharp edges! Read the 62 | documentation about memory lifecycles carefully! 63 | 64 | Please see the docs at http://godoc.org/github.com/vivint/infectious 65 | 66 | ### Thanks 67 | 68 | We're forever indebted to the giants on whose shoulders we stand. The LICENSE 69 | has our full copyright history, but an extra special thanks to Klaus Post for 70 | much of the initial Go code. See his post for more: 71 | http://blog.klauspost.com/blazingly-fast-reed-solomon-coding/ 72 | 73 | ### LICENSE 74 | 75 | * Copyright (C) 2016-2017 Vivint, Inc. 76 | * Copyright (c) 2015 Klaus Post 77 | * Copyright (c) 2015 Backblaze 78 | * Copyright (C) 2011 Billy Brumley (billy.brumley@aalto.fi) 79 | * Copyright (C) 2009-2010 Jack Lloyd (lloyd@randombit.net) 80 | * Copyright (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it) 81 | 82 | Portions derived from code by Phil Karn (karn@ka9q.ampr.org), 83 | Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari 84 | Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 85 | 86 | **Portions of this project (labeled in each file) are licensed under this 87 | license:** 88 | 89 | Redistribution and use in source and binary forms, with or without 90 | modification, are permitted provided that the following conditions are 91 | met: 92 | 93 | 1. Redistributions of source code must retain the above copyright 94 | notice, this list of conditions and the following disclaimer. 95 | 96 | 2. Redistributions in binary form must reproduce the above copyright 97 | notice, this list of conditions and the following disclaimer in the 98 | documentation and/or other materials provided with the 99 | distribution. 100 | 101 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 102 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 103 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 104 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, 105 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 106 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 107 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 108 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 109 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 110 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 111 | POSSIBILITY OF SUCH DAMAGE. 112 | 113 | **All other portions of this project are licensed under this license:** 114 | 115 | The MIT License (MIT) 116 | 117 | Permission is hereby granted, free of charge, to any person obtaining a copy 118 | of this software and associated documentation files (the "Software"), to deal 119 | in the Software without restriction, including without limitation the rights 120 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 121 | copies of the Software, and to permit persons to whom the Software is 122 | furnished to do so, subject to the following conditions: 123 | 124 | The above copyright notice and this permission notice shall be included in all 125 | copies or substantial portions of the Software. 126 | 127 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 128 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 129 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 130 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 131 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 132 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 133 | SOFTWARE. 134 | -------------------------------------------------------------------------------- /addmul_amd64.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // Copyright (c) 2015 Klaus Post 5 | // Copyright (c) 2015 Backblaze 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | package infectious 26 | 27 | //go:noescape 28 | func addmulSSSE3(lowhigh *pair, in, out *byte, n int, mul *byte) 29 | 30 | //go:noescape 31 | func addmulAVX2(lowhigh *pair, in, out *byte, n int) 32 | 33 | func addmul(z, x []byte, y byte) { 34 | if len(z) == 0 { 35 | return 36 | } 37 | 38 | var done int 39 | if hasAVX2 { 40 | addmulAVX2(&mul_table_pair[y], &x[0], &z[0], len(z)) 41 | done = (len(x) >> 5) << 5 42 | } else if hasSSSE3 { 43 | addmulSSSE3(&mul_table_pair[y], &x[0], &z[0], len(z), &gf_mul_table[y][0]) 44 | //done = (len(x) >> 4) << 4 45 | return 46 | } 47 | 48 | if done < len(z) { 49 | // hints to the compiler to remove bounds checks 50 | z = z[done:] 51 | x = x[done : done+len(z)] 52 | 53 | gf_mul_y := gf_mul_table[y][:] 54 | for i := range z { 55 | z[i] ^= gf_mul_y[x[i]] 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /addmul_amd64.s: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // Copyright (c) 2015 Klaus Post 5 | // Copyright (c) 2015 Backblaze 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | /* 26 | The corresponding C implementations: 27 | 28 | void addmul( 29 | uint8_t * restrict lowhigh, 30 | uint8_t * restrict in, 31 | uint8_t * restrict out, 32 | int n 33 | ) { 34 | for(int i = 0; i < n; i++){ 35 | int value = in[i]; 36 | int low = value & 15; 37 | int high = value >> 4; 38 | out[i] = out[i] ^ lowhigh[low] ^ lowhigh[high+16]; 39 | } 40 | } 41 | 42 | void addmulSSSE3( 43 | uint8_t * restrict lowhigh, 44 | uint8_t * restrict in, 45 | uint8_t * restrict out, 46 | int n 47 | ) { 48 | int i = 0; 49 | 50 | __m128i lotbl = _mm_loadu_si128((__m128i*)(&lowhigh[0])); 51 | __m128i hitbl = _mm_loadu_si128((__m128i*)(&lowhigh[16])); 52 | 53 | __m128i lomask = _mm_set1_epi8(0xF); 54 | 55 | #pragma nounroll 56 | for(i = 0; i < (n/16)*16; i += 16){ 57 | __m128i input8 = _mm_loadu_si128((__m128i*)(&in[i])); 58 | __m128i output8 = _mm_loadu_si128((__m128i*)(&out[i])); 59 | 60 | __m128i lo8 = _mm_and_si128(lomask, input8); 61 | __m128i hi8 = _mm_and_si128(lomask, _mm_srli_si128(input8, 4)); // simulate shrli epi8 62 | 63 | output8 = _mm_xor_si128(output8, _mm_shuffle_epi8(lotbl, lo8)); 64 | output8 = _mm_xor_si128(output8, _mm_shuffle_epi8(hitbl, hi8)); 65 | 66 | _mm_storeu_si128((__m128i*)(&out[i]), output8); 67 | } 68 | } 69 | */ 70 | 71 | #include "textflag.h" 72 | DATA nybble_mask<>+0x00(SB)/8, $0x0F0F0F0F0F0F0F0F 73 | DATA nybble_mask<>+0x08(SB)/8, $0x0F0F0F0F0F0F0F0F 74 | DATA nybble_mask<>+0x10(SB)/8, $0x0F0F0F0F0F0F0F0F 75 | DATA nybble_mask<>+0x18(SB)/8, $0x0F0F0F0F0F0F0F0F 76 | GLOBL nybble_mask<>(SB), (NOPTR+RODATA), $32 77 | 78 | #define LOWHIGH DI 79 | #define LOW X8 80 | #define HIGH X9 81 | #define IN SI 82 | #define OUT DX 83 | #define INDEX AX 84 | 85 | #define LEN CX 86 | #define LEN16 R8 // LEN16 = (LEN / 16) * 16 87 | 88 | #define LOMASK X7 // LOMASK = repeated 15 89 | // X0-X5 temps 90 | 91 | // func addmulSSSE3(lowhigh *[2][16]byte, in, out *byte, len int) 92 | TEXT ·addmulSSSE3(SB), 7, $0 93 | MOVQ _in+8(FP), IN 94 | MOVQ _out+16(FP), OUT 95 | MOVQ _len+24(FP), LEN 96 | 97 | MOVQ LEN, LEN16 98 | ANDQ $-16, LEN16 99 | 100 | JLE start_slow // if LEN16 == 0 { goto done } 101 | 102 | MOVQ _lohi+0(FP), LOWHIGH 103 | MOVOU (LOWHIGH), LOW 104 | MOVOU 16(LOWHIGH), HIGH 105 | 106 | MOVOU nybble_mask<>(SB), LOMASK 107 | XORQ INDEX, INDEX // INDEX = 0 108 | 109 | loop16: 110 | MOVOU (IN)(INDEX*1), X0 // X0 = INPUT[INDEX] 111 | MOVOU LOW, X4 // X4 = copy(LOW) 112 | MOVOU (OUT)(INDEX*1), X2 // X2 = OUT[INDEX] 113 | MOVOU X0, X1 // X0 = input[index] & 15 114 | MOVOU HIGH, X5 // X5 = copy(HIGH) 115 | 116 | PAND LOMASK, X0 117 | PSRLQ $4, X1 // X1 = input[index] 118 | PSHUFB X0, X4 // X4 = LOW[X0] 119 | 120 | PAND LOMASK, X1 // X1 = input[index] >> 4 121 | PSHUFB X1, X5 // X5 = HIGH[X1] 122 | PXOR X4, X2 // X2 = OUT[INDEX] ^ X4 ^ X5 123 | PXOR X5, X2 124 | 125 | MOVOU X2, 0(OUT)(INDEX*1) 126 | 127 | ADDQ $16, INDEX 128 | CMPQ LEN16, INDEX // INDEX < LEN16 129 | JG loop16 130 | 131 | start_slow: 132 | MOVQ _len+32(FP), LOWHIGH 133 | MOVQ LEN16, INDEX 134 | CMPQ LEN, INDEX 135 | JLE done 136 | 137 | loop1: 138 | MOVBQZX (IN)(INDEX*1), R9 // R9 := in[index] 139 | MOVBQZX (LOWHIGH)(R9*1), R10 // R10 := multiply[R9] 140 | XORB R10B, (OUT)(INDEX*1) // out[index] ^= R10 141 | INCQ INDEX 142 | CMPQ LEN, INDEX 143 | JG loop1 144 | 145 | done: 146 | RET 147 | 148 | #undef LOWHIGH 149 | #undef LOW 150 | #undef HIGH 151 | #undef IN 152 | #undef OUT 153 | #undef LEN 154 | #undef INDEX 155 | #undef LEN16 156 | #undef LOMASK 157 | 158 | // func addmulAVX2(lowhigh *[2][16]byte, in, out *byte, len int) 159 | TEXT ·addmulAVX2(SB), 7, $0 160 | MOVQ low+0(FP), SI // SI: &lowhigh 161 | MOVOU (SI), X6 // X6: low 162 | MOVOU 16(SI), X7 // X7: high 163 | 164 | MOVQ $15, BX // BX: low mask 165 | MOVQ BX, X5 166 | 167 | MOVQ len+24(FP), R9 // R9: len(in), len(out) 168 | 169 | LONG $0x384de3c4; WORD $0x01f6 // VINSERTI128 YMM6, YMM6, XMM6, 1 ; low 170 | LONG $0x3845e3c4; WORD $0x01ff // VINSERTI128 YMM7, YMM7, XMM7, 1 ; high 171 | LONG $0x787d62c4; BYTE $0xc5 // VPBROADCASTB YMM8, XMM5 ; X8: lomask (unpacked) 172 | 173 | SHRQ $5, R9 // len(in) / 32 174 | MOVQ out+16(FP), DX // DX: &out 175 | MOVQ in+8(FP), SI // R11: &in 176 | TESTQ R9, R9 177 | JZ done_xor_avx2 178 | 179 | loopback_xor_avx2: 180 | LONG $0x066ffec5 // VMOVDQU YMM0, [rsi] 181 | LONG $0x226ffec5 // VMOVDQU YMM4, [rdx] 182 | LONG $0xd073f5c5; BYTE $0x04 // VPSRLQ YMM1, YMM0, 4 ; X1: high input 183 | LONG $0xdb7dc1c4; BYTE $0xc0 // VPAND YMM0, YMM0, YMM8 ; X0: low input 184 | LONG $0xdb75c1c4; BYTE $0xc8 // VPAND YMM1, YMM1, YMM8 ; X1: high input 185 | LONG $0x004de2c4; BYTE $0xd0 // VPSHUFB YMM2, YMM6, YMM0 ; X2: mul low part 186 | LONG $0x0045e2c4; BYTE $0xd9 // VPSHUFB YMM3, YMM7, YMM1 ; X2: mul high part 187 | LONG $0xdbefedc5 // VPXOR YMM3, YMM2, YMM3 ; X3: Result 188 | LONG $0xe4efe5c5 // VPXOR YMM4, YMM3, YMM4 ; X4: Result 189 | LONG $0x227ffec5 // VMOVDQU [rdx], YMM4 190 | 191 | ADDQ $32, SI // in+=32 192 | ADDQ $32, DX // out+=32 193 | SUBQ $1, R9 194 | JNZ loopback_xor_avx2 195 | 196 | done_xor_avx2: 197 | // VZEROUPPER 198 | BYTE $0xc5; BYTE $0xf8; BYTE $0x77 199 | RET 200 | -------------------------------------------------------------------------------- /addmul_noasm.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // +build !amd64 24 | 25 | package infectious 26 | 27 | func addmul(z []byte, x []byte, y byte) { 28 | if y == 0 { 29 | return 30 | } 31 | 32 | // hint to the compiler that we don't need bounds checks on x 33 | x = x[:len(z)] 34 | 35 | // TODO(jeff): loop unrolling for SPEEDS 36 | gf_mul_y := gf_mul_table[y][:] 37 | for i := range z { 38 | z[i] ^= gf_mul_y[x[i]] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /addmul_tables_amd64.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // Copyright (c) 2015 Klaus Post 5 | // Copyright (c) 2015 Backblaze 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | package infectious 26 | 27 | type pair struct { 28 | low, high [16]byte 29 | } 30 | 31 | var mul_table_pair = [256]pair{ 32 | {[16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, [16]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, 33 | {[16]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, [16]byte{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}}, 34 | {[16]byte{0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e}, [16]byte{0x00, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x1d, 0x3d, 0x5d, 0x7d, 0x9d, 0xbd, 0xdd, 0xfd}}, 35 | {[16]byte{0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11}, [16]byte{0x00, 0x30, 0x60, 0x50, 0xc0, 0xf0, 0xa0, 0x90, 0x9d, 0xad, 0xfd, 0xcd, 0x5d, 0x6d, 0x3d, 0x0d}}, 36 | {[16]byte{0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c}, [16]byte{0x00, 0x40, 0x80, 0xc0, 0x1d, 0x5d, 0x9d, 0xdd, 0x3a, 0x7a, 0xba, 0xfa, 0x27, 0x67, 0xa7, 0xe7}}, 37 | {[16]byte{0x00, 0x05, 0x0a, 0x0f, 0x14, 0x11, 0x1e, 0x1b, 0x28, 0x2d, 0x22, 0x27, 0x3c, 0x39, 0x36, 0x33}, [16]byte{0x00, 0x50, 0xa0, 0xf0, 0x5d, 0x0d, 0xfd, 0xad, 0xba, 0xea, 0x1a, 0x4a, 0xe7, 0xb7, 0x47, 0x17}}, 38 | {[16]byte{0x00, 0x06, 0x0c, 0x0a, 0x18, 0x1e, 0x14, 0x12, 0x30, 0x36, 0x3c, 0x3a, 0x28, 0x2e, 0x24, 0x22}, [16]byte{0x00, 0x60, 0xc0, 0xa0, 0x9d, 0xfd, 0x5d, 0x3d, 0x27, 0x47, 0xe7, 0x87, 0xba, 0xda, 0x7a, 0x1a}}, 39 | {[16]byte{0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d}, [16]byte{0x00, 0x70, 0xe0, 0x90, 0xdd, 0xad, 0x3d, 0x4d, 0xa7, 0xd7, 0x47, 0x37, 0x7a, 0x0a, 0x9a, 0xea}}, 40 | {[16]byte{0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78}, [16]byte{0x00, 0x80, 0x1d, 0x9d, 0x3a, 0xba, 0x27, 0xa7, 0x74, 0xf4, 0x69, 0xe9, 0x4e, 0xce, 0x53, 0xd3}}, 41 | {[16]byte{0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77}, [16]byte{0x00, 0x90, 0x3d, 0xad, 0x7a, 0xea, 0x47, 0xd7, 0xf4, 0x64, 0xc9, 0x59, 0x8e, 0x1e, 0xb3, 0x23}}, 42 | {[16]byte{0x00, 0x0a, 0x14, 0x1e, 0x28, 0x22, 0x3c, 0x36, 0x50, 0x5a, 0x44, 0x4e, 0x78, 0x72, 0x6c, 0x66}, [16]byte{0x00, 0xa0, 0x5d, 0xfd, 0xba, 0x1a, 0xe7, 0x47, 0x69, 0xc9, 0x34, 0x94, 0xd3, 0x73, 0x8e, 0x2e}}, 43 | {[16]byte{0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69}, [16]byte{0x00, 0xb0, 0x7d, 0xcd, 0xfa, 0x4a, 0x87, 0x37, 0xe9, 0x59, 0x94, 0x24, 0x13, 0xa3, 0x6e, 0xde}}, 44 | {[16]byte{0x00, 0x0c, 0x18, 0x14, 0x30, 0x3c, 0x28, 0x24, 0x60, 0x6c, 0x78, 0x74, 0x50, 0x5c, 0x48, 0x44}, [16]byte{0x00, 0xc0, 0x9d, 0x5d, 0x27, 0xe7, 0xba, 0x7a, 0x4e, 0x8e, 0xd3, 0x13, 0x69, 0xa9, 0xf4, 0x34}}, 45 | {[16]byte{0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b}, [16]byte{0x00, 0xd0, 0xbd, 0x6d, 0x67, 0xb7, 0xda, 0x0a, 0xce, 0x1e, 0x73, 0xa3, 0xa9, 0x79, 0x14, 0xc4}}, 46 | {[16]byte{0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a}, [16]byte{0x00, 0xe0, 0xdd, 0x3d, 0xa7, 0x47, 0x7a, 0x9a, 0x53, 0xb3, 0x8e, 0x6e, 0xf4, 0x14, 0x29, 0xc9}}, 47 | {[16]byte{0x00, 0x0f, 0x1e, 0x11, 0x3c, 0x33, 0x22, 0x2d, 0x78, 0x77, 0x66, 0x69, 0x44, 0x4b, 0x5a, 0x55}, [16]byte{0x00, 0xf0, 0xfd, 0x0d, 0xe7, 0x17, 0x1a, 0xea, 0xd3, 0x23, 0x2e, 0xde, 0x34, 0xc4, 0xc9, 0x39}}, 48 | {[16]byte{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}, [16]byte{0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb}}, 49 | {[16]byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, [16]byte{0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b}}, 50 | {[16]byte{0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee}, [16]byte{0x00, 0x3d, 0x7a, 0x47, 0xf4, 0xc9, 0x8e, 0xb3, 0xf5, 0xc8, 0x8f, 0xb2, 0x01, 0x3c, 0x7b, 0x46}}, 51 | {[16]byte{0x00, 0x13, 0x26, 0x35, 0x4c, 0x5f, 0x6a, 0x79, 0x98, 0x8b, 0xbe, 0xad, 0xd4, 0xc7, 0xf2, 0xe1}, [16]byte{0x00, 0x2d, 0x5a, 0x77, 0xb4, 0x99, 0xee, 0xc3, 0x75, 0x58, 0x2f, 0x02, 0xc1, 0xec, 0x9b, 0xb6}}, 52 | {[16]byte{0x00, 0x14, 0x28, 0x3c, 0x50, 0x44, 0x78, 0x6c, 0xa0, 0xb4, 0x88, 0x9c, 0xf0, 0xe4, 0xd8, 0xcc}, [16]byte{0x00, 0x5d, 0xba, 0xe7, 0x69, 0x34, 0xd3, 0x8e, 0xd2, 0x8f, 0x68, 0x35, 0xbb, 0xe6, 0x01, 0x5c}}, 53 | {[16]byte{0x00, 0x15, 0x2a, 0x3f, 0x54, 0x41, 0x7e, 0x6b, 0xa8, 0xbd, 0x82, 0x97, 0xfc, 0xe9, 0xd6, 0xc3}, [16]byte{0x00, 0x4d, 0x9a, 0xd7, 0x29, 0x64, 0xb3, 0xfe, 0x52, 0x1f, 0xc8, 0x85, 0x7b, 0x36, 0xe1, 0xac}}, 54 | {[16]byte{0x00, 0x16, 0x2c, 0x3a, 0x58, 0x4e, 0x74, 0x62, 0xb0, 0xa6, 0x9c, 0x8a, 0xe8, 0xfe, 0xc4, 0xd2}, [16]byte{0x00, 0x7d, 0xfa, 0x87, 0xe9, 0x94, 0x13, 0x6e, 0xcf, 0xb2, 0x35, 0x48, 0x26, 0x5b, 0xdc, 0xa1}}, 55 | {[16]byte{0x00, 0x17, 0x2e, 0x39, 0x5c, 0x4b, 0x72, 0x65, 0xb8, 0xaf, 0x96, 0x81, 0xe4, 0xf3, 0xca, 0xdd}, [16]byte{0x00, 0x6d, 0xda, 0xb7, 0xa9, 0xc4, 0x73, 0x1e, 0x4f, 0x22, 0x95, 0xf8, 0xe6, 0x8b, 0x3c, 0x51}}, 56 | {[16]byte{0x00, 0x18, 0x30, 0x28, 0x60, 0x78, 0x50, 0x48, 0xc0, 0xd8, 0xf0, 0xe8, 0xa0, 0xb8, 0x90, 0x88}, [16]byte{0x00, 0x9d, 0x27, 0xba, 0x4e, 0xd3, 0x69, 0xf4, 0x9c, 0x01, 0xbb, 0x26, 0xd2, 0x4f, 0xf5, 0x68}}, 57 | {[16]byte{0x00, 0x19, 0x32, 0x2b, 0x64, 0x7d, 0x56, 0x4f, 0xc8, 0xd1, 0xfa, 0xe3, 0xac, 0xb5, 0x9e, 0x87}, [16]byte{0x00, 0x8d, 0x07, 0x8a, 0x0e, 0x83, 0x09, 0x84, 0x1c, 0x91, 0x1b, 0x96, 0x12, 0x9f, 0x15, 0x98}}, 58 | {[16]byte{0x00, 0x1a, 0x34, 0x2e, 0x68, 0x72, 0x5c, 0x46, 0xd0, 0xca, 0xe4, 0xfe, 0xb8, 0xa2, 0x8c, 0x96}, [16]byte{0x00, 0xbd, 0x67, 0xda, 0xce, 0x73, 0xa9, 0x14, 0x81, 0x3c, 0xe6, 0x5b, 0x4f, 0xf2, 0x28, 0x95}}, 59 | {[16]byte{0x00, 0x1b, 0x36, 0x2d, 0x6c, 0x77, 0x5a, 0x41, 0xd8, 0xc3, 0xee, 0xf5, 0xb4, 0xaf, 0x82, 0x99}, [16]byte{0x00, 0xad, 0x47, 0xea, 0x8e, 0x23, 0xc9, 0x64, 0x01, 0xac, 0x46, 0xeb, 0x8f, 0x22, 0xc8, 0x65}}, 60 | {[16]byte{0x00, 0x1c, 0x38, 0x24, 0x70, 0x6c, 0x48, 0x54, 0xe0, 0xfc, 0xd8, 0xc4, 0x90, 0x8c, 0xa8, 0xb4}, [16]byte{0x00, 0xdd, 0xa7, 0x7a, 0x53, 0x8e, 0xf4, 0x29, 0xa6, 0x7b, 0x01, 0xdc, 0xf5, 0x28, 0x52, 0x8f}}, 61 | {[16]byte{0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb}, [16]byte{0x00, 0xcd, 0x87, 0x4a, 0x13, 0xde, 0x94, 0x59, 0x26, 0xeb, 0xa1, 0x6c, 0x35, 0xf8, 0xb2, 0x7f}}, 62 | {[16]byte{0x00, 0x1e, 0x3c, 0x22, 0x78, 0x66, 0x44, 0x5a, 0xf0, 0xee, 0xcc, 0xd2, 0x88, 0x96, 0xb4, 0xaa}, [16]byte{0x00, 0xfd, 0xe7, 0x1a, 0xd3, 0x2e, 0x34, 0xc9, 0xbb, 0x46, 0x5c, 0xa1, 0x68, 0x95, 0x8f, 0x72}}, 63 | {[16]byte{0x00, 0x1f, 0x3e, 0x21, 0x7c, 0x63, 0x42, 0x5d, 0xf8, 0xe7, 0xc6, 0xd9, 0x84, 0x9b, 0xba, 0xa5}, [16]byte{0x00, 0xed, 0xc7, 0x2a, 0x93, 0x7e, 0x54, 0xb9, 0x3b, 0xd6, 0xfc, 0x11, 0xa8, 0x45, 0x6f, 0x82}}, 64 | {[16]byte{0x00, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x1d, 0x3d, 0x5d, 0x7d, 0x9d, 0xbd, 0xdd, 0xfd}, [16]byte{0x00, 0x3a, 0x74, 0x4e, 0xe8, 0xd2, 0x9c, 0xa6, 0xcd, 0xf7, 0xb9, 0x83, 0x25, 0x1f, 0x51, 0x6b}}, 65 | {[16]byte{0x00, 0x21, 0x42, 0x63, 0x84, 0xa5, 0xc6, 0xe7, 0x15, 0x34, 0x57, 0x76, 0x91, 0xb0, 0xd3, 0xf2}, [16]byte{0x00, 0x2a, 0x54, 0x7e, 0xa8, 0x82, 0xfc, 0xd6, 0x4d, 0x67, 0x19, 0x33, 0xe5, 0xcf, 0xb1, 0x9b}}, 66 | {[16]byte{0x00, 0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0x0d, 0x2f, 0x49, 0x6b, 0x85, 0xa7, 0xc1, 0xe3}, [16]byte{0x00, 0x1a, 0x34, 0x2e, 0x68, 0x72, 0x5c, 0x46, 0xd0, 0xca, 0xe4, 0xfe, 0xb8, 0xa2, 0x8c, 0x96}}, 67 | {[16]byte{0x00, 0x23, 0x46, 0x65, 0x8c, 0xaf, 0xca, 0xe9, 0x05, 0x26, 0x43, 0x60, 0x89, 0xaa, 0xcf, 0xec}, [16]byte{0x00, 0x0a, 0x14, 0x1e, 0x28, 0x22, 0x3c, 0x36, 0x50, 0x5a, 0x44, 0x4e, 0x78, 0x72, 0x6c, 0x66}}, 68 | {[16]byte{0x00, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0xd8, 0xfc, 0x3d, 0x19, 0x75, 0x51, 0xad, 0x89, 0xe5, 0xc1}, [16]byte{0x00, 0x7a, 0xf4, 0x8e, 0xf5, 0x8f, 0x01, 0x7b, 0xf7, 0x8d, 0x03, 0x79, 0x02, 0x78, 0xf6, 0x8c}}, 69 | {[16]byte{0x00, 0x25, 0x4a, 0x6f, 0x94, 0xb1, 0xde, 0xfb, 0x35, 0x10, 0x7f, 0x5a, 0xa1, 0x84, 0xeb, 0xce}, [16]byte{0x00, 0x6a, 0xd4, 0xbe, 0xb5, 0xdf, 0x61, 0x0b, 0x77, 0x1d, 0xa3, 0xc9, 0xc2, 0xa8, 0x16, 0x7c}}, 70 | {[16]byte{0x00, 0x26, 0x4c, 0x6a, 0x98, 0xbe, 0xd4, 0xf2, 0x2d, 0x0b, 0x61, 0x47, 0xb5, 0x93, 0xf9, 0xdf}, [16]byte{0x00, 0x5a, 0xb4, 0xee, 0x75, 0x2f, 0xc1, 0x9b, 0xea, 0xb0, 0x5e, 0x04, 0x9f, 0xc5, 0x2b, 0x71}}, 71 | {[16]byte{0x00, 0x27, 0x4e, 0x69, 0x9c, 0xbb, 0xd2, 0xf5, 0x25, 0x02, 0x6b, 0x4c, 0xb9, 0x9e, 0xf7, 0xd0}, [16]byte{0x00, 0x4a, 0x94, 0xde, 0x35, 0x7f, 0xa1, 0xeb, 0x6a, 0x20, 0xfe, 0xb4, 0x5f, 0x15, 0xcb, 0x81}}, 72 | {[16]byte{0x00, 0x28, 0x50, 0x78, 0xa0, 0x88, 0xf0, 0xd8, 0x5d, 0x75, 0x0d, 0x25, 0xfd, 0xd5, 0xad, 0x85}, [16]byte{0x00, 0xba, 0x69, 0xd3, 0xd2, 0x68, 0xbb, 0x01, 0xb9, 0x03, 0xd0, 0x6a, 0x6b, 0xd1, 0x02, 0xb8}}, 73 | {[16]byte{0x00, 0x29, 0x52, 0x7b, 0xa4, 0x8d, 0xf6, 0xdf, 0x55, 0x7c, 0x07, 0x2e, 0xf1, 0xd8, 0xa3, 0x8a}, [16]byte{0x00, 0xaa, 0x49, 0xe3, 0x92, 0x38, 0xdb, 0x71, 0x39, 0x93, 0x70, 0xda, 0xab, 0x01, 0xe2, 0x48}}, 74 | {[16]byte{0x00, 0x2a, 0x54, 0x7e, 0xa8, 0x82, 0xfc, 0xd6, 0x4d, 0x67, 0x19, 0x33, 0xe5, 0xcf, 0xb1, 0x9b}, [16]byte{0x00, 0x9a, 0x29, 0xb3, 0x52, 0xc8, 0x7b, 0xe1, 0xa4, 0x3e, 0x8d, 0x17, 0xf6, 0x6c, 0xdf, 0x45}}, 75 | {[16]byte{0x00, 0x2b, 0x56, 0x7d, 0xac, 0x87, 0xfa, 0xd1, 0x45, 0x6e, 0x13, 0x38, 0xe9, 0xc2, 0xbf, 0x94}, [16]byte{0x00, 0x8a, 0x09, 0x83, 0x12, 0x98, 0x1b, 0x91, 0x24, 0xae, 0x2d, 0xa7, 0x36, 0xbc, 0x3f, 0xb5}}, 76 | {[16]byte{0x00, 0x2c, 0x58, 0x74, 0xb0, 0x9c, 0xe8, 0xc4, 0x7d, 0x51, 0x25, 0x09, 0xcd, 0xe1, 0x95, 0xb9}, [16]byte{0x00, 0xfa, 0xe9, 0x13, 0xcf, 0x35, 0x26, 0xdc, 0x83, 0x79, 0x6a, 0x90, 0x4c, 0xb6, 0xa5, 0x5f}}, 77 | {[16]byte{0x00, 0x2d, 0x5a, 0x77, 0xb4, 0x99, 0xee, 0xc3, 0x75, 0x58, 0x2f, 0x02, 0xc1, 0xec, 0x9b, 0xb6}, [16]byte{0x00, 0xea, 0xc9, 0x23, 0x8f, 0x65, 0x46, 0xac, 0x03, 0xe9, 0xca, 0x20, 0x8c, 0x66, 0x45, 0xaf}}, 78 | {[16]byte{0x00, 0x2e, 0x5c, 0x72, 0xb8, 0x96, 0xe4, 0xca, 0x6d, 0x43, 0x31, 0x1f, 0xd5, 0xfb, 0x89, 0xa7}, [16]byte{0x00, 0xda, 0xa9, 0x73, 0x4f, 0x95, 0xe6, 0x3c, 0x9e, 0x44, 0x37, 0xed, 0xd1, 0x0b, 0x78, 0xa2}}, 79 | {[16]byte{0x00, 0x2f, 0x5e, 0x71, 0xbc, 0x93, 0xe2, 0xcd, 0x65, 0x4a, 0x3b, 0x14, 0xd9, 0xf6, 0x87, 0xa8}, [16]byte{0x00, 0xca, 0x89, 0x43, 0x0f, 0xc5, 0x86, 0x4c, 0x1e, 0xd4, 0x97, 0x5d, 0x11, 0xdb, 0x98, 0x52}}, 80 | {[16]byte{0x00, 0x30, 0x60, 0x50, 0xc0, 0xf0, 0xa0, 0x90, 0x9d, 0xad, 0xfd, 0xcd, 0x5d, 0x6d, 0x3d, 0x0d}, [16]byte{0x00, 0x27, 0x4e, 0x69, 0x9c, 0xbb, 0xd2, 0xf5, 0x25, 0x02, 0x6b, 0x4c, 0xb9, 0x9e, 0xf7, 0xd0}}, 81 | {[16]byte{0x00, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, 0x95, 0xa4, 0xf7, 0xc6, 0x51, 0x60, 0x33, 0x02}, [16]byte{0x00, 0x37, 0x6e, 0x59, 0xdc, 0xeb, 0xb2, 0x85, 0xa5, 0x92, 0xcb, 0xfc, 0x79, 0x4e, 0x17, 0x20}}, 82 | {[16]byte{0x00, 0x32, 0x64, 0x56, 0xc8, 0xfa, 0xac, 0x9e, 0x8d, 0xbf, 0xe9, 0xdb, 0x45, 0x77, 0x21, 0x13}, [16]byte{0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d}}, 83 | {[16]byte{0x00, 0x33, 0x66, 0x55, 0xcc, 0xff, 0xaa, 0x99, 0x85, 0xb6, 0xe3, 0xd0, 0x49, 0x7a, 0x2f, 0x1c}, [16]byte{0x00, 0x17, 0x2e, 0x39, 0x5c, 0x4b, 0x72, 0x65, 0xb8, 0xaf, 0x96, 0x81, 0xe4, 0xf3, 0xca, 0xdd}}, 84 | {[16]byte{0x00, 0x34, 0x68, 0x5c, 0xd0, 0xe4, 0xb8, 0x8c, 0xbd, 0x89, 0xd5, 0xe1, 0x6d, 0x59, 0x05, 0x31}, [16]byte{0x00, 0x67, 0xce, 0xa9, 0x81, 0xe6, 0x4f, 0x28, 0x1f, 0x78, 0xd1, 0xb6, 0x9e, 0xf9, 0x50, 0x37}}, 85 | {[16]byte{0x00, 0x35, 0x6a, 0x5f, 0xd4, 0xe1, 0xbe, 0x8b, 0xb5, 0x80, 0xdf, 0xea, 0x61, 0x54, 0x0b, 0x3e}, [16]byte{0x00, 0x77, 0xee, 0x99, 0xc1, 0xb6, 0x2f, 0x58, 0x9f, 0xe8, 0x71, 0x06, 0x5e, 0x29, 0xb0, 0xc7}}, 86 | {[16]byte{0x00, 0x36, 0x6c, 0x5a, 0xd8, 0xee, 0xb4, 0x82, 0xad, 0x9b, 0xc1, 0xf7, 0x75, 0x43, 0x19, 0x2f}, [16]byte{0x00, 0x47, 0x8e, 0xc9, 0x01, 0x46, 0x8f, 0xc8, 0x02, 0x45, 0x8c, 0xcb, 0x03, 0x44, 0x8d, 0xca}}, 87 | {[16]byte{0x00, 0x37, 0x6e, 0x59, 0xdc, 0xeb, 0xb2, 0x85, 0xa5, 0x92, 0xcb, 0xfc, 0x79, 0x4e, 0x17, 0x20}, [16]byte{0x00, 0x57, 0xae, 0xf9, 0x41, 0x16, 0xef, 0xb8, 0x82, 0xd5, 0x2c, 0x7b, 0xc3, 0x94, 0x6d, 0x3a}}, 88 | {[16]byte{0x00, 0x38, 0x70, 0x48, 0xe0, 0xd8, 0x90, 0xa8, 0xdd, 0xe5, 0xad, 0x95, 0x3d, 0x05, 0x4d, 0x75}, [16]byte{0x00, 0xa7, 0x53, 0xf4, 0xa6, 0x01, 0xf5, 0x52, 0x51, 0xf6, 0x02, 0xa5, 0xf7, 0x50, 0xa4, 0x03}}, 89 | {[16]byte{0x00, 0x39, 0x72, 0x4b, 0xe4, 0xdd, 0x96, 0xaf, 0xd5, 0xec, 0xa7, 0x9e, 0x31, 0x08, 0x43, 0x7a}, [16]byte{0x00, 0xb7, 0x73, 0xc4, 0xe6, 0x51, 0x95, 0x22, 0xd1, 0x66, 0xa2, 0x15, 0x37, 0x80, 0x44, 0xf3}}, 90 | {[16]byte{0x00, 0x3a, 0x74, 0x4e, 0xe8, 0xd2, 0x9c, 0xa6, 0xcd, 0xf7, 0xb9, 0x83, 0x25, 0x1f, 0x51, 0x6b}, [16]byte{0x00, 0x87, 0x13, 0x94, 0x26, 0xa1, 0x35, 0xb2, 0x4c, 0xcb, 0x5f, 0xd8, 0x6a, 0xed, 0x79, 0xfe}}, 91 | {[16]byte{0x00, 0x3b, 0x76, 0x4d, 0xec, 0xd7, 0x9a, 0xa1, 0xc5, 0xfe, 0xb3, 0x88, 0x29, 0x12, 0x5f, 0x64}, [16]byte{0x00, 0x97, 0x33, 0xa4, 0x66, 0xf1, 0x55, 0xc2, 0xcc, 0x5b, 0xff, 0x68, 0xaa, 0x3d, 0x99, 0x0e}}, 92 | {[16]byte{0x00, 0x3c, 0x78, 0x44, 0xf0, 0xcc, 0x88, 0xb4, 0xfd, 0xc1, 0x85, 0xb9, 0x0d, 0x31, 0x75, 0x49}, [16]byte{0x00, 0xe7, 0xd3, 0x34, 0xbb, 0x5c, 0x68, 0x8f, 0x6b, 0x8c, 0xb8, 0x5f, 0xd0, 0x37, 0x03, 0xe4}}, 93 | {[16]byte{0x00, 0x3d, 0x7a, 0x47, 0xf4, 0xc9, 0x8e, 0xb3, 0xf5, 0xc8, 0x8f, 0xb2, 0x01, 0x3c, 0x7b, 0x46}, [16]byte{0x00, 0xf7, 0xf3, 0x04, 0xfb, 0x0c, 0x08, 0xff, 0xeb, 0x1c, 0x18, 0xef, 0x10, 0xe7, 0xe3, 0x14}}, 94 | {[16]byte{0x00, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0xed, 0xd3, 0x91, 0xaf, 0x15, 0x2b, 0x69, 0x57}, [16]byte{0x00, 0xc7, 0x93, 0x54, 0x3b, 0xfc, 0xa8, 0x6f, 0x76, 0xb1, 0xe5, 0x22, 0x4d, 0x8a, 0xde, 0x19}}, 95 | {[16]byte{0x00, 0x3f, 0x7e, 0x41, 0xfc, 0xc3, 0x82, 0xbd, 0xe5, 0xda, 0x9b, 0xa4, 0x19, 0x26, 0x67, 0x58}, [16]byte{0x00, 0xd7, 0xb3, 0x64, 0x7b, 0xac, 0xc8, 0x1f, 0xf6, 0x21, 0x45, 0x92, 0x8d, 0x5a, 0x3e, 0xe9}}, 96 | {[16]byte{0x00, 0x40, 0x80, 0xc0, 0x1d, 0x5d, 0x9d, 0xdd, 0x3a, 0x7a, 0xba, 0xfa, 0x27, 0x67, 0xa7, 0xe7}, [16]byte{0x00, 0x74, 0xe8, 0x9c, 0xcd, 0xb9, 0x25, 0x51, 0x87, 0xf3, 0x6f, 0x1b, 0x4a, 0x3e, 0xa2, 0xd6}}, 97 | {[16]byte{0x00, 0x41, 0x82, 0xc3, 0x19, 0x58, 0x9b, 0xda, 0x32, 0x73, 0xb0, 0xf1, 0x2b, 0x6a, 0xa9, 0xe8}, [16]byte{0x00, 0x64, 0xc8, 0xac, 0x8d, 0xe9, 0x45, 0x21, 0x07, 0x63, 0xcf, 0xab, 0x8a, 0xee, 0x42, 0x26}}, 98 | {[16]byte{0x00, 0x42, 0x84, 0xc6, 0x15, 0x57, 0x91, 0xd3, 0x2a, 0x68, 0xae, 0xec, 0x3f, 0x7d, 0xbb, 0xf9}, [16]byte{0x00, 0x54, 0xa8, 0xfc, 0x4d, 0x19, 0xe5, 0xb1, 0x9a, 0xce, 0x32, 0x66, 0xd7, 0x83, 0x7f, 0x2b}}, 99 | {[16]byte{0x00, 0x43, 0x86, 0xc5, 0x11, 0x52, 0x97, 0xd4, 0x22, 0x61, 0xa4, 0xe7, 0x33, 0x70, 0xb5, 0xf6}, [16]byte{0x00, 0x44, 0x88, 0xcc, 0x0d, 0x49, 0x85, 0xc1, 0x1a, 0x5e, 0x92, 0xd6, 0x17, 0x53, 0x9f, 0xdb}}, 100 | {[16]byte{0x00, 0x44, 0x88, 0xcc, 0x0d, 0x49, 0x85, 0xc1, 0x1a, 0x5e, 0x92, 0xd6, 0x17, 0x53, 0x9f, 0xdb}, [16]byte{0x00, 0x34, 0x68, 0x5c, 0xd0, 0xe4, 0xb8, 0x8c, 0xbd, 0x89, 0xd5, 0xe1, 0x6d, 0x59, 0x05, 0x31}}, 101 | {[16]byte{0x00, 0x45, 0x8a, 0xcf, 0x09, 0x4c, 0x83, 0xc6, 0x12, 0x57, 0x98, 0xdd, 0x1b, 0x5e, 0x91, 0xd4}, [16]byte{0x00, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0xd8, 0xfc, 0x3d, 0x19, 0x75, 0x51, 0xad, 0x89, 0xe5, 0xc1}}, 102 | {[16]byte{0x00, 0x46, 0x8c, 0xca, 0x05, 0x43, 0x89, 0xcf, 0x0a, 0x4c, 0x86, 0xc0, 0x0f, 0x49, 0x83, 0xc5}, [16]byte{0x00, 0x14, 0x28, 0x3c, 0x50, 0x44, 0x78, 0x6c, 0xa0, 0xb4, 0x88, 0x9c, 0xf0, 0xe4, 0xd8, 0xcc}}, 103 | {[16]byte{0x00, 0x47, 0x8e, 0xc9, 0x01, 0x46, 0x8f, 0xc8, 0x02, 0x45, 0x8c, 0xcb, 0x03, 0x44, 0x8d, 0xca}, [16]byte{0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c}}, 104 | {[16]byte{0x00, 0x48, 0x90, 0xd8, 0x3d, 0x75, 0xad, 0xe5, 0x7a, 0x32, 0xea, 0xa2, 0x47, 0x0f, 0xd7, 0x9f}, [16]byte{0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05}}, 105 | {[16]byte{0x00, 0x49, 0x92, 0xdb, 0x39, 0x70, 0xab, 0xe2, 0x72, 0x3b, 0xe0, 0xa9, 0x4b, 0x02, 0xd9, 0x90}, [16]byte{0x00, 0xe4, 0xd5, 0x31, 0xb7, 0x53, 0x62, 0x86, 0x73, 0x97, 0xa6, 0x42, 0xc4, 0x20, 0x11, 0xf5}}, 106 | {[16]byte{0x00, 0x4a, 0x94, 0xde, 0x35, 0x7f, 0xa1, 0xeb, 0x6a, 0x20, 0xfe, 0xb4, 0x5f, 0x15, 0xcb, 0x81}, [16]byte{0x00, 0xd4, 0xb5, 0x61, 0x77, 0xa3, 0xc2, 0x16, 0xee, 0x3a, 0x5b, 0x8f, 0x99, 0x4d, 0x2c, 0xf8}}, 107 | {[16]byte{0x00, 0x4b, 0x96, 0xdd, 0x31, 0x7a, 0xa7, 0xec, 0x62, 0x29, 0xf4, 0xbf, 0x53, 0x18, 0xc5, 0x8e}, [16]byte{0x00, 0xc4, 0x95, 0x51, 0x37, 0xf3, 0xa2, 0x66, 0x6e, 0xaa, 0xfb, 0x3f, 0x59, 0x9d, 0xcc, 0x08}}, 108 | {[16]byte{0x00, 0x4c, 0x98, 0xd4, 0x2d, 0x61, 0xb5, 0xf9, 0x5a, 0x16, 0xc2, 0x8e, 0x77, 0x3b, 0xef, 0xa3}, [16]byte{0x00, 0xb4, 0x75, 0xc1, 0xea, 0x5e, 0x9f, 0x2b, 0xc9, 0x7d, 0xbc, 0x08, 0x23, 0x97, 0x56, 0xe2}}, 109 | {[16]byte{0x00, 0x4d, 0x9a, 0xd7, 0x29, 0x64, 0xb3, 0xfe, 0x52, 0x1f, 0xc8, 0x85, 0x7b, 0x36, 0xe1, 0xac}, [16]byte{0x00, 0xa4, 0x55, 0xf1, 0xaa, 0x0e, 0xff, 0x5b, 0x49, 0xed, 0x1c, 0xb8, 0xe3, 0x47, 0xb6, 0x12}}, 110 | {[16]byte{0x00, 0x4e, 0x9c, 0xd2, 0x25, 0x6b, 0xb9, 0xf7, 0x4a, 0x04, 0xd6, 0x98, 0x6f, 0x21, 0xf3, 0xbd}, [16]byte{0x00, 0x94, 0x35, 0xa1, 0x6a, 0xfe, 0x5f, 0xcb, 0xd4, 0x40, 0xe1, 0x75, 0xbe, 0x2a, 0x8b, 0x1f}}, 111 | {[16]byte{0x00, 0x4f, 0x9e, 0xd1, 0x21, 0x6e, 0xbf, 0xf0, 0x42, 0x0d, 0xdc, 0x93, 0x63, 0x2c, 0xfd, 0xb2}, [16]byte{0x00, 0x84, 0x15, 0x91, 0x2a, 0xae, 0x3f, 0xbb, 0x54, 0xd0, 0x41, 0xc5, 0x7e, 0xfa, 0x6b, 0xef}}, 112 | {[16]byte{0x00, 0x50, 0xa0, 0xf0, 0x5d, 0x0d, 0xfd, 0xad, 0xba, 0xea, 0x1a, 0x4a, 0xe7, 0xb7, 0x47, 0x17}, [16]byte{0x00, 0x69, 0xd2, 0xbb, 0xb9, 0xd0, 0x6b, 0x02, 0x6f, 0x06, 0xbd, 0xd4, 0xd6, 0xbf, 0x04, 0x6d}}, 113 | {[16]byte{0x00, 0x51, 0xa2, 0xf3, 0x59, 0x08, 0xfb, 0xaa, 0xb2, 0xe3, 0x10, 0x41, 0xeb, 0xba, 0x49, 0x18}, [16]byte{0x00, 0x79, 0xf2, 0x8b, 0xf9, 0x80, 0x0b, 0x72, 0xef, 0x96, 0x1d, 0x64, 0x16, 0x6f, 0xe4, 0x9d}}, 114 | {[16]byte{0x00, 0x52, 0xa4, 0xf6, 0x55, 0x07, 0xf1, 0xa3, 0xaa, 0xf8, 0x0e, 0x5c, 0xff, 0xad, 0x5b, 0x09}, [16]byte{0x00, 0x49, 0x92, 0xdb, 0x39, 0x70, 0xab, 0xe2, 0x72, 0x3b, 0xe0, 0xa9, 0x4b, 0x02, 0xd9, 0x90}}, 115 | {[16]byte{0x00, 0x53, 0xa6, 0xf5, 0x51, 0x02, 0xf7, 0xa4, 0xa2, 0xf1, 0x04, 0x57, 0xf3, 0xa0, 0x55, 0x06}, [16]byte{0x00, 0x59, 0xb2, 0xeb, 0x79, 0x20, 0xcb, 0x92, 0xf2, 0xab, 0x40, 0x19, 0x8b, 0xd2, 0x39, 0x60}}, 116 | {[16]byte{0x00, 0x54, 0xa8, 0xfc, 0x4d, 0x19, 0xe5, 0xb1, 0x9a, 0xce, 0x32, 0x66, 0xd7, 0x83, 0x7f, 0x2b}, [16]byte{0x00, 0x29, 0x52, 0x7b, 0xa4, 0x8d, 0xf6, 0xdf, 0x55, 0x7c, 0x07, 0x2e, 0xf1, 0xd8, 0xa3, 0x8a}}, 117 | {[16]byte{0x00, 0x55, 0xaa, 0xff, 0x49, 0x1c, 0xe3, 0xb6, 0x92, 0xc7, 0x38, 0x6d, 0xdb, 0x8e, 0x71, 0x24}, [16]byte{0x00, 0x39, 0x72, 0x4b, 0xe4, 0xdd, 0x96, 0xaf, 0xd5, 0xec, 0xa7, 0x9e, 0x31, 0x08, 0x43, 0x7a}}, 118 | {[16]byte{0x00, 0x56, 0xac, 0xfa, 0x45, 0x13, 0xe9, 0xbf, 0x8a, 0xdc, 0x26, 0x70, 0xcf, 0x99, 0x63, 0x35}, [16]byte{0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77}}, 119 | {[16]byte{0x00, 0x57, 0xae, 0xf9, 0x41, 0x16, 0xef, 0xb8, 0x82, 0xd5, 0x2c, 0x7b, 0xc3, 0x94, 0x6d, 0x3a}, [16]byte{0x00, 0x19, 0x32, 0x2b, 0x64, 0x7d, 0x56, 0x4f, 0xc8, 0xd1, 0xfa, 0xe3, 0xac, 0xb5, 0x9e, 0x87}}, 120 | {[16]byte{0x00, 0x58, 0xb0, 0xe8, 0x7d, 0x25, 0xcd, 0x95, 0xfa, 0xa2, 0x4a, 0x12, 0x87, 0xdf, 0x37, 0x6f}, [16]byte{0x00, 0xe9, 0xcf, 0x26, 0x83, 0x6a, 0x4c, 0xa5, 0x1b, 0xf2, 0xd4, 0x3d, 0x98, 0x71, 0x57, 0xbe}}, 121 | {[16]byte{0x00, 0x59, 0xb2, 0xeb, 0x79, 0x20, 0xcb, 0x92, 0xf2, 0xab, 0x40, 0x19, 0x8b, 0xd2, 0x39, 0x60}, [16]byte{0x00, 0xf9, 0xef, 0x16, 0xc3, 0x3a, 0x2c, 0xd5, 0x9b, 0x62, 0x74, 0x8d, 0x58, 0xa1, 0xb7, 0x4e}}, 122 | {[16]byte{0x00, 0x5a, 0xb4, 0xee, 0x75, 0x2f, 0xc1, 0x9b, 0xea, 0xb0, 0x5e, 0x04, 0x9f, 0xc5, 0x2b, 0x71}, [16]byte{0x00, 0xc9, 0x8f, 0x46, 0x03, 0xca, 0x8c, 0x45, 0x06, 0xcf, 0x89, 0x40, 0x05, 0xcc, 0x8a, 0x43}}, 123 | {[16]byte{0x00, 0x5b, 0xb6, 0xed, 0x71, 0x2a, 0xc7, 0x9c, 0xe2, 0xb9, 0x54, 0x0f, 0x93, 0xc8, 0x25, 0x7e}, [16]byte{0x00, 0xd9, 0xaf, 0x76, 0x43, 0x9a, 0xec, 0x35, 0x86, 0x5f, 0x29, 0xf0, 0xc5, 0x1c, 0x6a, 0xb3}}, 124 | {[16]byte{0x00, 0x5c, 0xb8, 0xe4, 0x6d, 0x31, 0xd5, 0x89, 0xda, 0x86, 0x62, 0x3e, 0xb7, 0xeb, 0x0f, 0x53}, [16]byte{0x00, 0xa9, 0x4f, 0xe6, 0x9e, 0x37, 0xd1, 0x78, 0x21, 0x88, 0x6e, 0xc7, 0xbf, 0x16, 0xf0, 0x59}}, 125 | {[16]byte{0x00, 0x5d, 0xba, 0xe7, 0x69, 0x34, 0xd3, 0x8e, 0xd2, 0x8f, 0x68, 0x35, 0xbb, 0xe6, 0x01, 0x5c}, [16]byte{0x00, 0xb9, 0x6f, 0xd6, 0xde, 0x67, 0xb1, 0x08, 0xa1, 0x18, 0xce, 0x77, 0x7f, 0xc6, 0x10, 0xa9}}, 126 | {[16]byte{0x00, 0x5e, 0xbc, 0xe2, 0x65, 0x3b, 0xd9, 0x87, 0xca, 0x94, 0x76, 0x28, 0xaf, 0xf1, 0x13, 0x4d}, [16]byte{0x00, 0x89, 0x0f, 0x86, 0x1e, 0x97, 0x11, 0x98, 0x3c, 0xb5, 0x33, 0xba, 0x22, 0xab, 0x2d, 0xa4}}, 127 | {[16]byte{0x00, 0x5f, 0xbe, 0xe1, 0x61, 0x3e, 0xdf, 0x80, 0xc2, 0x9d, 0x7c, 0x23, 0xa3, 0xfc, 0x1d, 0x42}, [16]byte{0x00, 0x99, 0x2f, 0xb6, 0x5e, 0xc7, 0x71, 0xe8, 0xbc, 0x25, 0x93, 0x0a, 0xe2, 0x7b, 0xcd, 0x54}}, 128 | {[16]byte{0x00, 0x60, 0xc0, 0xa0, 0x9d, 0xfd, 0x5d, 0x3d, 0x27, 0x47, 0xe7, 0x87, 0xba, 0xda, 0x7a, 0x1a}, [16]byte{0x00, 0x4e, 0x9c, 0xd2, 0x25, 0x6b, 0xb9, 0xf7, 0x4a, 0x04, 0xd6, 0x98, 0x6f, 0x21, 0xf3, 0xbd}}, 129 | {[16]byte{0x00, 0x61, 0xc2, 0xa3, 0x99, 0xf8, 0x5b, 0x3a, 0x2f, 0x4e, 0xed, 0x8c, 0xb6, 0xd7, 0x74, 0x15}, [16]byte{0x00, 0x5e, 0xbc, 0xe2, 0x65, 0x3b, 0xd9, 0x87, 0xca, 0x94, 0x76, 0x28, 0xaf, 0xf1, 0x13, 0x4d}}, 130 | {[16]byte{0x00, 0x62, 0xc4, 0xa6, 0x95, 0xf7, 0x51, 0x33, 0x37, 0x55, 0xf3, 0x91, 0xa2, 0xc0, 0x66, 0x04}, [16]byte{0x00, 0x6e, 0xdc, 0xb2, 0xa5, 0xcb, 0x79, 0x17, 0x57, 0x39, 0x8b, 0xe5, 0xf2, 0x9c, 0x2e, 0x40}}, 131 | {[16]byte{0x00, 0x63, 0xc6, 0xa5, 0x91, 0xf2, 0x57, 0x34, 0x3f, 0x5c, 0xf9, 0x9a, 0xae, 0xcd, 0x68, 0x0b}, [16]byte{0x00, 0x7e, 0xfc, 0x82, 0xe5, 0x9b, 0x19, 0x67, 0xd7, 0xa9, 0x2b, 0x55, 0x32, 0x4c, 0xce, 0xb0}}, 132 | {[16]byte{0x00, 0x64, 0xc8, 0xac, 0x8d, 0xe9, 0x45, 0x21, 0x07, 0x63, 0xcf, 0xab, 0x8a, 0xee, 0x42, 0x26}, [16]byte{0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a}}, 133 | {[16]byte{0x00, 0x65, 0xca, 0xaf, 0x89, 0xec, 0x43, 0x26, 0x0f, 0x6a, 0xc5, 0xa0, 0x86, 0xe3, 0x4c, 0x29}, [16]byte{0x00, 0x1e, 0x3c, 0x22, 0x78, 0x66, 0x44, 0x5a, 0xf0, 0xee, 0xcc, 0xd2, 0x88, 0x96, 0xb4, 0xaa}}, 134 | {[16]byte{0x00, 0x66, 0xcc, 0xaa, 0x85, 0xe3, 0x49, 0x2f, 0x17, 0x71, 0xdb, 0xbd, 0x92, 0xf4, 0x5e, 0x38}, [16]byte{0x00, 0x2e, 0x5c, 0x72, 0xb8, 0x96, 0xe4, 0xca, 0x6d, 0x43, 0x31, 0x1f, 0xd5, 0xfb, 0x89, 0xa7}}, 135 | {[16]byte{0x00, 0x67, 0xce, 0xa9, 0x81, 0xe6, 0x4f, 0x28, 0x1f, 0x78, 0xd1, 0xb6, 0x9e, 0xf9, 0x50, 0x37}, [16]byte{0x00, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0xed, 0xd3, 0x91, 0xaf, 0x15, 0x2b, 0x69, 0x57}}, 136 | {[16]byte{0x00, 0x68, 0xd0, 0xb8, 0xbd, 0xd5, 0x6d, 0x05, 0x67, 0x0f, 0xb7, 0xdf, 0xda, 0xb2, 0x0a, 0x62}, [16]byte{0x00, 0xce, 0x81, 0x4f, 0x1f, 0xd1, 0x9e, 0x50, 0x3e, 0xf0, 0xbf, 0x71, 0x21, 0xef, 0xa0, 0x6e}}, 137 | {[16]byte{0x00, 0x69, 0xd2, 0xbb, 0xb9, 0xd0, 0x6b, 0x02, 0x6f, 0x06, 0xbd, 0xd4, 0xd6, 0xbf, 0x04, 0x6d}, [16]byte{0x00, 0xde, 0xa1, 0x7f, 0x5f, 0x81, 0xfe, 0x20, 0xbe, 0x60, 0x1f, 0xc1, 0xe1, 0x3f, 0x40, 0x9e}}, 138 | {[16]byte{0x00, 0x6a, 0xd4, 0xbe, 0xb5, 0xdf, 0x61, 0x0b, 0x77, 0x1d, 0xa3, 0xc9, 0xc2, 0xa8, 0x16, 0x7c}, [16]byte{0x00, 0xee, 0xc1, 0x2f, 0x9f, 0x71, 0x5e, 0xb0, 0x23, 0xcd, 0xe2, 0x0c, 0xbc, 0x52, 0x7d, 0x93}}, 139 | {[16]byte{0x00, 0x6b, 0xd6, 0xbd, 0xb1, 0xda, 0x67, 0x0c, 0x7f, 0x14, 0xa9, 0xc2, 0xce, 0xa5, 0x18, 0x73}, [16]byte{0x00, 0xfe, 0xe1, 0x1f, 0xdf, 0x21, 0x3e, 0xc0, 0xa3, 0x5d, 0x42, 0xbc, 0x7c, 0x82, 0x9d, 0x63}}, 140 | {[16]byte{0x00, 0x6c, 0xd8, 0xb4, 0xad, 0xc1, 0x75, 0x19, 0x47, 0x2b, 0x9f, 0xf3, 0xea, 0x86, 0x32, 0x5e}, [16]byte{0x00, 0x8e, 0x01, 0x8f, 0x02, 0x8c, 0x03, 0x8d, 0x04, 0x8a, 0x05, 0x8b, 0x06, 0x88, 0x07, 0x89}}, 141 | {[16]byte{0x00, 0x6d, 0xda, 0xb7, 0xa9, 0xc4, 0x73, 0x1e, 0x4f, 0x22, 0x95, 0xf8, 0xe6, 0x8b, 0x3c, 0x51}, [16]byte{0x00, 0x9e, 0x21, 0xbf, 0x42, 0xdc, 0x63, 0xfd, 0x84, 0x1a, 0xa5, 0x3b, 0xc6, 0x58, 0xe7, 0x79}}, 142 | {[16]byte{0x00, 0x6e, 0xdc, 0xb2, 0xa5, 0xcb, 0x79, 0x17, 0x57, 0x39, 0x8b, 0xe5, 0xf2, 0x9c, 0x2e, 0x40}, [16]byte{0x00, 0xae, 0x41, 0xef, 0x82, 0x2c, 0xc3, 0x6d, 0x19, 0xb7, 0x58, 0xf6, 0x9b, 0x35, 0xda, 0x74}}, 143 | {[16]byte{0x00, 0x6f, 0xde, 0xb1, 0xa1, 0xce, 0x7f, 0x10, 0x5f, 0x30, 0x81, 0xee, 0xfe, 0x91, 0x20, 0x4f}, [16]byte{0x00, 0xbe, 0x61, 0xdf, 0xc2, 0x7c, 0xa3, 0x1d, 0x99, 0x27, 0xf8, 0x46, 0x5b, 0xe5, 0x3a, 0x84}}, 144 | {[16]byte{0x00, 0x70, 0xe0, 0x90, 0xdd, 0xad, 0x3d, 0x4d, 0xa7, 0xd7, 0x47, 0x37, 0x7a, 0x0a, 0x9a, 0xea}, [16]byte{0x00, 0x53, 0xa6, 0xf5, 0x51, 0x02, 0xf7, 0xa4, 0xa2, 0xf1, 0x04, 0x57, 0xf3, 0xa0, 0x55, 0x06}}, 145 | {[16]byte{0x00, 0x71, 0xe2, 0x93, 0xd9, 0xa8, 0x3b, 0x4a, 0xaf, 0xde, 0x4d, 0x3c, 0x76, 0x07, 0x94, 0xe5}, [16]byte{0x00, 0x43, 0x86, 0xc5, 0x11, 0x52, 0x97, 0xd4, 0x22, 0x61, 0xa4, 0xe7, 0x33, 0x70, 0xb5, 0xf6}}, 146 | {[16]byte{0x00, 0x72, 0xe4, 0x96, 0xd5, 0xa7, 0x31, 0x43, 0xb7, 0xc5, 0x53, 0x21, 0x62, 0x10, 0x86, 0xf4}, [16]byte{0x00, 0x73, 0xe6, 0x95, 0xd1, 0xa2, 0x37, 0x44, 0xbf, 0xcc, 0x59, 0x2a, 0x6e, 0x1d, 0x88, 0xfb}}, 147 | {[16]byte{0x00, 0x73, 0xe6, 0x95, 0xd1, 0xa2, 0x37, 0x44, 0xbf, 0xcc, 0x59, 0x2a, 0x6e, 0x1d, 0x88, 0xfb}, [16]byte{0x00, 0x63, 0xc6, 0xa5, 0x91, 0xf2, 0x57, 0x34, 0x3f, 0x5c, 0xf9, 0x9a, 0xae, 0xcd, 0x68, 0x0b}}, 148 | {[16]byte{0x00, 0x74, 0xe8, 0x9c, 0xcd, 0xb9, 0x25, 0x51, 0x87, 0xf3, 0x6f, 0x1b, 0x4a, 0x3e, 0xa2, 0xd6}, [16]byte{0x00, 0x13, 0x26, 0x35, 0x4c, 0x5f, 0x6a, 0x79, 0x98, 0x8b, 0xbe, 0xad, 0xd4, 0xc7, 0xf2, 0xe1}}, 149 | {[16]byte{0x00, 0x75, 0xea, 0x9f, 0xc9, 0xbc, 0x23, 0x56, 0x8f, 0xfa, 0x65, 0x10, 0x46, 0x33, 0xac, 0xd9}, [16]byte{0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11}}, 150 | {[16]byte{0x00, 0x76, 0xec, 0x9a, 0xc5, 0xb3, 0x29, 0x5f, 0x97, 0xe1, 0x7b, 0x0d, 0x52, 0x24, 0xbe, 0xc8}, [16]byte{0x00, 0x33, 0x66, 0x55, 0xcc, 0xff, 0xaa, 0x99, 0x85, 0xb6, 0xe3, 0xd0, 0x49, 0x7a, 0x2f, 0x1c}}, 151 | {[16]byte{0x00, 0x77, 0xee, 0x99, 0xc1, 0xb6, 0x2f, 0x58, 0x9f, 0xe8, 0x71, 0x06, 0x5e, 0x29, 0xb0, 0xc7}, [16]byte{0x00, 0x23, 0x46, 0x65, 0x8c, 0xaf, 0xca, 0xe9, 0x05, 0x26, 0x43, 0x60, 0x89, 0xaa, 0xcf, 0xec}}, 152 | {[16]byte{0x00, 0x78, 0xf0, 0x88, 0xfd, 0x85, 0x0d, 0x75, 0xe7, 0x9f, 0x17, 0x6f, 0x1a, 0x62, 0xea, 0x92}, [16]byte{0x00, 0xd3, 0xbb, 0x68, 0x6b, 0xb8, 0xd0, 0x03, 0xd6, 0x05, 0x6d, 0xbe, 0xbd, 0x6e, 0x06, 0xd5}}, 153 | {[16]byte{0x00, 0x79, 0xf2, 0x8b, 0xf9, 0x80, 0x0b, 0x72, 0xef, 0x96, 0x1d, 0x64, 0x16, 0x6f, 0xe4, 0x9d}, [16]byte{0x00, 0xc3, 0x9b, 0x58, 0x2b, 0xe8, 0xb0, 0x73, 0x56, 0x95, 0xcd, 0x0e, 0x7d, 0xbe, 0xe6, 0x25}}, 154 | {[16]byte{0x00, 0x7a, 0xf4, 0x8e, 0xf5, 0x8f, 0x01, 0x7b, 0xf7, 0x8d, 0x03, 0x79, 0x02, 0x78, 0xf6, 0x8c}, [16]byte{0x00, 0xf3, 0xfb, 0x08, 0xeb, 0x18, 0x10, 0xe3, 0xcb, 0x38, 0x30, 0xc3, 0x20, 0xd3, 0xdb, 0x28}}, 155 | {[16]byte{0x00, 0x7b, 0xf6, 0x8d, 0xf1, 0x8a, 0x07, 0x7c, 0xff, 0x84, 0x09, 0x72, 0x0e, 0x75, 0xf8, 0x83}, [16]byte{0x00, 0xe3, 0xdb, 0x38, 0xab, 0x48, 0x70, 0x93, 0x4b, 0xa8, 0x90, 0x73, 0xe0, 0x03, 0x3b, 0xd8}}, 156 | {[16]byte{0x00, 0x7c, 0xf8, 0x84, 0xed, 0x91, 0x15, 0x69, 0xc7, 0xbb, 0x3f, 0x43, 0x2a, 0x56, 0xd2, 0xae}, [16]byte{0x00, 0x93, 0x3b, 0xa8, 0x76, 0xe5, 0x4d, 0xde, 0xec, 0x7f, 0xd7, 0x44, 0x9a, 0x09, 0xa1, 0x32}}, 157 | {[16]byte{0x00, 0x7d, 0xfa, 0x87, 0xe9, 0x94, 0x13, 0x6e, 0xcf, 0xb2, 0x35, 0x48, 0x26, 0x5b, 0xdc, 0xa1}, [16]byte{0x00, 0x83, 0x1b, 0x98, 0x36, 0xb5, 0x2d, 0xae, 0x6c, 0xef, 0x77, 0xf4, 0x5a, 0xd9, 0x41, 0xc2}}, 158 | {[16]byte{0x00, 0x7e, 0xfc, 0x82, 0xe5, 0x9b, 0x19, 0x67, 0xd7, 0xa9, 0x2b, 0x55, 0x32, 0x4c, 0xce, 0xb0}, [16]byte{0x00, 0xb3, 0x7b, 0xc8, 0xf6, 0x45, 0x8d, 0x3e, 0xf1, 0x42, 0x8a, 0x39, 0x07, 0xb4, 0x7c, 0xcf}}, 159 | {[16]byte{0x00, 0x7f, 0xfe, 0x81, 0xe1, 0x9e, 0x1f, 0x60, 0xdf, 0xa0, 0x21, 0x5e, 0x3e, 0x41, 0xc0, 0xbf}, [16]byte{0x00, 0xa3, 0x5b, 0xf8, 0xb6, 0x15, 0xed, 0x4e, 0x71, 0xd2, 0x2a, 0x89, 0xc7, 0x64, 0x9c, 0x3f}}, 160 | {[16]byte{0x00, 0x80, 0x1d, 0x9d, 0x3a, 0xba, 0x27, 0xa7, 0x74, 0xf4, 0x69, 0xe9, 0x4e, 0xce, 0x53, 0xd3}, [16]byte{0x00, 0xe8, 0xcd, 0x25, 0x87, 0x6f, 0x4a, 0xa2, 0x13, 0xfb, 0xde, 0x36, 0x94, 0x7c, 0x59, 0xb1}}, 161 | {[16]byte{0x00, 0x81, 0x1f, 0x9e, 0x3e, 0xbf, 0x21, 0xa0, 0x7c, 0xfd, 0x63, 0xe2, 0x42, 0xc3, 0x5d, 0xdc}, [16]byte{0x00, 0xf8, 0xed, 0x15, 0xc7, 0x3f, 0x2a, 0xd2, 0x93, 0x6b, 0x7e, 0x86, 0x54, 0xac, 0xb9, 0x41}}, 162 | {[16]byte{0x00, 0x82, 0x19, 0x9b, 0x32, 0xb0, 0x2b, 0xa9, 0x64, 0xe6, 0x7d, 0xff, 0x56, 0xd4, 0x4f, 0xcd}, [16]byte{0x00, 0xc8, 0x8d, 0x45, 0x07, 0xcf, 0x8a, 0x42, 0x0e, 0xc6, 0x83, 0x4b, 0x09, 0xc1, 0x84, 0x4c}}, 163 | {[16]byte{0x00, 0x83, 0x1b, 0x98, 0x36, 0xb5, 0x2d, 0xae, 0x6c, 0xef, 0x77, 0xf4, 0x5a, 0xd9, 0x41, 0xc2}, [16]byte{0x00, 0xd8, 0xad, 0x75, 0x47, 0x9f, 0xea, 0x32, 0x8e, 0x56, 0x23, 0xfb, 0xc9, 0x11, 0x64, 0xbc}}, 164 | {[16]byte{0x00, 0x84, 0x15, 0x91, 0x2a, 0xae, 0x3f, 0xbb, 0x54, 0xd0, 0x41, 0xc5, 0x7e, 0xfa, 0x6b, 0xef}, [16]byte{0x00, 0xa8, 0x4d, 0xe5, 0x9a, 0x32, 0xd7, 0x7f, 0x29, 0x81, 0x64, 0xcc, 0xb3, 0x1b, 0xfe, 0x56}}, 165 | {[16]byte{0x00, 0x85, 0x17, 0x92, 0x2e, 0xab, 0x39, 0xbc, 0x5c, 0xd9, 0x4b, 0xce, 0x72, 0xf7, 0x65, 0xe0}, [16]byte{0x00, 0xb8, 0x6d, 0xd5, 0xda, 0x62, 0xb7, 0x0f, 0xa9, 0x11, 0xc4, 0x7c, 0x73, 0xcb, 0x1e, 0xa6}}, 166 | {[16]byte{0x00, 0x86, 0x11, 0x97, 0x22, 0xa4, 0x33, 0xb5, 0x44, 0xc2, 0x55, 0xd3, 0x66, 0xe0, 0x77, 0xf1}, [16]byte{0x00, 0x88, 0x0d, 0x85, 0x1a, 0x92, 0x17, 0x9f, 0x34, 0xbc, 0x39, 0xb1, 0x2e, 0xa6, 0x23, 0xab}}, 167 | {[16]byte{0x00, 0x87, 0x13, 0x94, 0x26, 0xa1, 0x35, 0xb2, 0x4c, 0xcb, 0x5f, 0xd8, 0x6a, 0xed, 0x79, 0xfe}, [16]byte{0x00, 0x98, 0x2d, 0xb5, 0x5a, 0xc2, 0x77, 0xef, 0xb4, 0x2c, 0x99, 0x01, 0xee, 0x76, 0xc3, 0x5b}}, 168 | {[16]byte{0x00, 0x88, 0x0d, 0x85, 0x1a, 0x92, 0x17, 0x9f, 0x34, 0xbc, 0x39, 0xb1, 0x2e, 0xa6, 0x23, 0xab}, [16]byte{0x00, 0x68, 0xd0, 0xb8, 0xbd, 0xd5, 0x6d, 0x05, 0x67, 0x0f, 0xb7, 0xdf, 0xda, 0xb2, 0x0a, 0x62}}, 169 | {[16]byte{0x00, 0x89, 0x0f, 0x86, 0x1e, 0x97, 0x11, 0x98, 0x3c, 0xb5, 0x33, 0xba, 0x22, 0xab, 0x2d, 0xa4}, [16]byte{0x00, 0x78, 0xf0, 0x88, 0xfd, 0x85, 0x0d, 0x75, 0xe7, 0x9f, 0x17, 0x6f, 0x1a, 0x62, 0xea, 0x92}}, 170 | {[16]byte{0x00, 0x8a, 0x09, 0x83, 0x12, 0x98, 0x1b, 0x91, 0x24, 0xae, 0x2d, 0xa7, 0x36, 0xbc, 0x3f, 0xb5}, [16]byte{0x00, 0x48, 0x90, 0xd8, 0x3d, 0x75, 0xad, 0xe5, 0x7a, 0x32, 0xea, 0xa2, 0x47, 0x0f, 0xd7, 0x9f}}, 171 | {[16]byte{0x00, 0x8b, 0x0b, 0x80, 0x16, 0x9d, 0x1d, 0x96, 0x2c, 0xa7, 0x27, 0xac, 0x3a, 0xb1, 0x31, 0xba}, [16]byte{0x00, 0x58, 0xb0, 0xe8, 0x7d, 0x25, 0xcd, 0x95, 0xfa, 0xa2, 0x4a, 0x12, 0x87, 0xdf, 0x37, 0x6f}}, 172 | {[16]byte{0x00, 0x8c, 0x05, 0x89, 0x0a, 0x86, 0x0f, 0x83, 0x14, 0x98, 0x11, 0x9d, 0x1e, 0x92, 0x1b, 0x97}, [16]byte{0x00, 0x28, 0x50, 0x78, 0xa0, 0x88, 0xf0, 0xd8, 0x5d, 0x75, 0x0d, 0x25, 0xfd, 0xd5, 0xad, 0x85}}, 173 | {[16]byte{0x00, 0x8d, 0x07, 0x8a, 0x0e, 0x83, 0x09, 0x84, 0x1c, 0x91, 0x1b, 0x96, 0x12, 0x9f, 0x15, 0x98}, [16]byte{0x00, 0x38, 0x70, 0x48, 0xe0, 0xd8, 0x90, 0xa8, 0xdd, 0xe5, 0xad, 0x95, 0x3d, 0x05, 0x4d, 0x75}}, 174 | {[16]byte{0x00, 0x8e, 0x01, 0x8f, 0x02, 0x8c, 0x03, 0x8d, 0x04, 0x8a, 0x05, 0x8b, 0x06, 0x88, 0x07, 0x89}, [16]byte{0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78}}, 175 | {[16]byte{0x00, 0x8f, 0x03, 0x8c, 0x06, 0x89, 0x05, 0x8a, 0x0c, 0x83, 0x0f, 0x80, 0x0a, 0x85, 0x09, 0x86}, [16]byte{0x00, 0x18, 0x30, 0x28, 0x60, 0x78, 0x50, 0x48, 0xc0, 0xd8, 0xf0, 0xe8, 0xa0, 0xb8, 0x90, 0x88}}, 176 | {[16]byte{0x00, 0x90, 0x3d, 0xad, 0x7a, 0xea, 0x47, 0xd7, 0xf4, 0x64, 0xc9, 0x59, 0x8e, 0x1e, 0xb3, 0x23}, [16]byte{0x00, 0xf5, 0xf7, 0x02, 0xf3, 0x06, 0x04, 0xf1, 0xfb, 0x0e, 0x0c, 0xf9, 0x08, 0xfd, 0xff, 0x0a}}, 177 | {[16]byte{0x00, 0x91, 0x3f, 0xae, 0x7e, 0xef, 0x41, 0xd0, 0xfc, 0x6d, 0xc3, 0x52, 0x82, 0x13, 0xbd, 0x2c}, [16]byte{0x00, 0xe5, 0xd7, 0x32, 0xb3, 0x56, 0x64, 0x81, 0x7b, 0x9e, 0xac, 0x49, 0xc8, 0x2d, 0x1f, 0xfa}}, 178 | {[16]byte{0x00, 0x92, 0x39, 0xab, 0x72, 0xe0, 0x4b, 0xd9, 0xe4, 0x76, 0xdd, 0x4f, 0x96, 0x04, 0xaf, 0x3d}, [16]byte{0x00, 0xd5, 0xb7, 0x62, 0x73, 0xa6, 0xc4, 0x11, 0xe6, 0x33, 0x51, 0x84, 0x95, 0x40, 0x22, 0xf7}}, 179 | {[16]byte{0x00, 0x93, 0x3b, 0xa8, 0x76, 0xe5, 0x4d, 0xde, 0xec, 0x7f, 0xd7, 0x44, 0x9a, 0x09, 0xa1, 0x32}, [16]byte{0x00, 0xc5, 0x97, 0x52, 0x33, 0xf6, 0xa4, 0x61, 0x66, 0xa3, 0xf1, 0x34, 0x55, 0x90, 0xc2, 0x07}}, 180 | {[16]byte{0x00, 0x94, 0x35, 0xa1, 0x6a, 0xfe, 0x5f, 0xcb, 0xd4, 0x40, 0xe1, 0x75, 0xbe, 0x2a, 0x8b, 0x1f}, [16]byte{0x00, 0xb5, 0x77, 0xc2, 0xee, 0x5b, 0x99, 0x2c, 0xc1, 0x74, 0xb6, 0x03, 0x2f, 0x9a, 0x58, 0xed}}, 181 | {[16]byte{0x00, 0x95, 0x37, 0xa2, 0x6e, 0xfb, 0x59, 0xcc, 0xdc, 0x49, 0xeb, 0x7e, 0xb2, 0x27, 0x85, 0x10}, [16]byte{0x00, 0xa5, 0x57, 0xf2, 0xae, 0x0b, 0xf9, 0x5c, 0x41, 0xe4, 0x16, 0xb3, 0xef, 0x4a, 0xb8, 0x1d}}, 182 | {[16]byte{0x00, 0x96, 0x31, 0xa7, 0x62, 0xf4, 0x53, 0xc5, 0xc4, 0x52, 0xf5, 0x63, 0xa6, 0x30, 0x97, 0x01}, [16]byte{0x00, 0x95, 0x37, 0xa2, 0x6e, 0xfb, 0x59, 0xcc, 0xdc, 0x49, 0xeb, 0x7e, 0xb2, 0x27, 0x85, 0x10}}, 183 | {[16]byte{0x00, 0x97, 0x33, 0xa4, 0x66, 0xf1, 0x55, 0xc2, 0xcc, 0x5b, 0xff, 0x68, 0xaa, 0x3d, 0x99, 0x0e}, [16]byte{0x00, 0x85, 0x17, 0x92, 0x2e, 0xab, 0x39, 0xbc, 0x5c, 0xd9, 0x4b, 0xce, 0x72, 0xf7, 0x65, 0xe0}}, 184 | {[16]byte{0x00, 0x98, 0x2d, 0xb5, 0x5a, 0xc2, 0x77, 0xef, 0xb4, 0x2c, 0x99, 0x01, 0xee, 0x76, 0xc3, 0x5b}, [16]byte{0x00, 0x75, 0xea, 0x9f, 0xc9, 0xbc, 0x23, 0x56, 0x8f, 0xfa, 0x65, 0x10, 0x46, 0x33, 0xac, 0xd9}}, 185 | {[16]byte{0x00, 0x99, 0x2f, 0xb6, 0x5e, 0xc7, 0x71, 0xe8, 0xbc, 0x25, 0x93, 0x0a, 0xe2, 0x7b, 0xcd, 0x54}, [16]byte{0x00, 0x65, 0xca, 0xaf, 0x89, 0xec, 0x43, 0x26, 0x0f, 0x6a, 0xc5, 0xa0, 0x86, 0xe3, 0x4c, 0x29}}, 186 | {[16]byte{0x00, 0x9a, 0x29, 0xb3, 0x52, 0xc8, 0x7b, 0xe1, 0xa4, 0x3e, 0x8d, 0x17, 0xf6, 0x6c, 0xdf, 0x45}, [16]byte{0x00, 0x55, 0xaa, 0xff, 0x49, 0x1c, 0xe3, 0xb6, 0x92, 0xc7, 0x38, 0x6d, 0xdb, 0x8e, 0x71, 0x24}}, 187 | {[16]byte{0x00, 0x9b, 0x2b, 0xb0, 0x56, 0xcd, 0x7d, 0xe6, 0xac, 0x37, 0x87, 0x1c, 0xfa, 0x61, 0xd1, 0x4a}, [16]byte{0x00, 0x45, 0x8a, 0xcf, 0x09, 0x4c, 0x83, 0xc6, 0x12, 0x57, 0x98, 0xdd, 0x1b, 0x5e, 0x91, 0xd4}}, 188 | {[16]byte{0x00, 0x9c, 0x25, 0xb9, 0x4a, 0xd6, 0x6f, 0xf3, 0x94, 0x08, 0xb1, 0x2d, 0xde, 0x42, 0xfb, 0x67}, [16]byte{0x00, 0x35, 0x6a, 0x5f, 0xd4, 0xe1, 0xbe, 0x8b, 0xb5, 0x80, 0xdf, 0xea, 0x61, 0x54, 0x0b, 0x3e}}, 189 | {[16]byte{0x00, 0x9d, 0x27, 0xba, 0x4e, 0xd3, 0x69, 0xf4, 0x9c, 0x01, 0xbb, 0x26, 0xd2, 0x4f, 0xf5, 0x68}, [16]byte{0x00, 0x25, 0x4a, 0x6f, 0x94, 0xb1, 0xde, 0xfb, 0x35, 0x10, 0x7f, 0x5a, 0xa1, 0x84, 0xeb, 0xce}}, 190 | {[16]byte{0x00, 0x9e, 0x21, 0xbf, 0x42, 0xdc, 0x63, 0xfd, 0x84, 0x1a, 0xa5, 0x3b, 0xc6, 0x58, 0xe7, 0x79}, [16]byte{0x00, 0x15, 0x2a, 0x3f, 0x54, 0x41, 0x7e, 0x6b, 0xa8, 0xbd, 0x82, 0x97, 0xfc, 0xe9, 0xd6, 0xc3}}, 191 | {[16]byte{0x00, 0x9f, 0x23, 0xbc, 0x46, 0xd9, 0x65, 0xfa, 0x8c, 0x13, 0xaf, 0x30, 0xca, 0x55, 0xe9, 0x76}, [16]byte{0x00, 0x05, 0x0a, 0x0f, 0x14, 0x11, 0x1e, 0x1b, 0x28, 0x2d, 0x22, 0x27, 0x3c, 0x39, 0x36, 0x33}}, 192 | {[16]byte{0x00, 0xa0, 0x5d, 0xfd, 0xba, 0x1a, 0xe7, 0x47, 0x69, 0xc9, 0x34, 0x94, 0xd3, 0x73, 0x8e, 0x2e}, [16]byte{0x00, 0xd2, 0xb9, 0x6b, 0x6f, 0xbd, 0xd6, 0x04, 0xde, 0x0c, 0x67, 0xb5, 0xb1, 0x63, 0x08, 0xda}}, 193 | {[16]byte{0x00, 0xa1, 0x5f, 0xfe, 0xbe, 0x1f, 0xe1, 0x40, 0x61, 0xc0, 0x3e, 0x9f, 0xdf, 0x7e, 0x80, 0x21}, [16]byte{0x00, 0xc2, 0x99, 0x5b, 0x2f, 0xed, 0xb6, 0x74, 0x5e, 0x9c, 0xc7, 0x05, 0x71, 0xb3, 0xe8, 0x2a}}, 194 | {[16]byte{0x00, 0xa2, 0x59, 0xfb, 0xb2, 0x10, 0xeb, 0x49, 0x79, 0xdb, 0x20, 0x82, 0xcb, 0x69, 0x92, 0x30}, [16]byte{0x00, 0xf2, 0xf9, 0x0b, 0xef, 0x1d, 0x16, 0xe4, 0xc3, 0x31, 0x3a, 0xc8, 0x2c, 0xde, 0xd5, 0x27}}, 195 | {[16]byte{0x00, 0xa3, 0x5b, 0xf8, 0xb6, 0x15, 0xed, 0x4e, 0x71, 0xd2, 0x2a, 0x89, 0xc7, 0x64, 0x9c, 0x3f}, [16]byte{0x00, 0xe2, 0xd9, 0x3b, 0xaf, 0x4d, 0x76, 0x94, 0x43, 0xa1, 0x9a, 0x78, 0xec, 0x0e, 0x35, 0xd7}}, 196 | {[16]byte{0x00, 0xa4, 0x55, 0xf1, 0xaa, 0x0e, 0xff, 0x5b, 0x49, 0xed, 0x1c, 0xb8, 0xe3, 0x47, 0xb6, 0x12}, [16]byte{0x00, 0x92, 0x39, 0xab, 0x72, 0xe0, 0x4b, 0xd9, 0xe4, 0x76, 0xdd, 0x4f, 0x96, 0x04, 0xaf, 0x3d}}, 197 | {[16]byte{0x00, 0xa5, 0x57, 0xf2, 0xae, 0x0b, 0xf9, 0x5c, 0x41, 0xe4, 0x16, 0xb3, 0xef, 0x4a, 0xb8, 0x1d}, [16]byte{0x00, 0x82, 0x19, 0x9b, 0x32, 0xb0, 0x2b, 0xa9, 0x64, 0xe6, 0x7d, 0xff, 0x56, 0xd4, 0x4f, 0xcd}}, 198 | {[16]byte{0x00, 0xa6, 0x51, 0xf7, 0xa2, 0x04, 0xf3, 0x55, 0x59, 0xff, 0x08, 0xae, 0xfb, 0x5d, 0xaa, 0x0c}, [16]byte{0x00, 0xb2, 0x79, 0xcb, 0xf2, 0x40, 0x8b, 0x39, 0xf9, 0x4b, 0x80, 0x32, 0x0b, 0xb9, 0x72, 0xc0}}, 199 | {[16]byte{0x00, 0xa7, 0x53, 0xf4, 0xa6, 0x01, 0xf5, 0x52, 0x51, 0xf6, 0x02, 0xa5, 0xf7, 0x50, 0xa4, 0x03}, [16]byte{0x00, 0xa2, 0x59, 0xfb, 0xb2, 0x10, 0xeb, 0x49, 0x79, 0xdb, 0x20, 0x82, 0xcb, 0x69, 0x92, 0x30}}, 200 | {[16]byte{0x00, 0xa8, 0x4d, 0xe5, 0x9a, 0x32, 0xd7, 0x7f, 0x29, 0x81, 0x64, 0xcc, 0xb3, 0x1b, 0xfe, 0x56}, [16]byte{0x00, 0x52, 0xa4, 0xf6, 0x55, 0x07, 0xf1, 0xa3, 0xaa, 0xf8, 0x0e, 0x5c, 0xff, 0xad, 0x5b, 0x09}}, 201 | {[16]byte{0x00, 0xa9, 0x4f, 0xe6, 0x9e, 0x37, 0xd1, 0x78, 0x21, 0x88, 0x6e, 0xc7, 0xbf, 0x16, 0xf0, 0x59}, [16]byte{0x00, 0x42, 0x84, 0xc6, 0x15, 0x57, 0x91, 0xd3, 0x2a, 0x68, 0xae, 0xec, 0x3f, 0x7d, 0xbb, 0xf9}}, 202 | {[16]byte{0x00, 0xaa, 0x49, 0xe3, 0x92, 0x38, 0xdb, 0x71, 0x39, 0x93, 0x70, 0xda, 0xab, 0x01, 0xe2, 0x48}, [16]byte{0x00, 0x72, 0xe4, 0x96, 0xd5, 0xa7, 0x31, 0x43, 0xb7, 0xc5, 0x53, 0x21, 0x62, 0x10, 0x86, 0xf4}}, 203 | {[16]byte{0x00, 0xab, 0x4b, 0xe0, 0x96, 0x3d, 0xdd, 0x76, 0x31, 0x9a, 0x7a, 0xd1, 0xa7, 0x0c, 0xec, 0x47}, [16]byte{0x00, 0x62, 0xc4, 0xa6, 0x95, 0xf7, 0x51, 0x33, 0x37, 0x55, 0xf3, 0x91, 0xa2, 0xc0, 0x66, 0x04}}, 204 | {[16]byte{0x00, 0xac, 0x45, 0xe9, 0x8a, 0x26, 0xcf, 0x63, 0x09, 0xa5, 0x4c, 0xe0, 0x83, 0x2f, 0xc6, 0x6a}, [16]byte{0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee}}, 205 | {[16]byte{0x00, 0xad, 0x47, 0xea, 0x8e, 0x23, 0xc9, 0x64, 0x01, 0xac, 0x46, 0xeb, 0x8f, 0x22, 0xc8, 0x65}, [16]byte{0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e}}, 206 | {[16]byte{0x00, 0xae, 0x41, 0xef, 0x82, 0x2c, 0xc3, 0x6d, 0x19, 0xb7, 0x58, 0xf6, 0x9b, 0x35, 0xda, 0x74}, [16]byte{0x00, 0x32, 0x64, 0x56, 0xc8, 0xfa, 0xac, 0x9e, 0x8d, 0xbf, 0xe9, 0xdb, 0x45, 0x77, 0x21, 0x13}}, 207 | {[16]byte{0x00, 0xaf, 0x43, 0xec, 0x86, 0x29, 0xc5, 0x6a, 0x11, 0xbe, 0x52, 0xfd, 0x97, 0x38, 0xd4, 0x7b}, [16]byte{0x00, 0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0x0d, 0x2f, 0x49, 0x6b, 0x85, 0xa7, 0xc1, 0xe3}}, 208 | {[16]byte{0x00, 0xb0, 0x7d, 0xcd, 0xfa, 0x4a, 0x87, 0x37, 0xe9, 0x59, 0x94, 0x24, 0x13, 0xa3, 0x6e, 0xde}, [16]byte{0x00, 0xcf, 0x83, 0x4c, 0x1b, 0xd4, 0x98, 0x57, 0x36, 0xf9, 0xb5, 0x7a, 0x2d, 0xe2, 0xae, 0x61}}, 209 | {[16]byte{0x00, 0xb1, 0x7f, 0xce, 0xfe, 0x4f, 0x81, 0x30, 0xe1, 0x50, 0x9e, 0x2f, 0x1f, 0xae, 0x60, 0xd1}, [16]byte{0x00, 0xdf, 0xa3, 0x7c, 0x5b, 0x84, 0xf8, 0x27, 0xb6, 0x69, 0x15, 0xca, 0xed, 0x32, 0x4e, 0x91}}, 210 | {[16]byte{0x00, 0xb2, 0x79, 0xcb, 0xf2, 0x40, 0x8b, 0x39, 0xf9, 0x4b, 0x80, 0x32, 0x0b, 0xb9, 0x72, 0xc0}, [16]byte{0x00, 0xef, 0xc3, 0x2c, 0x9b, 0x74, 0x58, 0xb7, 0x2b, 0xc4, 0xe8, 0x07, 0xb0, 0x5f, 0x73, 0x9c}}, 211 | {[16]byte{0x00, 0xb3, 0x7b, 0xc8, 0xf6, 0x45, 0x8d, 0x3e, 0xf1, 0x42, 0x8a, 0x39, 0x07, 0xb4, 0x7c, 0xcf}, [16]byte{0x00, 0xff, 0xe3, 0x1c, 0xdb, 0x24, 0x38, 0xc7, 0xab, 0x54, 0x48, 0xb7, 0x70, 0x8f, 0x93, 0x6c}}, 212 | {[16]byte{0x00, 0xb4, 0x75, 0xc1, 0xea, 0x5e, 0x9f, 0x2b, 0xc9, 0x7d, 0xbc, 0x08, 0x23, 0x97, 0x56, 0xe2}, [16]byte{0x00, 0x8f, 0x03, 0x8c, 0x06, 0x89, 0x05, 0x8a, 0x0c, 0x83, 0x0f, 0x80, 0x0a, 0x85, 0x09, 0x86}}, 213 | {[16]byte{0x00, 0xb5, 0x77, 0xc2, 0xee, 0x5b, 0x99, 0x2c, 0xc1, 0x74, 0xb6, 0x03, 0x2f, 0x9a, 0x58, 0xed}, [16]byte{0x00, 0x9f, 0x23, 0xbc, 0x46, 0xd9, 0x65, 0xfa, 0x8c, 0x13, 0xaf, 0x30, 0xca, 0x55, 0xe9, 0x76}}, 214 | {[16]byte{0x00, 0xb6, 0x71, 0xc7, 0xe2, 0x54, 0x93, 0x25, 0xd9, 0x6f, 0xa8, 0x1e, 0x3b, 0x8d, 0x4a, 0xfc}, [16]byte{0x00, 0xaf, 0x43, 0xec, 0x86, 0x29, 0xc5, 0x6a, 0x11, 0xbe, 0x52, 0xfd, 0x97, 0x38, 0xd4, 0x7b}}, 215 | {[16]byte{0x00, 0xb7, 0x73, 0xc4, 0xe6, 0x51, 0x95, 0x22, 0xd1, 0x66, 0xa2, 0x15, 0x37, 0x80, 0x44, 0xf3}, [16]byte{0x00, 0xbf, 0x63, 0xdc, 0xc6, 0x79, 0xa5, 0x1a, 0x91, 0x2e, 0xf2, 0x4d, 0x57, 0xe8, 0x34, 0x8b}}, 216 | {[16]byte{0x00, 0xb8, 0x6d, 0xd5, 0xda, 0x62, 0xb7, 0x0f, 0xa9, 0x11, 0xc4, 0x7c, 0x73, 0xcb, 0x1e, 0xa6}, [16]byte{0x00, 0x4f, 0x9e, 0xd1, 0x21, 0x6e, 0xbf, 0xf0, 0x42, 0x0d, 0xdc, 0x93, 0x63, 0x2c, 0xfd, 0xb2}}, 217 | {[16]byte{0x00, 0xb9, 0x6f, 0xd6, 0xde, 0x67, 0xb1, 0x08, 0xa1, 0x18, 0xce, 0x77, 0x7f, 0xc6, 0x10, 0xa9}, [16]byte{0x00, 0x5f, 0xbe, 0xe1, 0x61, 0x3e, 0xdf, 0x80, 0xc2, 0x9d, 0x7c, 0x23, 0xa3, 0xfc, 0x1d, 0x42}}, 218 | {[16]byte{0x00, 0xba, 0x69, 0xd3, 0xd2, 0x68, 0xbb, 0x01, 0xb9, 0x03, 0xd0, 0x6a, 0x6b, 0xd1, 0x02, 0xb8}, [16]byte{0x00, 0x6f, 0xde, 0xb1, 0xa1, 0xce, 0x7f, 0x10, 0x5f, 0x30, 0x81, 0xee, 0xfe, 0x91, 0x20, 0x4f}}, 219 | {[16]byte{0x00, 0xbb, 0x6b, 0xd0, 0xd6, 0x6d, 0xbd, 0x06, 0xb1, 0x0a, 0xda, 0x61, 0x67, 0xdc, 0x0c, 0xb7}, [16]byte{0x00, 0x7f, 0xfe, 0x81, 0xe1, 0x9e, 0x1f, 0x60, 0xdf, 0xa0, 0x21, 0x5e, 0x3e, 0x41, 0xc0, 0xbf}}, 220 | {[16]byte{0x00, 0xbc, 0x65, 0xd9, 0xca, 0x76, 0xaf, 0x13, 0x89, 0x35, 0xec, 0x50, 0x43, 0xff, 0x26, 0x9a}, [16]byte{0x00, 0x0f, 0x1e, 0x11, 0x3c, 0x33, 0x22, 0x2d, 0x78, 0x77, 0x66, 0x69, 0x44, 0x4b, 0x5a, 0x55}}, 221 | {[16]byte{0x00, 0xbd, 0x67, 0xda, 0xce, 0x73, 0xa9, 0x14, 0x81, 0x3c, 0xe6, 0x5b, 0x4f, 0xf2, 0x28, 0x95}, [16]byte{0x00, 0x1f, 0x3e, 0x21, 0x7c, 0x63, 0x42, 0x5d, 0xf8, 0xe7, 0xc6, 0xd9, 0x84, 0x9b, 0xba, 0xa5}}, 222 | {[16]byte{0x00, 0xbe, 0x61, 0xdf, 0xc2, 0x7c, 0xa3, 0x1d, 0x99, 0x27, 0xf8, 0x46, 0x5b, 0xe5, 0x3a, 0x84}, [16]byte{0x00, 0x2f, 0x5e, 0x71, 0xbc, 0x93, 0xe2, 0xcd, 0x65, 0x4a, 0x3b, 0x14, 0xd9, 0xf6, 0x87, 0xa8}}, 223 | {[16]byte{0x00, 0xbf, 0x63, 0xdc, 0xc6, 0x79, 0xa5, 0x1a, 0x91, 0x2e, 0xf2, 0x4d, 0x57, 0xe8, 0x34, 0x8b}, [16]byte{0x00, 0x3f, 0x7e, 0x41, 0xfc, 0xc3, 0x82, 0xbd, 0xe5, 0xda, 0x9b, 0xa4, 0x19, 0x26, 0x67, 0x58}}, 224 | {[16]byte{0x00, 0xc0, 0x9d, 0x5d, 0x27, 0xe7, 0xba, 0x7a, 0x4e, 0x8e, 0xd3, 0x13, 0x69, 0xa9, 0xf4, 0x34}, [16]byte{0x00, 0x9c, 0x25, 0xb9, 0x4a, 0xd6, 0x6f, 0xf3, 0x94, 0x08, 0xb1, 0x2d, 0xde, 0x42, 0xfb, 0x67}}, 225 | {[16]byte{0x00, 0xc1, 0x9f, 0x5e, 0x23, 0xe2, 0xbc, 0x7d, 0x46, 0x87, 0xd9, 0x18, 0x65, 0xa4, 0xfa, 0x3b}, [16]byte{0x00, 0x8c, 0x05, 0x89, 0x0a, 0x86, 0x0f, 0x83, 0x14, 0x98, 0x11, 0x9d, 0x1e, 0x92, 0x1b, 0x97}}, 226 | {[16]byte{0x00, 0xc2, 0x99, 0x5b, 0x2f, 0xed, 0xb6, 0x74, 0x5e, 0x9c, 0xc7, 0x05, 0x71, 0xb3, 0xe8, 0x2a}, [16]byte{0x00, 0xbc, 0x65, 0xd9, 0xca, 0x76, 0xaf, 0x13, 0x89, 0x35, 0xec, 0x50, 0x43, 0xff, 0x26, 0x9a}}, 227 | {[16]byte{0x00, 0xc3, 0x9b, 0x58, 0x2b, 0xe8, 0xb0, 0x73, 0x56, 0x95, 0xcd, 0x0e, 0x7d, 0xbe, 0xe6, 0x25}, [16]byte{0x00, 0xac, 0x45, 0xe9, 0x8a, 0x26, 0xcf, 0x63, 0x09, 0xa5, 0x4c, 0xe0, 0x83, 0x2f, 0xc6, 0x6a}}, 228 | {[16]byte{0x00, 0xc4, 0x95, 0x51, 0x37, 0xf3, 0xa2, 0x66, 0x6e, 0xaa, 0xfb, 0x3f, 0x59, 0x9d, 0xcc, 0x08}, [16]byte{0x00, 0xdc, 0xa5, 0x79, 0x57, 0x8b, 0xf2, 0x2e, 0xae, 0x72, 0x0b, 0xd7, 0xf9, 0x25, 0x5c, 0x80}}, 229 | {[16]byte{0x00, 0xc5, 0x97, 0x52, 0x33, 0xf6, 0xa4, 0x61, 0x66, 0xa3, 0xf1, 0x34, 0x55, 0x90, 0xc2, 0x07}, [16]byte{0x00, 0xcc, 0x85, 0x49, 0x17, 0xdb, 0x92, 0x5e, 0x2e, 0xe2, 0xab, 0x67, 0x39, 0xf5, 0xbc, 0x70}}, 230 | {[16]byte{0x00, 0xc6, 0x91, 0x57, 0x3f, 0xf9, 0xae, 0x68, 0x7e, 0xb8, 0xef, 0x29, 0x41, 0x87, 0xd0, 0x16}, [16]byte{0x00, 0xfc, 0xe5, 0x19, 0xd7, 0x2b, 0x32, 0xce, 0xb3, 0x4f, 0x56, 0xaa, 0x64, 0x98, 0x81, 0x7d}}, 231 | {[16]byte{0x00, 0xc7, 0x93, 0x54, 0x3b, 0xfc, 0xa8, 0x6f, 0x76, 0xb1, 0xe5, 0x22, 0x4d, 0x8a, 0xde, 0x19}, [16]byte{0x00, 0xec, 0xc5, 0x29, 0x97, 0x7b, 0x52, 0xbe, 0x33, 0xdf, 0xf6, 0x1a, 0xa4, 0x48, 0x61, 0x8d}}, 232 | {[16]byte{0x00, 0xc8, 0x8d, 0x45, 0x07, 0xcf, 0x8a, 0x42, 0x0e, 0xc6, 0x83, 0x4b, 0x09, 0xc1, 0x84, 0x4c}, [16]byte{0x00, 0x1c, 0x38, 0x24, 0x70, 0x6c, 0x48, 0x54, 0xe0, 0xfc, 0xd8, 0xc4, 0x90, 0x8c, 0xa8, 0xb4}}, 233 | {[16]byte{0x00, 0xc9, 0x8f, 0x46, 0x03, 0xca, 0x8c, 0x45, 0x06, 0xcf, 0x89, 0x40, 0x05, 0xcc, 0x8a, 0x43}, [16]byte{0x00, 0x0c, 0x18, 0x14, 0x30, 0x3c, 0x28, 0x24, 0x60, 0x6c, 0x78, 0x74, 0x50, 0x5c, 0x48, 0x44}}, 234 | {[16]byte{0x00, 0xca, 0x89, 0x43, 0x0f, 0xc5, 0x86, 0x4c, 0x1e, 0xd4, 0x97, 0x5d, 0x11, 0xdb, 0x98, 0x52}, [16]byte{0x00, 0x3c, 0x78, 0x44, 0xf0, 0xcc, 0x88, 0xb4, 0xfd, 0xc1, 0x85, 0xb9, 0x0d, 0x31, 0x75, 0x49}}, 235 | {[16]byte{0x00, 0xcb, 0x8b, 0x40, 0x0b, 0xc0, 0x80, 0x4b, 0x16, 0xdd, 0x9d, 0x56, 0x1d, 0xd6, 0x96, 0x5d}, [16]byte{0x00, 0x2c, 0x58, 0x74, 0xb0, 0x9c, 0xe8, 0xc4, 0x7d, 0x51, 0x25, 0x09, 0xcd, 0xe1, 0x95, 0xb9}}, 236 | {[16]byte{0x00, 0xcc, 0x85, 0x49, 0x17, 0xdb, 0x92, 0x5e, 0x2e, 0xe2, 0xab, 0x67, 0x39, 0xf5, 0xbc, 0x70}, [16]byte{0x00, 0x5c, 0xb8, 0xe4, 0x6d, 0x31, 0xd5, 0x89, 0xda, 0x86, 0x62, 0x3e, 0xb7, 0xeb, 0x0f, 0x53}}, 237 | {[16]byte{0x00, 0xcd, 0x87, 0x4a, 0x13, 0xde, 0x94, 0x59, 0x26, 0xeb, 0xa1, 0x6c, 0x35, 0xf8, 0xb2, 0x7f}, [16]byte{0x00, 0x4c, 0x98, 0xd4, 0x2d, 0x61, 0xb5, 0xf9, 0x5a, 0x16, 0xc2, 0x8e, 0x77, 0x3b, 0xef, 0xa3}}, 238 | {[16]byte{0x00, 0xce, 0x81, 0x4f, 0x1f, 0xd1, 0x9e, 0x50, 0x3e, 0xf0, 0xbf, 0x71, 0x21, 0xef, 0xa0, 0x6e}, [16]byte{0x00, 0x7c, 0xf8, 0x84, 0xed, 0x91, 0x15, 0x69, 0xc7, 0xbb, 0x3f, 0x43, 0x2a, 0x56, 0xd2, 0xae}}, 239 | {[16]byte{0x00, 0xcf, 0x83, 0x4c, 0x1b, 0xd4, 0x98, 0x57, 0x36, 0xf9, 0xb5, 0x7a, 0x2d, 0xe2, 0xae, 0x61}, [16]byte{0x00, 0x6c, 0xd8, 0xb4, 0xad, 0xc1, 0x75, 0x19, 0x47, 0x2b, 0x9f, 0xf3, 0xea, 0x86, 0x32, 0x5e}}, 240 | {[16]byte{0x00, 0xd0, 0xbd, 0x6d, 0x67, 0xb7, 0xda, 0x0a, 0xce, 0x1e, 0x73, 0xa3, 0xa9, 0x79, 0x14, 0xc4}, [16]byte{0x00, 0x81, 0x1f, 0x9e, 0x3e, 0xbf, 0x21, 0xa0, 0x7c, 0xfd, 0x63, 0xe2, 0x42, 0xc3, 0x5d, 0xdc}}, 241 | {[16]byte{0x00, 0xd1, 0xbf, 0x6e, 0x63, 0xb2, 0xdc, 0x0d, 0xc6, 0x17, 0x79, 0xa8, 0xa5, 0x74, 0x1a, 0xcb}, [16]byte{0x00, 0x91, 0x3f, 0xae, 0x7e, 0xef, 0x41, 0xd0, 0xfc, 0x6d, 0xc3, 0x52, 0x82, 0x13, 0xbd, 0x2c}}, 242 | {[16]byte{0x00, 0xd2, 0xb9, 0x6b, 0x6f, 0xbd, 0xd6, 0x04, 0xde, 0x0c, 0x67, 0xb5, 0xb1, 0x63, 0x08, 0xda}, [16]byte{0x00, 0xa1, 0x5f, 0xfe, 0xbe, 0x1f, 0xe1, 0x40, 0x61, 0xc0, 0x3e, 0x9f, 0xdf, 0x7e, 0x80, 0x21}}, 243 | {[16]byte{0x00, 0xd3, 0xbb, 0x68, 0x6b, 0xb8, 0xd0, 0x03, 0xd6, 0x05, 0x6d, 0xbe, 0xbd, 0x6e, 0x06, 0xd5}, [16]byte{0x00, 0xb1, 0x7f, 0xce, 0xfe, 0x4f, 0x81, 0x30, 0xe1, 0x50, 0x9e, 0x2f, 0x1f, 0xae, 0x60, 0xd1}}, 244 | {[16]byte{0x00, 0xd4, 0xb5, 0x61, 0x77, 0xa3, 0xc2, 0x16, 0xee, 0x3a, 0x5b, 0x8f, 0x99, 0x4d, 0x2c, 0xf8}, [16]byte{0x00, 0xc1, 0x9f, 0x5e, 0x23, 0xe2, 0xbc, 0x7d, 0x46, 0x87, 0xd9, 0x18, 0x65, 0xa4, 0xfa, 0x3b}}, 245 | {[16]byte{0x00, 0xd5, 0xb7, 0x62, 0x73, 0xa6, 0xc4, 0x11, 0xe6, 0x33, 0x51, 0x84, 0x95, 0x40, 0x22, 0xf7}, [16]byte{0x00, 0xd1, 0xbf, 0x6e, 0x63, 0xb2, 0xdc, 0x0d, 0xc6, 0x17, 0x79, 0xa8, 0xa5, 0x74, 0x1a, 0xcb}}, 246 | {[16]byte{0x00, 0xd6, 0xb1, 0x67, 0x7f, 0xa9, 0xce, 0x18, 0xfe, 0x28, 0x4f, 0x99, 0x81, 0x57, 0x30, 0xe6}, [16]byte{0x00, 0xe1, 0xdf, 0x3e, 0xa3, 0x42, 0x7c, 0x9d, 0x5b, 0xba, 0x84, 0x65, 0xf8, 0x19, 0x27, 0xc6}}, 247 | {[16]byte{0x00, 0xd7, 0xb3, 0x64, 0x7b, 0xac, 0xc8, 0x1f, 0xf6, 0x21, 0x45, 0x92, 0x8d, 0x5a, 0x3e, 0xe9}, [16]byte{0x00, 0xf1, 0xff, 0x0e, 0xe3, 0x12, 0x1c, 0xed, 0xdb, 0x2a, 0x24, 0xd5, 0x38, 0xc9, 0xc7, 0x36}}, 248 | {[16]byte{0x00, 0xd8, 0xad, 0x75, 0x47, 0x9f, 0xea, 0x32, 0x8e, 0x56, 0x23, 0xfb, 0xc9, 0x11, 0x64, 0xbc}, [16]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}}, 249 | {[16]byte{0x00, 0xd9, 0xaf, 0x76, 0x43, 0x9a, 0xec, 0x35, 0x86, 0x5f, 0x29, 0xf0, 0xc5, 0x1c, 0x6a, 0xb3}, [16]byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, 250 | {[16]byte{0x00, 0xda, 0xa9, 0x73, 0x4f, 0x95, 0xe6, 0x3c, 0x9e, 0x44, 0x37, 0xed, 0xd1, 0x0b, 0x78, 0xa2}, [16]byte{0x00, 0x21, 0x42, 0x63, 0x84, 0xa5, 0xc6, 0xe7, 0x15, 0x34, 0x57, 0x76, 0x91, 0xb0, 0xd3, 0xf2}}, 251 | {[16]byte{0x00, 0xdb, 0xab, 0x70, 0x4b, 0x90, 0xe0, 0x3b, 0x96, 0x4d, 0x3d, 0xe6, 0xdd, 0x06, 0x76, 0xad}, [16]byte{0x00, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, 0x95, 0xa4, 0xf7, 0xc6, 0x51, 0x60, 0x33, 0x02}}, 252 | {[16]byte{0x00, 0xdc, 0xa5, 0x79, 0x57, 0x8b, 0xf2, 0x2e, 0xae, 0x72, 0x0b, 0xd7, 0xf9, 0x25, 0x5c, 0x80}, [16]byte{0x00, 0x41, 0x82, 0xc3, 0x19, 0x58, 0x9b, 0xda, 0x32, 0x73, 0xb0, 0xf1, 0x2b, 0x6a, 0xa9, 0xe8}}, 253 | {[16]byte{0x00, 0xdd, 0xa7, 0x7a, 0x53, 0x8e, 0xf4, 0x29, 0xa6, 0x7b, 0x01, 0xdc, 0xf5, 0x28, 0x52, 0x8f}, [16]byte{0x00, 0x51, 0xa2, 0xf3, 0x59, 0x08, 0xfb, 0xaa, 0xb2, 0xe3, 0x10, 0x41, 0xeb, 0xba, 0x49, 0x18}}, 254 | {[16]byte{0x00, 0xde, 0xa1, 0x7f, 0x5f, 0x81, 0xfe, 0x20, 0xbe, 0x60, 0x1f, 0xc1, 0xe1, 0x3f, 0x40, 0x9e}, [16]byte{0x00, 0x61, 0xc2, 0xa3, 0x99, 0xf8, 0x5b, 0x3a, 0x2f, 0x4e, 0xed, 0x8c, 0xb6, 0xd7, 0x74, 0x15}}, 255 | {[16]byte{0x00, 0xdf, 0xa3, 0x7c, 0x5b, 0x84, 0xf8, 0x27, 0xb6, 0x69, 0x15, 0xca, 0xed, 0x32, 0x4e, 0x91}, [16]byte{0x00, 0x71, 0xe2, 0x93, 0xd9, 0xa8, 0x3b, 0x4a, 0xaf, 0xde, 0x4d, 0x3c, 0x76, 0x07, 0x94, 0xe5}}, 256 | {[16]byte{0x00, 0xe0, 0xdd, 0x3d, 0xa7, 0x47, 0x7a, 0x9a, 0x53, 0xb3, 0x8e, 0x6e, 0xf4, 0x14, 0x29, 0xc9}, [16]byte{0x00, 0xa6, 0x51, 0xf7, 0xa2, 0x04, 0xf3, 0x55, 0x59, 0xff, 0x08, 0xae, 0xfb, 0x5d, 0xaa, 0x0c}}, 257 | {[16]byte{0x00, 0xe1, 0xdf, 0x3e, 0xa3, 0x42, 0x7c, 0x9d, 0x5b, 0xba, 0x84, 0x65, 0xf8, 0x19, 0x27, 0xc6}, [16]byte{0x00, 0xb6, 0x71, 0xc7, 0xe2, 0x54, 0x93, 0x25, 0xd9, 0x6f, 0xa8, 0x1e, 0x3b, 0x8d, 0x4a, 0xfc}}, 258 | {[16]byte{0x00, 0xe2, 0xd9, 0x3b, 0xaf, 0x4d, 0x76, 0x94, 0x43, 0xa1, 0x9a, 0x78, 0xec, 0x0e, 0x35, 0xd7}, [16]byte{0x00, 0x86, 0x11, 0x97, 0x22, 0xa4, 0x33, 0xb5, 0x44, 0xc2, 0x55, 0xd3, 0x66, 0xe0, 0x77, 0xf1}}, 259 | {[16]byte{0x00, 0xe3, 0xdb, 0x38, 0xab, 0x48, 0x70, 0x93, 0x4b, 0xa8, 0x90, 0x73, 0xe0, 0x03, 0x3b, 0xd8}, [16]byte{0x00, 0x96, 0x31, 0xa7, 0x62, 0xf4, 0x53, 0xc5, 0xc4, 0x52, 0xf5, 0x63, 0xa6, 0x30, 0x97, 0x01}}, 260 | {[16]byte{0x00, 0xe4, 0xd5, 0x31, 0xb7, 0x53, 0x62, 0x86, 0x73, 0x97, 0xa6, 0x42, 0xc4, 0x20, 0x11, 0xf5}, [16]byte{0x00, 0xe6, 0xd1, 0x37, 0xbf, 0x59, 0x6e, 0x88, 0x63, 0x85, 0xb2, 0x54, 0xdc, 0x3a, 0x0d, 0xeb}}, 261 | {[16]byte{0x00, 0xe5, 0xd7, 0x32, 0xb3, 0x56, 0x64, 0x81, 0x7b, 0x9e, 0xac, 0x49, 0xc8, 0x2d, 0x1f, 0xfa}, [16]byte{0x00, 0xf6, 0xf1, 0x07, 0xff, 0x09, 0x0e, 0xf8, 0xe3, 0x15, 0x12, 0xe4, 0x1c, 0xea, 0xed, 0x1b}}, 262 | {[16]byte{0x00, 0xe6, 0xd1, 0x37, 0xbf, 0x59, 0x6e, 0x88, 0x63, 0x85, 0xb2, 0x54, 0xdc, 0x3a, 0x0d, 0xeb}, [16]byte{0x00, 0xc6, 0x91, 0x57, 0x3f, 0xf9, 0xae, 0x68, 0x7e, 0xb8, 0xef, 0x29, 0x41, 0x87, 0xd0, 0x16}}, 263 | {[16]byte{0x00, 0xe7, 0xd3, 0x34, 0xbb, 0x5c, 0x68, 0x8f, 0x6b, 0x8c, 0xb8, 0x5f, 0xd0, 0x37, 0x03, 0xe4}, [16]byte{0x00, 0xd6, 0xb1, 0x67, 0x7f, 0xa9, 0xce, 0x18, 0xfe, 0x28, 0x4f, 0x99, 0x81, 0x57, 0x30, 0xe6}}, 264 | {[16]byte{0x00, 0xe8, 0xcd, 0x25, 0x87, 0x6f, 0x4a, 0xa2, 0x13, 0xfb, 0xde, 0x36, 0x94, 0x7c, 0x59, 0xb1}, [16]byte{0x00, 0x26, 0x4c, 0x6a, 0x98, 0xbe, 0xd4, 0xf2, 0x2d, 0x0b, 0x61, 0x47, 0xb5, 0x93, 0xf9, 0xdf}}, 265 | {[16]byte{0x00, 0xe9, 0xcf, 0x26, 0x83, 0x6a, 0x4c, 0xa5, 0x1b, 0xf2, 0xd4, 0x3d, 0x98, 0x71, 0x57, 0xbe}, [16]byte{0x00, 0x36, 0x6c, 0x5a, 0xd8, 0xee, 0xb4, 0x82, 0xad, 0x9b, 0xc1, 0xf7, 0x75, 0x43, 0x19, 0x2f}}, 266 | {[16]byte{0x00, 0xea, 0xc9, 0x23, 0x8f, 0x65, 0x46, 0xac, 0x03, 0xe9, 0xca, 0x20, 0x8c, 0x66, 0x45, 0xaf}, [16]byte{0x00, 0x06, 0x0c, 0x0a, 0x18, 0x1e, 0x14, 0x12, 0x30, 0x36, 0x3c, 0x3a, 0x28, 0x2e, 0x24, 0x22}}, 267 | {[16]byte{0x00, 0xeb, 0xcb, 0x20, 0x8b, 0x60, 0x40, 0xab, 0x0b, 0xe0, 0xc0, 0x2b, 0x80, 0x6b, 0x4b, 0xa0}, [16]byte{0x00, 0x16, 0x2c, 0x3a, 0x58, 0x4e, 0x74, 0x62, 0xb0, 0xa6, 0x9c, 0x8a, 0xe8, 0xfe, 0xc4, 0xd2}}, 268 | {[16]byte{0x00, 0xec, 0xc5, 0x29, 0x97, 0x7b, 0x52, 0xbe, 0x33, 0xdf, 0xf6, 0x1a, 0xa4, 0x48, 0x61, 0x8d}, [16]byte{0x00, 0x66, 0xcc, 0xaa, 0x85, 0xe3, 0x49, 0x2f, 0x17, 0x71, 0xdb, 0xbd, 0x92, 0xf4, 0x5e, 0x38}}, 269 | {[16]byte{0x00, 0xed, 0xc7, 0x2a, 0x93, 0x7e, 0x54, 0xb9, 0x3b, 0xd6, 0xfc, 0x11, 0xa8, 0x45, 0x6f, 0x82}, [16]byte{0x00, 0x76, 0xec, 0x9a, 0xc5, 0xb3, 0x29, 0x5f, 0x97, 0xe1, 0x7b, 0x0d, 0x52, 0x24, 0xbe, 0xc8}}, 270 | {[16]byte{0x00, 0xee, 0xc1, 0x2f, 0x9f, 0x71, 0x5e, 0xb0, 0x23, 0xcd, 0xe2, 0x0c, 0xbc, 0x52, 0x7d, 0x93}, [16]byte{0x00, 0x46, 0x8c, 0xca, 0x05, 0x43, 0x89, 0xcf, 0x0a, 0x4c, 0x86, 0xc0, 0x0f, 0x49, 0x83, 0xc5}}, 271 | {[16]byte{0x00, 0xef, 0xc3, 0x2c, 0x9b, 0x74, 0x58, 0xb7, 0x2b, 0xc4, 0xe8, 0x07, 0xb0, 0x5f, 0x73, 0x9c}, [16]byte{0x00, 0x56, 0xac, 0xfa, 0x45, 0x13, 0xe9, 0xbf, 0x8a, 0xdc, 0x26, 0x70, 0xcf, 0x99, 0x63, 0x35}}, 272 | {[16]byte{0x00, 0xf0, 0xfd, 0x0d, 0xe7, 0x17, 0x1a, 0xea, 0xd3, 0x23, 0x2e, 0xde, 0x34, 0xc4, 0xc9, 0x39}, [16]byte{0x00, 0xbb, 0x6b, 0xd0, 0xd6, 0x6d, 0xbd, 0x06, 0xb1, 0x0a, 0xda, 0x61, 0x67, 0xdc, 0x0c, 0xb7}}, 273 | {[16]byte{0x00, 0xf1, 0xff, 0x0e, 0xe3, 0x12, 0x1c, 0xed, 0xdb, 0x2a, 0x24, 0xd5, 0x38, 0xc9, 0xc7, 0x36}, [16]byte{0x00, 0xab, 0x4b, 0xe0, 0x96, 0x3d, 0xdd, 0x76, 0x31, 0x9a, 0x7a, 0xd1, 0xa7, 0x0c, 0xec, 0x47}}, 274 | {[16]byte{0x00, 0xf2, 0xf9, 0x0b, 0xef, 0x1d, 0x16, 0xe4, 0xc3, 0x31, 0x3a, 0xc8, 0x2c, 0xde, 0xd5, 0x27}, [16]byte{0x00, 0x9b, 0x2b, 0xb0, 0x56, 0xcd, 0x7d, 0xe6, 0xac, 0x37, 0x87, 0x1c, 0xfa, 0x61, 0xd1, 0x4a}}, 275 | {[16]byte{0x00, 0xf3, 0xfb, 0x08, 0xeb, 0x18, 0x10, 0xe3, 0xcb, 0x38, 0x30, 0xc3, 0x20, 0xd3, 0xdb, 0x28}, [16]byte{0x00, 0x8b, 0x0b, 0x80, 0x16, 0x9d, 0x1d, 0x96, 0x2c, 0xa7, 0x27, 0xac, 0x3a, 0xb1, 0x31, 0xba}}, 276 | {[16]byte{0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05}, [16]byte{0x00, 0xfb, 0xeb, 0x10, 0xcb, 0x30, 0x20, 0xdb, 0x8b, 0x70, 0x60, 0x9b, 0x40, 0xbb, 0xab, 0x50}}, 277 | {[16]byte{0x00, 0xf5, 0xf7, 0x02, 0xf3, 0x06, 0x04, 0xf1, 0xfb, 0x0e, 0x0c, 0xf9, 0x08, 0xfd, 0xff, 0x0a}, [16]byte{0x00, 0xeb, 0xcb, 0x20, 0x8b, 0x60, 0x40, 0xab, 0x0b, 0xe0, 0xc0, 0x2b, 0x80, 0x6b, 0x4b, 0xa0}}, 278 | {[16]byte{0x00, 0xf6, 0xf1, 0x07, 0xff, 0x09, 0x0e, 0xf8, 0xe3, 0x15, 0x12, 0xe4, 0x1c, 0xea, 0xed, 0x1b}, [16]byte{0x00, 0xdb, 0xab, 0x70, 0x4b, 0x90, 0xe0, 0x3b, 0x96, 0x4d, 0x3d, 0xe6, 0xdd, 0x06, 0x76, 0xad}}, 279 | {[16]byte{0x00, 0xf7, 0xf3, 0x04, 0xfb, 0x0c, 0x08, 0xff, 0xeb, 0x1c, 0x18, 0xef, 0x10, 0xe7, 0xe3, 0x14}, [16]byte{0x00, 0xcb, 0x8b, 0x40, 0x0b, 0xc0, 0x80, 0x4b, 0x16, 0xdd, 0x9d, 0x56, 0x1d, 0xd6, 0x96, 0x5d}}, 280 | {[16]byte{0x00, 0xf8, 0xed, 0x15, 0xc7, 0x3f, 0x2a, 0xd2, 0x93, 0x6b, 0x7e, 0x86, 0x54, 0xac, 0xb9, 0x41}, [16]byte{0x00, 0x3b, 0x76, 0x4d, 0xec, 0xd7, 0x9a, 0xa1, 0xc5, 0xfe, 0xb3, 0x88, 0x29, 0x12, 0x5f, 0x64}}, 281 | {[16]byte{0x00, 0xf9, 0xef, 0x16, 0xc3, 0x3a, 0x2c, 0xd5, 0x9b, 0x62, 0x74, 0x8d, 0x58, 0xa1, 0xb7, 0x4e}, [16]byte{0x00, 0x2b, 0x56, 0x7d, 0xac, 0x87, 0xfa, 0xd1, 0x45, 0x6e, 0x13, 0x38, 0xe9, 0xc2, 0xbf, 0x94}}, 282 | {[16]byte{0x00, 0xfa, 0xe9, 0x13, 0xcf, 0x35, 0x26, 0xdc, 0x83, 0x79, 0x6a, 0x90, 0x4c, 0xb6, 0xa5, 0x5f}, [16]byte{0x00, 0x1b, 0x36, 0x2d, 0x6c, 0x77, 0x5a, 0x41, 0xd8, 0xc3, 0xee, 0xf5, 0xb4, 0xaf, 0x82, 0x99}}, 283 | {[16]byte{0x00, 0xfb, 0xeb, 0x10, 0xcb, 0x30, 0x20, 0xdb, 0x8b, 0x70, 0x60, 0x9b, 0x40, 0xbb, 0xab, 0x50}, [16]byte{0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69}}, 284 | {[16]byte{0x00, 0xfc, 0xe5, 0x19, 0xd7, 0x2b, 0x32, 0xce, 0xb3, 0x4f, 0x56, 0xaa, 0x64, 0x98, 0x81, 0x7d}, [16]byte{0x00, 0x7b, 0xf6, 0x8d, 0xf1, 0x8a, 0x07, 0x7c, 0xff, 0x84, 0x09, 0x72, 0x0e, 0x75, 0xf8, 0x83}}, 285 | {[16]byte{0x00, 0xfd, 0xe7, 0x1a, 0xd3, 0x2e, 0x34, 0xc9, 0xbb, 0x46, 0x5c, 0xa1, 0x68, 0x95, 0x8f, 0x72}, [16]byte{0x00, 0x6b, 0xd6, 0xbd, 0xb1, 0xda, 0x67, 0x0c, 0x7f, 0x14, 0xa9, 0xc2, 0xce, 0xa5, 0x18, 0x73}}, 286 | {[16]byte{0x00, 0xfe, 0xe1, 0x1f, 0xdf, 0x21, 0x3e, 0xc0, 0xa3, 0x5d, 0x42, 0xbc, 0x7c, 0x82, 0x9d, 0x63}, [16]byte{0x00, 0x5b, 0xb6, 0xed, 0x71, 0x2a, 0xc7, 0x9c, 0xe2, 0xb9, 0x54, 0x0f, 0x93, 0xc8, 0x25, 0x7e}}, 287 | {[16]byte{0x00, 0xff, 0xe3, 0x1c, 0xdb, 0x24, 0x38, 0xc7, 0xab, 0x54, 0x48, 0xb7, 0x70, 0x8f, 0x93, 0x6c}, [16]byte{0x00, 0x4b, 0x96, 0xdd, 0x31, 0x7a, 0xa7, 0xec, 0x62, 0x29, 0xf4, 0xbf, 0x53, 0x18, 0xc5, 0x8e}}, 288 | } 289 | -------------------------------------------------------------------------------- /addmul_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import ( 26 | "bytes" 27 | "math/rand" 28 | "testing" 29 | ) 30 | 31 | func addmulSlow(z []byte, x []byte, y byte) { 32 | gf_mul_y := gf_mul_table[y][:] 33 | for i := range z { 34 | z[i] ^= gf_mul_y[x[i]] 35 | } 36 | } 37 | 38 | func TestAddmul(t *testing.T) { 39 | for i := 0; i < 10000; i++ { 40 | align := rand.Intn(256) 41 | size := rand.Intn(1024) + align 42 | y := byte(rand.Intn(256)) 43 | x := RandomBytes(size) 44 | z := RandomBytes(size) 45 | z1 := append([]byte(nil), z...) 46 | z2 := append([]byte(nil), z...) 47 | 48 | addmulSlow(z1[align:], x[align:], y) 49 | addmul(z2[align:], x[align:], y) 50 | 51 | if !bytes.Equal(z1, z2) { 52 | t.Logf("align: %d", align) 53 | t.Logf("size: %d", size) 54 | t.Logf("x: %x", x) 55 | t.Logf("z: %x", z) 56 | t.Logf("z1: %x", z1) 57 | t.Logf("z2: %x", z2) 58 | t.Fatal("mismatch") 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /berlekamp_welch.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import ( 26 | "errors" 27 | "sort" 28 | ) 29 | 30 | // Decode will take a destination buffer (can be nil) and a list of shares 31 | // (pieces). It will return the data passed in to the corresponding Encode 32 | // call or return an error. 33 | // 34 | // It will first correct the shares using Correct, mutating and reordering the 35 | // passed-in shares arguments. Then it will rebuild the data using Rebuild. 36 | // Finally it will concatenate the data into the given output buffer dst if it 37 | // has capacity, growing it otherwise. 38 | // 39 | // If you already know your data does not contain errors, Rebuild will be 40 | // faster. 41 | // 42 | // If you only want to identify which pieces are bad, you may be interested in 43 | // Correct. 44 | // 45 | // If you don't want the data concatenated for you, you can use Correct and 46 | // then Rebuild individually. 47 | func (f *FEC) Decode(dst []byte, shares []Share) ([]byte, error) { 48 | err := f.Correct(shares) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | if len(shares) == 0 { 54 | return nil, errors.New("must specify at least one share") 55 | } 56 | piece_len := len(shares[0].Data) 57 | result_len := piece_len * f.k 58 | if cap(dst) < result_len { 59 | dst = make([]byte, result_len) 60 | } else { 61 | dst = dst[:result_len] 62 | } 63 | 64 | return dst, f.Rebuild(shares, func(s Share) { 65 | copy(dst[s.Number*piece_len:], s.Data) 66 | }) 67 | } 68 | 69 | func (f *FEC) decode(shares []Share, output func(Share)) error { 70 | err := f.Correct(shares) 71 | if err != nil { 72 | return err 73 | } 74 | return f.Rebuild(shares, output) 75 | } 76 | 77 | // Correct implements the Berlekamp-Welch algorithm for correcting 78 | // errors in given FEC encoded data. It will correct the supplied shares, 79 | // mutating the underlying byte slices and reordering the shares 80 | func (fc *FEC) Correct(shares []Share) error { 81 | if len(shares) < fc.k { 82 | return errors.New("must specify at least the number of required shares") 83 | } 84 | 85 | sort.Sort(byNumber(shares)) 86 | 87 | // fast path: check to see if there are no errors by evaluating it with 88 | // the syndrome matrix. 89 | synd, err := fc.syndromeMatrix(shares) 90 | if err != nil { 91 | return err 92 | } 93 | buf := make([]byte, len(shares[0].Data)) 94 | 95 | for i := 0; i < synd.r; i++ { 96 | for j := range buf { 97 | buf[j] = 0 98 | } 99 | 100 | for j := 0; j < synd.c; j++ { 101 | addmul(buf, shares[j].Data, byte(synd.get(i, j))) 102 | } 103 | 104 | for j := range buf { 105 | if buf[j] == 0 { 106 | continue 107 | } 108 | data, err := fc.berlekampWelch(shares, j) 109 | if err != nil { 110 | return err 111 | } 112 | for _, share := range shares { 113 | share.Data[j] = data[share.Number] 114 | } 115 | } 116 | } 117 | 118 | return nil 119 | } 120 | 121 | func (fc *FEC) berlekampWelch(shares []Share, index int) ([]byte, error) { 122 | k := fc.k // required size 123 | r := len(shares) // required + redundancy size 124 | e := (r - k) / 2 // deg of E polynomial 125 | q := e + k // def of Q polynomial 126 | 127 | if e <= 0 { 128 | return nil, NotEnoughShares 129 | } 130 | 131 | const interp_base = gfVal(2) 132 | 133 | eval_point := func(num int) gfVal { 134 | if num == 0 { 135 | return 0 136 | } 137 | return interp_base.pow(num - 1) 138 | } 139 | 140 | dim := q + e 141 | 142 | // build the system of equations s * u = f 143 | s := matrixNew(dim, dim) // constraint matrix 144 | a := matrixNew(dim, dim) // augmented matrix 145 | f := make(gfVals, dim) // constant column vector 146 | u := make(gfVals, dim) // solution vector 147 | 148 | for i := 0; i < dim; i++ { 149 | x_i := eval_point(shares[i].Number) 150 | r_i := gfConst(shares[i].Data[index]) 151 | 152 | f[i] = x_i.pow(e).mul(r_i) 153 | 154 | for j := 0; j < q; j++ { 155 | s.set(i, j, x_i.pow(j)) 156 | if i == j { 157 | a.set(i, j, gfConst(1)) 158 | } 159 | } 160 | 161 | for k := 0; k < e; k++ { 162 | j := k + q 163 | 164 | s.set(i, j, x_i.pow(k).mul(r_i)) 165 | if i == j { 166 | a.set(i, j, gfConst(1)) 167 | } 168 | } 169 | } 170 | 171 | // invert and put the result in a 172 | err := s.invertWith(a) 173 | if err != nil { 174 | return nil, err 175 | } 176 | 177 | // multiply the inverted matrix by the column vector 178 | for i := 0; i < dim; i++ { 179 | ri := a.indexRow(i) 180 | u[i] = ri.dot(f) 181 | } 182 | 183 | // reverse u for easier construction of the polynomials 184 | for i := 0; i < len(u)/2; i++ { 185 | o := len(u) - i - 1 186 | u[i], u[o] = u[o], u[i] 187 | } 188 | 189 | q_poly := gfPoly(u[e:]) 190 | e_poly := append(gfPoly{gfConst(1)}, u[:e]...) 191 | 192 | p_poly, rem, err := q_poly.div(e_poly) 193 | if err != nil { 194 | return nil, err 195 | } 196 | 197 | if !rem.isZero() { 198 | return nil, TooManyErrors 199 | } 200 | 201 | out := make([]byte, fc.n) 202 | for i := range out { 203 | pt := gfConst(0) 204 | if i != 0 { 205 | pt = interp_base.pow(i - 1) 206 | } 207 | out[i] = byte(p_poly.eval(pt)) 208 | } 209 | 210 | return out, nil 211 | } 212 | 213 | func (fc *FEC) syndromeMatrix(shares []Share) (gfMat, error) { 214 | // get a list of keepers 215 | keepers := make([]bool, fc.n) 216 | shareCount := 0 217 | for _, share := range shares { 218 | if !keepers[share.Number] { 219 | keepers[share.Number] = true 220 | shareCount++ 221 | } 222 | } 223 | 224 | // create a vandermonde matrix but skip columns where we're missing the 225 | // share. 226 | out := matrixNew(fc.k, shareCount) 227 | for i := 0; i < fc.k; i++ { 228 | skipped := 0 229 | for j := 0; j < fc.n; j++ { 230 | if !keepers[j] { 231 | skipped++ 232 | continue 233 | } 234 | 235 | out.set(i, j-skipped, gfConst(fc.vand_matrix[i*fc.n+j])) 236 | } 237 | } 238 | 239 | // standardize the output and convert into parity form 240 | err := out.standardize() 241 | if err != nil { 242 | return gfMat{}, err 243 | } 244 | 245 | return out.parity(), nil 246 | } 247 | -------------------------------------------------------------------------------- /berlekamp_welch_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import ( 26 | "bytes" 27 | "math/rand" 28 | "testing" 29 | ) 30 | 31 | func TestBerlekampWelchSingle(t *testing.T) { 32 | const block = 1 33 | const total, required = 7, 3 34 | 35 | test := NewBerlekampWelchTest(t, required, total) 36 | _, shares := test.SomeShares(block) 37 | 38 | out, err := test.code.berlekampWelch(shares, 0) 39 | test.AssertNoError(err) 40 | test.AssertDeepEqual(out, []byte{0x01, 0x02, 0x03, 0x15, 0x69, 0xcc, 0xf2}) 41 | } 42 | 43 | func TestBerlekampWelch(t *testing.T) { 44 | const block = 4096 45 | const total, required = 7, 3 46 | 47 | test := NewBerlekampWelchTest(t, required, total) 48 | _, shares := test.SomeShares(block) 49 | 50 | test.AssertNoError(test.code.decode(shares, nil)) 51 | 52 | shares[0].Data[0]++ 53 | shares[1].Data[0]++ 54 | 55 | decoded_shares, callback := test.StoreShares() 56 | test.AssertNoError(test.code.decode(shares, callback)) 57 | test.AssertDeepEqual(decoded_shares[:required], shares[:required]) 58 | } 59 | 60 | func TestDecode(t *testing.T) { 61 | const block = 4096 62 | const total, required = 7, 3 63 | 64 | test := NewBerlekampWelchTest(t, required, total) 65 | origdata, shares := test.SomeShares(block) 66 | 67 | combined, err := test.code.Decode(nil, shares) 68 | test.AssertNoError(err) 69 | test.AssertDeepEqual(combined, origdata) 70 | 71 | combined, err = test.code.Decode(make([]byte, len(origdata)+1), shares) 72 | test.AssertNoError(err) 73 | test.AssertDeepEqual(combined, origdata) 74 | } 75 | 76 | func TestBerlekampWelchZero(t *testing.T) { 77 | const total, required = 40, 20 78 | 79 | test := NewBerlekampWelchTest(t, required, total) 80 | 81 | buf := make([]byte, 200) 82 | buf = append(buf, bytes.Repeat([]byte{0x14}, 20)...) 83 | 84 | shares, callback := test.StoreShares() 85 | test.AssertNoError(test.code.Encode(buf, callback)) 86 | 87 | shares[0].Data[0]++ 88 | 89 | test.AssertNoError(test.code.decode(shares, nil)) 90 | } 91 | 92 | func TestBerlekampWelchErrors(t *testing.T) { 93 | const block = 4096 94 | const total, required = 7, 3 95 | 96 | test := NewBerlekampWelchTest(t, required, total) 97 | _, shares := test.SomeShares(block) 98 | test.AssertNoError(test.code.decode(shares, nil)) 99 | 100 | for i := 0; i < 500; i++ { 101 | shares_copy := test.CopyShares(shares) 102 | for i := 0; i < block; i++ { 103 | test.MutateShare(i, shares_copy[rand.Intn(total)]) 104 | test.MutateShare(i, shares_copy[rand.Intn(total)]) 105 | } 106 | 107 | decoded_shares, callback := test.StoreShares() 108 | test.AssertNoError(test.code.decode(shares, callback)) 109 | test.AssertDeepEqual(decoded_shares[:required], shares[:required]) 110 | } 111 | } 112 | 113 | func TestBerlekampWelchRandomShares(t *testing.T) { 114 | const block = 4096 115 | const total, required = 7, 3 116 | 117 | test := NewBerlekampWelchTest(t, required, total) 118 | _, shares := test.SomeShares(block) 119 | test.AssertNoError(test.code.decode(shares, nil)) 120 | 121 | for i := 0; i < 500; i++ { 122 | test_shares := test.CopyShares(shares) 123 | test.PermuteShares(test_shares) 124 | test_shares = test_shares[:required+2+rand.Intn(total-required-2)] 125 | 126 | for i := 0; i < block; i++ { 127 | test.MutateShare(i, test_shares[rand.Intn(len(test_shares))]) 128 | } 129 | 130 | decoded_shares, callback := test.StoreShares() 131 | test.AssertNoError(test.code.decode(test_shares, callback)) 132 | test.AssertDeepEqual(decoded_shares[:required], shares[:required]) 133 | } 134 | } 135 | 136 | func BenchmarkBerlekampWelch(b *testing.B) { 137 | const block = 4096 138 | const total, required = 40, 20 139 | 140 | test := NewBerlekampWelchTest(b, required, total) 141 | _, shares := test.SomeShares(block) 142 | 143 | b.ReportAllocs() 144 | b.SetBytes(required * block) 145 | b.ResetTimer() 146 | 147 | for i := 0; i < b.N; i++ { 148 | test.AssertNoError(test.code.decode(shares, nil)) 149 | } 150 | } 151 | 152 | func BenchmarkBerlekampWelchOneError(b *testing.B) { 153 | const block = 4096 154 | const total, required = 40, 20 155 | 156 | test := NewBerlekampWelchTest(b, required, total) 157 | _, shares := test.SomeShares(block) 158 | dec_shares := shares[total-required-2:] 159 | 160 | b.ReportAllocs() 161 | b.SetBytes(required * block) 162 | b.ResetTimer() 163 | 164 | for i := 0; i < b.N; i++ { 165 | test.AssertNoError(test.code.decode(dec_shares, nil)) 166 | } 167 | } 168 | 169 | func BenchmarkBerlekampWelchTwoErrors(b *testing.B) { 170 | const block = 4096 171 | const total, required = 40, 20 172 | 173 | test := NewBerlekampWelchTest(b, required, total) 174 | _, shares := test.SomeShares(block) 175 | dec_shares := shares[total-required-4:] 176 | 177 | b.ReportAllocs() 178 | b.SetBytes(required * block) 179 | b.ResetTimer() 180 | 181 | for i := 0; i < b.N; i++ { 182 | test.AssertNoError(test.code.decode(dec_shares, nil)) 183 | } 184 | } 185 | 186 | /////////////////////////////////////////////////////////////////////////////// 187 | // Helpers 188 | /////////////////////////////////////////////////////////////////////////////// 189 | 190 | type BerlekampWelchTest struct { 191 | *Asserter 192 | 193 | code *FEC 194 | } 195 | 196 | func NewBerlekampWelchTest(t testing.TB, 197 | required, total int) *BerlekampWelchTest { 198 | 199 | asserter := Wrap(t) 200 | 201 | code, err := NewFEC(required, total) 202 | asserter.AssertNoError(err) 203 | 204 | return &BerlekampWelchTest{ 205 | Asserter: asserter, 206 | 207 | code: code, 208 | } 209 | } 210 | 211 | func (t *BerlekampWelchTest) StoreShares() ([]Share, func(Share)) { 212 | out := make([]Share, t.code.n) 213 | return out, func(s Share) { 214 | idx := s.Number 215 | out[idx].Number = idx 216 | out[idx].Data = append(out[idx].Data, s.Data...) 217 | } 218 | } 219 | 220 | func (t *BerlekampWelchTest) SomeShares(block int) ([]byte, []Share) { 221 | // seed the initial data 222 | data := make([]byte, t.code.k*block) 223 | for i := range data { 224 | data[i] = byte(i + 1) 225 | } 226 | 227 | shares, store := t.StoreShares() 228 | t.AssertNoError(t.code.Encode(data, store)) 229 | return data, shares 230 | } 231 | 232 | func (t *BerlekampWelchTest) CopyShares(shares []Share) []Share { 233 | out := make([]Share, t.code.n) 234 | for i, share := range shares { 235 | out[i] = share.DeepCopy() 236 | } 237 | return out 238 | } 239 | 240 | func (t *BerlekampWelchTest) MutateShare(idx int, share Share) { 241 | orig := share.Data[idx] 242 | next := byte(rand.Intn(256)) 243 | for next == orig { 244 | next = byte(rand.Intn(256)) 245 | } 246 | share.Data[idx] = next 247 | } 248 | 249 | func (t *BerlekampWelchTest) PermuteShares(shares []Share) { 250 | for i := 0; i < len(shares); i++ { 251 | with := rand.Intn(len(shares)-i) + i 252 | shares[i], shares[with] = shares[with], shares[i] 253 | } 254 | } 255 | 256 | func (t *BerlekampWelchTest) DataDiff(a, b []byte) []byte { 257 | out := make([]byte, len(a)) 258 | for i := range out { 259 | if a[i] != b[i] { 260 | out[i] = 0xff 261 | } 262 | } 263 | return out 264 | } 265 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // Package infectious implements Reed-Solomon forward error correction [1]. It 24 | // uses the Berlekamp-Welch [2] error correction algorithm to achieve the 25 | // ability to actually correct errors. 26 | // 27 | // Caution: this package API leans toward providing the user more power and 28 | // performance at the expense of having some really sharp edges! Read the 29 | // documentation about memory lifecycles carefully! 30 | // 31 | // We wrote a blog post about how this library works! 32 | // https://innovation.vivint.com/introduction-to-reed-solomon-bc264d0794f8 33 | // 34 | // [1] https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction 35 | // [2] https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Welch_algorithm 36 | package infectious 37 | 38 | import ( 39 | "errors" 40 | 41 | "golang.org/x/sys/cpu" 42 | ) 43 | 44 | var hasAVX2 = cpu.X86.HasAVX2 45 | var hasSSSE3 = cpu.X86.HasSSSE3 46 | 47 | var ( 48 | NotEnoughShares = errors.New("not enough shares") 49 | TooManyErrors = errors.New("too many errors to reconstruct") 50 | ) 51 | -------------------------------------------------------------------------------- /common_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import ( 26 | crand "crypto/rand" 27 | "fmt" 28 | "math/rand" 29 | "reflect" 30 | "testing" 31 | "time" 32 | ) 33 | 34 | func RandomBytes(size int) []byte { 35 | buf := make([]byte, size) 36 | _, err := crand.Read(buf) 37 | if err != nil { 38 | panic(fmt.Sprintf("rand.Read failed: %s", err)) 39 | } 40 | return buf 41 | } 42 | 43 | // we want all of our test runs to be with a different seed 44 | func init() { rand.Seed(int64(time.Now().UnixNano())) } 45 | 46 | type Asserter struct { 47 | tb testing.TB 48 | } 49 | 50 | func Wrap(tb testing.TB) *Asserter { 51 | return &Asserter{ 52 | tb: tb, 53 | } 54 | } 55 | 56 | func (a *Asserter) AssertNoError(err error) { 57 | if err != nil { 58 | a.tb.Fatalf("expected no error; got %v", err) 59 | } 60 | } 61 | 62 | func (a *Asserter) AssertDeepEqual(x, y interface{}) { 63 | if !reflect.DeepEqual(x, y) { 64 | a.tb.Fatalf("expected\n%#v\n%#v\nto be equal", x, y) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious_test 24 | 25 | import ( 26 | "fmt" 27 | "testing" 28 | 29 | "github.com/vivint/infectious" 30 | ) 31 | 32 | func Example(t *testing.T) { 33 | const ( 34 | required = 8 35 | total = 14 36 | ) 37 | 38 | // Create a *FEC, which will require required pieces for reconstruction at 39 | // minimum, and generate total total pieces. 40 | f, err := infectious.NewFEC(required, total) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | // Prepare to receive the shares of encoded data. 46 | shares := make([]infectious.Share, total) 47 | output := func(s infectious.Share) { 48 | // the memory in s gets reused, so we need to make a deep copy 49 | shares[s.Number] = s.DeepCopy() 50 | } 51 | 52 | // the data to encode must be padded to a multiple of required, hence the 53 | // underscores. 54 | err = f.Encode([]byte("hello, world! __"), output) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | // we now have total shares. 60 | for _, share := range shares { 61 | fmt.Printf("%d: %#v\n", share.Number, string(share.Data)) 62 | } 63 | 64 | // Let's reconstitute with two pieces missing and one piece corrupted. 65 | shares = shares[2:] // drop the first two pieces 66 | shares[2].Data[1] = '!' // mutate some data 67 | 68 | result, err := f.Decode(nil, shares) 69 | if err != nil { 70 | panic(err) 71 | } 72 | 73 | // we have the original data! 74 | fmt.Printf("got: %#v\n", string(result)) 75 | } 76 | -------------------------------------------------------------------------------- /fec.go: -------------------------------------------------------------------------------- 1 | // (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it) 2 | // 2009-2010 Jack Lloyd (lloyd@randombit.net) 3 | // 2011 Billy Brumley (billy.brumley@aalto.fi) 4 | // 2016-2017 Vivint, Inc. (jeff.wendling@vivint.com) 5 | // 6 | // Portions derived from code by Phil Karn (karn@ka9q.ampr.org), 7 | // Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari 8 | // Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 9 | // 10 | // Redistribution and use in source and binary forms, with or without 11 | // modification, are permitted provided that the following conditions are 12 | // met: 13 | // 14 | // 1. Redistributions of source code must retain the above copyright 15 | // notice, this list of conditions and the following disclaimer. 16 | // 17 | // 2. Redistributions in binary form must reproduce the above copyright 18 | // notice, this list of conditions and the following disclaimer in the 19 | // documentation and/or other materials provided with the 20 | // distribution. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 23 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | // DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 31 | // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | // POSSIBILITY OF SUCH DAMAGE. 33 | 34 | package infectious 35 | 36 | import ( 37 | "errors" 38 | "fmt" 39 | "sort" 40 | ) 41 | 42 | // FEC represents operations performed on a Reed-Solomon-based 43 | // forward error correction code. Make sure to construct using NewFEC. 44 | type FEC struct { 45 | k int 46 | n int 47 | enc_matrix []byte 48 | vand_matrix []byte 49 | } 50 | 51 | // NewFEC creates a *FEC using k required pieces and n total pieces. 52 | // Encoding data with this *FEC will generate n pieces, and decoding 53 | // data requires k uncorrupted pieces. If during decode more than k pieces 54 | // exist, corrupted data can be detected and recovered from. 55 | func NewFEC(k, n int) (*FEC, error) { 56 | if k <= 0 || n <= 0 || k > 256 || n > 256 || k > n { 57 | return nil, errors.New("requires 1 <= k <= n <= 256") 58 | } 59 | 60 | enc_matrix := make([]byte, n*k) 61 | temp_matrix := make([]byte, n*k) 62 | createInvertedVdm(temp_matrix, k) 63 | 64 | for i := k * k; i < len(temp_matrix); i++ { 65 | temp_matrix[i] = gf_exp[((i/k)*(i%k))%255] 66 | } 67 | 68 | for i := 0; i < k; i++ { 69 | enc_matrix[i*(k+1)] = 1 70 | } 71 | 72 | for row := k * k; row < n*k; row += k { 73 | for col := 0; col < k; col++ { 74 | pa := temp_matrix[row:] 75 | pb := temp_matrix[col:] 76 | acc := byte(0) 77 | for i := 0; i < k; i, pa, pb = i+1, pa[1:], pb[k:] { 78 | acc ^= gf_mul_table[pa[0]][pb[0]] 79 | } 80 | enc_matrix[row+col] = acc 81 | } 82 | } 83 | 84 | // vand_matrix has more columns than rows 85 | // k rows, n columns. 86 | vand_matrix := make([]byte, k*n) 87 | vand_matrix[0] = 1 88 | g := byte(1) 89 | for row := 0; row < k; row++ { 90 | a := byte(1) 91 | for col := 1; col < n; col++ { 92 | vand_matrix[row*n+col] = a // 2.pow(i * j) FIGURE IT OUT 93 | a = gf_mul_table[g][a] 94 | } 95 | g = gf_mul_table[2][g] 96 | } 97 | 98 | return &FEC{ 99 | k: k, 100 | n: n, 101 | enc_matrix: enc_matrix, 102 | vand_matrix: vand_matrix, 103 | }, nil 104 | } 105 | 106 | // Required returns the number of required pieces for reconstruction. This is 107 | // the k value passed to NewFEC. 108 | func (f *FEC) Required() int { 109 | return f.k 110 | } 111 | 112 | // Total returns the number of total pieces that will be generated during 113 | // encoding. This is the n value passed to NewFEC. 114 | func (f *FEC) Total() int { 115 | return f.n 116 | } 117 | 118 | // Encode will take input data and encode to the total number of pieces n this 119 | // *FEC is configured for. It will call the callback output n times. 120 | // 121 | // The input data must be a multiple of the required number of pieces k. 122 | // Padding to this multiple is up to the caller. 123 | // 124 | // Note that the byte slices in Shares passed to output may be reused when 125 | // output returns. 126 | func (f *FEC) Encode(input []byte, output func(Share)) error { 127 | size := len(input) 128 | 129 | k := f.k 130 | n := f.n 131 | enc_matrix := f.enc_matrix 132 | 133 | if size%k != 0 { 134 | return fmt.Errorf("input length must be a multiple of %d", k) 135 | } 136 | 137 | block_size := size / k 138 | 139 | for i := 0; i < k; i++ { 140 | output(Share{ 141 | Number: i, 142 | Data: input[i*block_size : i*block_size+block_size]}) 143 | } 144 | 145 | fec_buf := make([]byte, block_size) 146 | for i := k; i < n; i++ { 147 | for j := range fec_buf { 148 | fec_buf[j] = 0 149 | } 150 | 151 | for j := 0; j < k; j++ { 152 | addmul(fec_buf, input[j*block_size:j*block_size+block_size], 153 | enc_matrix[i*k+j]) 154 | } 155 | 156 | output(Share{ 157 | Number: i, 158 | Data: fec_buf}) 159 | } 160 | return nil 161 | } 162 | 163 | // EncodeSingle will take input data and encode it to output only for the num 164 | // piece. 165 | // 166 | // The input data must be a multiple of the required number of pieces k. 167 | // Padding to this multiple is up to the caller. 168 | // 169 | // The output must be exactly len(input) / k bytes. 170 | // 171 | // The num must be 0 <= num < n. 172 | func (f *FEC) EncodeSingle(input, output []byte, num int) error { 173 | size := len(input) 174 | 175 | k := f.k 176 | n := f.n 177 | enc_matrix := f.enc_matrix 178 | 179 | if num < 0 { 180 | return errors.New("num must be non-negative") 181 | } 182 | 183 | if num >= n { 184 | return fmt.Errorf("num must be less than %d", n) 185 | } 186 | 187 | if size%k != 0 { 188 | return fmt.Errorf("input length must be a multiple of %d", k) 189 | } 190 | 191 | block_size := size / k 192 | 193 | if len(output) != block_size { 194 | return fmt.Errorf("output length must be %d", block_size) 195 | } 196 | 197 | if num < k { 198 | copy(output, input[num*block_size:]) 199 | return nil 200 | } 201 | 202 | for i := range output { 203 | output[i] = 0 204 | } 205 | 206 | for i := 0; i < k; i++ { 207 | addmul(output, input[i*block_size:i*block_size+block_size], 208 | enc_matrix[num*k+i]) 209 | } 210 | 211 | return nil 212 | } 213 | 214 | // A Share represents a piece of the FEC-encoded data. 215 | // Both fields are required. 216 | type Share struct { 217 | Number int 218 | Data []byte 219 | } 220 | 221 | // DeepCopy makes getting a deep copy of a Share easier. It will return an 222 | // identical Share that uses all new memory locations. 223 | func (s *Share) DeepCopy() (c Share) { 224 | c.Number = s.Number 225 | c.Data = append([]byte(nil), s.Data...) 226 | return c 227 | } 228 | 229 | type byNumber []Share 230 | 231 | func (b byNumber) Len() int { return len(b) } 232 | func (b byNumber) Less(i int, j int) bool { return b[i].Number < b[j].Number } 233 | func (b byNumber) Swap(i int, j int) { b[i], b[j] = b[j], b[i] } 234 | 235 | // Rebuild will take a list of corrected shares (pieces) and a callback output. 236 | // output will be called k times ((*FEC).Required() times) with 1/k of the 237 | // original data each time and the index of that data piece. 238 | // Decode is usually preferred. 239 | // 240 | // Note that the data is not necessarily sent to output ordered by the piece 241 | // number. 242 | // 243 | // Note that the byte slices in Shares passed to output may be reused when 244 | // output returns. 245 | // 246 | // Rebuild assumes that you have already called Correct or did not need to. 247 | func (f *FEC) Rebuild(shares []Share, output func(Share)) error { 248 | k := f.k 249 | n := f.n 250 | enc_matrix := f.enc_matrix 251 | 252 | if len(shares) < k { 253 | return NotEnoughShares 254 | } 255 | 256 | share_size := len(shares[0].Data) 257 | sort.Sort(byNumber(shares)) 258 | 259 | m_dec := make([]byte, k*k) 260 | indexes := make([]int, k) 261 | sharesv := make([][]byte, k) 262 | 263 | shares_b_iter := 0 264 | shares_e_iter := len(shares) - 1 265 | 266 | for i := 0; i < k; i++ { 267 | var share_id int 268 | var share_data []byte 269 | 270 | if share := shares[shares_b_iter]; share.Number == i { 271 | share_id = share.Number 272 | share_data = share.Data 273 | shares_b_iter++ 274 | } else { 275 | share := shares[shares_e_iter] 276 | share_id = share.Number 277 | share_data = share.Data 278 | shares_e_iter-- 279 | } 280 | 281 | if share_id >= n { 282 | return fmt.Errorf("invalid share id: %d", share_id) 283 | } 284 | 285 | if share_id < k { 286 | m_dec[i*(k+1)] = 1 287 | if output != nil { 288 | output(Share{ 289 | Number: share_id, 290 | Data: share_data}) 291 | } 292 | } else { 293 | copy(m_dec[i*k:i*k+k], enc_matrix[share_id*k:]) 294 | } 295 | 296 | sharesv[i] = share_data 297 | indexes[i] = share_id 298 | } 299 | 300 | if err := invertMatrix(m_dec, k); err != nil { 301 | return err 302 | } 303 | 304 | buf := make([]byte, share_size) 305 | for i := 0; i < len(indexes); i++ { 306 | if indexes[i] >= k { 307 | for j := range buf { 308 | buf[j] = 0 309 | } 310 | 311 | for col := 0; col < k; col++ { 312 | addmul(buf, sharesv[col], m_dec[i*k+col]) 313 | } 314 | 315 | if output != nil { 316 | output(Share{ 317 | Number: i, 318 | Data: buf}) 319 | } 320 | } 321 | } 322 | return nil 323 | } 324 | -------------------------------------------------------------------------------- /fec_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import ( 26 | "bytes" 27 | "fmt" 28 | "math/rand" 29 | "strconv" 30 | "testing" 31 | ) 32 | 33 | func TestBasicOperation(t *testing.T) { 34 | const block = 1024 * 1024 35 | const total, required = 40, 20 36 | 37 | code, err := NewFEC(required, total) 38 | if err != nil { 39 | t.Fatalf("failed to create new fec code: %s", err) 40 | } 41 | 42 | // seed the initial data 43 | data := make([]byte, required*block) 44 | for i := range data { 45 | data[i] = byte(i) 46 | } 47 | 48 | // encode it and store to outputs 49 | var outputs = make(map[int][]byte) 50 | store := func(s Share) { 51 | outputs[s.Number] = s.DeepCopy().Data 52 | } 53 | err = code.Encode(data[:], store) 54 | if err != nil { 55 | t.Fatalf("encode failed: %s", err) 56 | } 57 | 58 | // pick required of the total shares randomly 59 | var shares [required]Share 60 | for i, idx := range rand.Perm(total)[:required] { 61 | shares[i].Number = idx 62 | shares[i].Data = outputs[idx] 63 | } 64 | 65 | got := make([]byte, required*block) 66 | record := func(s Share) { 67 | copy(got[s.Number*block:], s.Data) 68 | } 69 | err = code.Rebuild(shares[:], record) 70 | if err != nil { 71 | t.Fatalf("decode failed: %s", err) 72 | } 73 | 74 | if !bytes.Equal(got, data) { 75 | t.Fatalf("did not match") 76 | } 77 | } 78 | 79 | func TestEncodeSingle(t *testing.T) { 80 | const block = 1024 * 1024 81 | const total, required = 40, 20 82 | 83 | code, err := NewFEC(required, total) 84 | if err != nil { 85 | t.Fatalf("failed to create new fec code: %s", err) 86 | } 87 | 88 | // seed the initial data 89 | data := make([]byte, required*block) 90 | for i := range data { 91 | data[i] = byte(i) 92 | } 93 | 94 | // encode it and store to outputs 95 | var outputs = make(map[int][]byte) 96 | for i := 0; i < total; i++ { 97 | outputs[i] = make([]byte, block) 98 | err = code.EncodeSingle(data[:], outputs[i], i) 99 | if err != nil { 100 | t.Fatalf("encode failed: %s", err) 101 | } 102 | } 103 | 104 | // pick required of the total shares randomly 105 | var shares [required]Share 106 | for i, idx := range rand.Perm(total)[:required] { 107 | shares[i].Number = idx 108 | shares[i].Data = outputs[idx] 109 | } 110 | 111 | got := make([]byte, required*block) 112 | record := func(s Share) { 113 | copy(got[s.Number*block:], s.Data) 114 | } 115 | err = code.Rebuild(shares[:], record) 116 | if err != nil { 117 | t.Fatalf("decode failed: %s", err) 118 | } 119 | 120 | if !bytes.Equal(got, data) { 121 | t.Fatalf("did not match") 122 | } 123 | } 124 | 125 | func BenchmarkEncode(b *testing.B) { 126 | const block = 1024 * 1024 127 | const total, required = 40, 20 128 | 129 | code, err := NewFEC(required, total) 130 | if err != nil { 131 | b.Fatalf("failed to create new fec code: %s", err) 132 | } 133 | 134 | // seed the initial data 135 | data := make([]byte, required*block) 136 | for i := range data { 137 | data[i] = byte(i) 138 | } 139 | store := func(Share) {} 140 | 141 | b.SetBytes(block * required) 142 | b.ReportAllocs() 143 | b.ResetTimer() 144 | 145 | for i := 0; i < b.N; i++ { 146 | code.Encode(data, store) 147 | } 148 | } 149 | 150 | func BenchmarkEncodeSingle(b *testing.B) { 151 | const block = 1024 * 1024 152 | const total, required = 40, 20 153 | 154 | code, err := NewFEC(required, total) 155 | if err != nil { 156 | b.Fatalf("failed to create new fec code: %s", err) 157 | } 158 | 159 | // seed the initial data 160 | data := make([]byte, required*block) 161 | for i := range data { 162 | data[i] = byte(i) 163 | } 164 | 165 | output := make([]byte, block) 166 | 167 | b.SetBytes(block * required) 168 | b.ReportAllocs() 169 | b.ResetTimer() 170 | 171 | for i := 0; i < b.N; i++ { 172 | for j := 0; j < total; j++ { 173 | code.EncodeSingle(data, output, j) 174 | } 175 | } 176 | } 177 | 178 | func BenchmarkRebuild(b *testing.B) { 179 | const block = 4096 180 | const total, required = 40, 20 181 | 182 | code, err := NewFEC(required, total) 183 | if err != nil { 184 | b.Fatalf("failed to create new fec code: %s", err) 185 | } 186 | 187 | // seed the initial data 188 | data := make([]byte, required*block) 189 | for i := range data { 190 | data[i] = byte(i) 191 | } 192 | shares := make([]Share, total) 193 | store := func(s Share) { 194 | idx := s.Number 195 | shares[idx].Number = idx 196 | shares[idx].Data = append(shares[idx].Data, s.Data...) 197 | } 198 | err = code.Encode(data, store) 199 | if err != nil { 200 | b.Fatalf("failed to encode: %s", err) 201 | } 202 | 203 | dec_shares := shares[total-required:] 204 | 205 | b.SetBytes(block * required) 206 | b.ReportAllocs() 207 | b.ResetTimer() 208 | 209 | for i := 0; i < b.N; i++ { 210 | code.Rebuild(dec_shares, nil) 211 | } 212 | } 213 | 214 | func BenchmarkMultiple(b *testing.B) { 215 | b.ReportAllocs() 216 | data := make([]byte, 8<<20) 217 | output := make([]byte, 8<<20) 218 | 219 | confs := []struct{ required, total int }{ 220 | {2, 4}, 221 | {20, 50}, 222 | {30, 60}, 223 | {50, 80}, 224 | } 225 | 226 | dataSizes := []int{ 227 | 64, 228 | 128, 229 | 256, 230 | 512, 231 | 1 << 10, 232 | 256 << 10, 233 | 1 << 20, 234 | 5 << 20, 235 | 8 << 20, 236 | } 237 | 238 | for _, conf := range confs { 239 | confname := fmt.Sprintf("r%dt%d/", conf.required, conf.total) 240 | for _, tryDataSize := range dataSizes { 241 | dataSize := (tryDataSize / conf.required) * conf.required 242 | var testname string 243 | if dataSize < 1024 { 244 | testname = strconv.Itoa(dataSize) + "B" 245 | } else { 246 | testname = strconv.Itoa(dataSize/1024) + "KB" 247 | } 248 | fec, _ := NewFEC(conf.required, conf.total) 249 | 250 | b.Run("Encode/"+confname+testname, func(b *testing.B) { 251 | b.ReportAllocs() 252 | b.SetBytes(int64(dataSize)) 253 | for i := 0; i < b.N; i++ { 254 | err := fec.Encode(data[:dataSize], func(share Share) {}) 255 | if err != nil { 256 | b.Fatal(err) 257 | } 258 | } 259 | }) 260 | 261 | b.Run("Single/"+confname+testname, func(b *testing.B) { 262 | b.ReportAllocs() 263 | b.SetBytes(int64(dataSize)) 264 | output := make([]byte, dataSize/conf.required) 265 | for i := 0; i < b.N; i++ { 266 | for j := 0; j < conf.total; j++ { 267 | err := fec.EncodeSingle(data[:dataSize], output, j) 268 | if err != nil { 269 | b.Fatal(err) 270 | } 271 | } 272 | } 273 | }) 274 | 275 | shares := []Share{} 276 | err := fec.Encode(data[:dataSize], func(share Share) { 277 | shares = append(shares, share.DeepCopy()) 278 | }) 279 | if err != nil { 280 | b.Fatal(err) 281 | } 282 | 283 | b.Run("Decode/"+confname+testname, func(b *testing.B) { 284 | b.ReportAllocs() 285 | b.SetBytes(int64(dataSize)) 286 | for i := 0; i < b.N; i++ { 287 | rand.Shuffle(len(shares), func(i, k int) { 288 | shares[i], shares[k] = shares[k], shares[i] 289 | }) 290 | 291 | offset := i % (conf.total / 4) 292 | n := conf.required + 1 + offset 293 | if n > conf.total { 294 | n = conf.total 295 | } 296 | 297 | _, err = fec.Decode(output[:dataSize], shares[:n]) 298 | if err != nil { 299 | b.Fatal(err) 300 | } 301 | } 302 | }) 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /gf_alg.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import ( 26 | "errors" 27 | "fmt" 28 | "strings" 29 | "unsafe" 30 | ) 31 | 32 | // 33 | // basic helpers around gf(2^8) values 34 | // 35 | 36 | type gfVal byte 37 | 38 | func gfConst(val byte) gfVal { 39 | return gfVal(val) 40 | } 41 | 42 | func (b gfVal) pow(val int) gfVal { 43 | out := gfVal(1) 44 | mul_base := gf_mul_table[b][:] 45 | for i := 0; i < val; i++ { 46 | out = gfVal(mul_base[out]) 47 | } 48 | return out 49 | } 50 | 51 | func (a gfVal) mul(b gfVal) gfVal { 52 | return gfVal(gf_mul_table[a][b]) 53 | } 54 | 55 | func (a gfVal) div(b gfVal) (gfVal, error) { 56 | if b == 0 { 57 | return 0, errors.New("divide by zero") 58 | } 59 | if a == 0 { 60 | return 0, nil 61 | } 62 | return gfVal(gf_exp[gf_log[a]-gf_log[b]]), nil 63 | } 64 | 65 | func (a gfVal) add(b gfVal) gfVal { 66 | return gfVal(a ^ b) 67 | } 68 | 69 | func (a gfVal) isZero() bool { 70 | return a == 0 71 | } 72 | 73 | func (a gfVal) inv() (gfVal, error) { 74 | if a == 0 { 75 | return 0, errors.New("invert zero") 76 | } 77 | return gfVal(gf_exp[255-gf_log[a]]), nil 78 | } 79 | 80 | // 81 | // basic helpers about a slice of gf(2^8) values 82 | // 83 | 84 | type gfVals []gfVal 85 | 86 | func (a gfVals) unsafeBytes() []byte { 87 | return *(*[]byte)(unsafe.Pointer(&a)) 88 | } 89 | 90 | func (a gfVals) dot(b gfVals) gfVal { 91 | out := gfConst(0) 92 | for i := range a { 93 | out = out.add(a[i].mul(b[i])) 94 | } 95 | return out 96 | } 97 | 98 | func (a gfVals) String() string { 99 | return fmt.Sprintf("%02x", a.unsafeBytes()) 100 | } 101 | 102 | // 103 | // basic helpers for dealing with polynomials with coefficients in gf(2^8) 104 | // 105 | 106 | type gfPoly []gfVal 107 | 108 | func polyZero(size int) gfPoly { 109 | out := make(gfPoly, size) 110 | for i := range out { 111 | out[i] = gfConst(0) 112 | } 113 | return out 114 | } 115 | 116 | func (p gfPoly) isZero() bool { 117 | for _, coef := range p { 118 | if !coef.isZero() { 119 | return false 120 | } 121 | } 122 | return true 123 | } 124 | 125 | func (p gfPoly) deg() int { 126 | return len(p) - 1 127 | } 128 | 129 | func (p gfPoly) index(power int) gfVal { 130 | if power < 0 { 131 | return gfConst(0) 132 | } 133 | which := p.deg() - power 134 | if which < 0 { 135 | return gfConst(0) 136 | } 137 | return p[which] 138 | } 139 | 140 | func (p gfPoly) scale(factor gfVal) gfPoly { 141 | out := make(gfPoly, len(p)) 142 | for i, coef := range p { 143 | out[i] = coef.mul(factor) 144 | } 145 | return out 146 | } 147 | 148 | func (p *gfPoly) set(pow int, coef gfVal) { 149 | which := p.deg() - pow 150 | if which < 0 { 151 | *p = append(polyZero(-which), *p...) 152 | which = p.deg() - pow 153 | } 154 | (*p)[which] = coef 155 | } 156 | 157 | func (p gfPoly) add(b gfPoly) gfPoly { 158 | size := len(p) 159 | if lb := len(b); lb > size { 160 | size = lb 161 | } 162 | out := make(gfPoly, size) 163 | for i := range out { 164 | pi := p.index(i) 165 | bi := b.index(i) 166 | out.set(i, pi.add(bi)) 167 | } 168 | return out 169 | } 170 | 171 | func (p gfPoly) div(b gfPoly) (q, r gfPoly, err error) { 172 | // sanitize the divisor by removing leading zeros. 173 | for len(b) > 0 && b[0].isZero() { 174 | b = b[1:] 175 | } 176 | if len(b) == 0 { 177 | return nil, nil, errors.New("divide by zero") 178 | } 179 | 180 | // sanitize the base poly as well 181 | for len(p) > 0 && p[0].isZero() { 182 | p = p[1:] 183 | } 184 | if len(p) == 0 { 185 | return polyZero(1), polyZero(1), nil 186 | } 187 | 188 | const debug = false 189 | indent := 2*len(b) + 1 190 | 191 | if debug { 192 | fmt.Printf("%02x %02x\n", b, p) 193 | } 194 | 195 | for b.deg() <= p.deg() { 196 | leading_p := p.index(p.deg()) 197 | leading_b := b.index(b.deg()) 198 | 199 | if debug { 200 | fmt.Printf("leading_p: %02x leading_b: %02x\n", 201 | leading_p, leading_b) 202 | } 203 | 204 | coef, err := leading_p.div(leading_b) 205 | if err != nil { 206 | return nil, nil, err 207 | } 208 | 209 | if debug { 210 | fmt.Printf("coef: %02x\n", coef) 211 | } 212 | 213 | q = append(q, coef) 214 | 215 | scaled := b.scale(coef) 216 | padded := append(scaled, polyZero(p.deg()-scaled.deg())...) 217 | 218 | if debug { 219 | fmt.Printf("%s%02x\n", strings.Repeat(" ", indent), padded) 220 | indent += 2 221 | } 222 | 223 | p = p.add(padded) 224 | if !p[0].isZero() { 225 | return nil, nil, fmt.Errorf("alg error: %x", p) 226 | } 227 | p = p[1:] 228 | } 229 | 230 | for len(p) > 1 && p[0].isZero() { 231 | p = p[1:] 232 | } 233 | 234 | return q, p, nil 235 | } 236 | 237 | func (p gfPoly) eval(x gfVal) gfVal { 238 | out := gfConst(0) 239 | for i := 0; i <= p.deg(); i++ { 240 | x_i := x.pow(i) 241 | p_i := p.index(i) 242 | out = out.add(p_i.mul(x_i)) 243 | } 244 | return out 245 | } 246 | 247 | // 248 | // basic helpers for matrices in gf(2^8) 249 | // 250 | 251 | type gfMat struct { 252 | d gfVals 253 | r, c int 254 | } 255 | 256 | func matrixNew(i, j int) gfMat { 257 | return gfMat{ 258 | d: make(gfVals, i*j), 259 | r: i, c: j, 260 | } 261 | } 262 | 263 | func (m gfMat) String() (out string) { 264 | if m.r == 0 { 265 | return "" 266 | } 267 | 268 | for i := 0; i < m.r-1; i++ { 269 | out += fmt.Sprintln(m.indexRow(i)) 270 | } 271 | out += fmt.Sprint(m.indexRow(m.r - 1)) 272 | 273 | return out 274 | } 275 | 276 | func (m gfMat) index(i, j int) int { 277 | return m.c*i + j 278 | } 279 | 280 | func (m gfMat) get(i, j int) gfVal { 281 | return m.d[m.index(i, j)] 282 | } 283 | 284 | func (m gfMat) set(i, j int, val gfVal) { 285 | m.d[m.index(i, j)] = val 286 | } 287 | 288 | func (m gfMat) indexRow(i int) gfVals { 289 | return m.d[m.index(i, 0):m.index(i+1, 0)] 290 | } 291 | 292 | func (m gfMat) swapRow(i, j int) { 293 | tmp := make(gfVals, m.r) 294 | ri := m.indexRow(i) 295 | rj := m.indexRow(j) 296 | copy(tmp, ri) 297 | copy(ri, rj) 298 | copy(rj, tmp) 299 | } 300 | 301 | func (m gfMat) scaleRow(i int, val gfVal) { 302 | ri := m.indexRow(i) 303 | for i := range ri { 304 | ri[i] = ri[i].mul(val) 305 | } 306 | } 307 | 308 | func (m gfMat) addmulRow(i, j int, val gfVal) { 309 | ri := m.indexRow(i) 310 | rj := m.indexRow(j) 311 | addmul(rj.unsafeBytes(), ri.unsafeBytes(), byte(val)) 312 | } 313 | 314 | // in place invert. the output is put into a and m is turned into the identity 315 | // matrix. a is expected to be the identity matrix. 316 | func (m gfMat) invertWith(a gfMat) error { 317 | for i := 0; i < m.r; i++ { 318 | p_row, p_val := i, m.get(i, i) 319 | for j := i + 1; j < m.r && p_val.isZero(); j++ { 320 | p_row, p_val = j, m.get(j, i) 321 | } 322 | if p_val.isZero() { 323 | continue 324 | } 325 | 326 | if p_row != i { 327 | m.swapRow(i, p_row) 328 | a.swapRow(i, p_row) 329 | } 330 | 331 | inv, err := p_val.inv() 332 | if err != nil { 333 | return err 334 | } 335 | m.scaleRow(i, inv) 336 | a.scaleRow(i, inv) 337 | 338 | for j := i + 1; j < m.r; j++ { 339 | leading := m.get(j, i) 340 | m.addmulRow(i, j, leading) 341 | a.addmulRow(i, j, leading) 342 | } 343 | } 344 | 345 | for i := m.r - 1; i > 0; i-- { 346 | for j := i - 1; j >= 0; j-- { 347 | trailing := m.get(j, i) 348 | m.addmulRow(i, j, trailing) 349 | a.addmulRow(i, j, trailing) 350 | } 351 | } 352 | 353 | return nil 354 | } 355 | 356 | // in place standardize. 357 | func (m gfMat) standardize() error { 358 | for i := 0; i < m.r; i++ { 359 | p_row, p_val := i, m.get(i, i) 360 | for j := i + 1; j < m.r && p_val.isZero(); j++ { 361 | p_row, p_val = j, m.get(j, i) 362 | } 363 | if p_val.isZero() { 364 | continue 365 | } 366 | 367 | if p_row != i { 368 | m.swapRow(i, p_row) 369 | } 370 | 371 | inv, err := p_val.inv() 372 | if err != nil { 373 | return err 374 | } 375 | m.scaleRow(i, inv) 376 | 377 | for j := i + 1; j < m.r; j++ { 378 | leading := m.get(j, i) 379 | m.addmulRow(i, j, leading) 380 | } 381 | } 382 | 383 | for i := m.r - 1; i > 0; i-- { 384 | for j := i - 1; j >= 0; j-- { 385 | trailing := m.get(j, i) 386 | m.addmulRow(i, j, trailing) 387 | } 388 | } 389 | 390 | return nil 391 | } 392 | 393 | // parity returns the new matrix because it changes dimensions and stuff. it 394 | // can be done in place, but is easier to implement with a copy. 395 | func (m gfMat) parity() gfMat { 396 | // we assume m is in standard form already 397 | // it is of form [I_r | P] 398 | // our output will be [-P_transpose | I_(c - r)] 399 | // but our field is of characteristic 2 so we do not need the negative. 400 | 401 | // In terms of m: 402 | // I_r has r rows and r columns. 403 | // P has r rows and c-r columns. 404 | // P_transpose has c-r rows, and r columns. 405 | // I_(c-r) has c-r rows and c-r columns. 406 | // so: out.r == c-r, out.c == r + c - r == c 407 | 408 | out := matrixNew(m.c-m.r, m.c) 409 | 410 | // step 1. fill in the identity. it starts at column offset r. 411 | for i := 0; i < m.c-m.r; i++ { 412 | out.set(i, i+m.r, gfConst(1)) 413 | } 414 | 415 | // step 2: fill in the transposed P matrix. i and j are in terms of out. 416 | for i := 0; i < m.c-m.r; i++ { 417 | for j := 0; j < m.r; j++ { 418 | out.set(i, j, m.get(j, i+m.r)) 419 | } 420 | } 421 | 422 | return out 423 | } 424 | -------------------------------------------------------------------------------- /gf_alg_test.go: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (C) 2016-2017 Vivint, Inc. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package infectious 24 | 25 | import "testing" 26 | 27 | func TestGFPolyDiv(t *testing.T) { 28 | q := gfPoly{ 29 | 0x5e, 0x60, 0x8c, 0x3d, 0xc6, 0x8e, 0x7e, 0xa5, 0x2c, 0xa4, 0x04, 0x8a, 30 | 0x2b, 0xc2, 0x36, 0x0f, 0xfc, 0x3f, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 31 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 32 | } 33 | e := gfPoly{ 34 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | } 36 | 37 | _, _, err := q.div(e) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vivint/infectious 2 | 3 | go 1.12 4 | 5 | require golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 2 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 3 | -------------------------------------------------------------------------------- /math.go: -------------------------------------------------------------------------------- 1 | // (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it) 2 | // 2009-2010 Jack Lloyd (lloyd@randombit.net) 3 | // 2011 Billy Brumley (billy.brumley@aalto.fi) 4 | // 2016-2017 Vivint, Inc. (jeff.wendling@vivint.com) 5 | // 6 | // Portions derived from code by Phil Karn (karn@ka9q.ampr.org), 7 | // Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari 8 | // Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 9 | // 10 | // Redistribution and use in source and binary forms, with or without 11 | // modification, are permitted provided that the following conditions are 12 | // met: 13 | // 14 | // 1. Redistributions of source code must retain the above copyright 15 | // notice, this list of conditions and the following disclaimer. 16 | // 17 | // 2. Redistributions in binary form must reproduce the above copyright 18 | // notice, this list of conditions and the following disclaimer in the 19 | // documentation and/or other materials provided with the 20 | // distribution. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 23 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | // DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 31 | // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | // POSSIBILITY OF SUCH DAMAGE. 33 | 34 | package infectious 35 | 36 | import ( 37 | "bytes" 38 | "errors" 39 | ) 40 | 41 | type pivotSearcher struct { 42 | k int 43 | ipiv []bool 44 | } 45 | 46 | func newPivotSearcher(k int) *pivotSearcher { 47 | return &pivotSearcher{ 48 | k: k, 49 | ipiv: make([]bool, k), 50 | } 51 | } 52 | 53 | func (p *pivotSearcher) search(col int, matrix []byte) (int, int, error) { 54 | if p.ipiv[col] == false && matrix[col*p.k+col] != 0 { 55 | p.ipiv[col] = true 56 | return col, col, nil 57 | } 58 | 59 | for row := 0; row < p.k; row++ { 60 | if p.ipiv[row] { 61 | continue 62 | } 63 | 64 | for i := 0; i < p.k; i++ { 65 | if p.ipiv[i] == false && matrix[row*p.k+i] != 0 { 66 | p.ipiv[i] = true 67 | return row, i, nil 68 | } 69 | } 70 | } 71 | 72 | return 0, 0, errors.New("pivot not found") 73 | } 74 | 75 | func swap(a, b *byte) { 76 | tmp := *a 77 | *a = *b 78 | *b = tmp 79 | } 80 | 81 | // TODO(jeff): matrix is a K*K array, row major. 82 | func invertMatrix(matrix []byte, k int) error { 83 | pivot_searcher := newPivotSearcher(k) 84 | indxc := make([]int, k) 85 | indxr := make([]int, k) 86 | id_row := make([]byte, k) 87 | 88 | for col := 0; col < k; col++ { 89 | icol, irow, err := pivot_searcher.search(col, matrix) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | if irow != icol { 95 | for i := 0; i < k; i++ { 96 | swap(&matrix[irow*k+i], &matrix[icol*k+i]) 97 | } 98 | } 99 | 100 | indxr[col] = irow 101 | indxc[col] = icol 102 | pivot_row := matrix[icol*k:][:k] 103 | c := pivot_row[icol] 104 | 105 | if c == 0 { 106 | return errors.New("singular matrix") 107 | } 108 | 109 | if c != 1 { 110 | c = gf_inverse[c] 111 | pivot_row[icol] = 1 112 | mul_c := gf_mul_table[c][:] 113 | 114 | for i := 0; i < k; i++ { 115 | pivot_row[i] = mul_c[pivot_row[i]] 116 | } 117 | } 118 | 119 | id_row[icol] = 1 120 | if !bytes.Equal(pivot_row, id_row) { 121 | p := matrix 122 | for i := 0; i < k; i++ { 123 | if i != icol { 124 | c = p[icol] 125 | p[icol] = 0 126 | addmul(p[:k], pivot_row, c) 127 | } 128 | p = p[k:] 129 | } 130 | } 131 | 132 | id_row[icol] = 0 133 | } 134 | 135 | for i := 0; i < k; i++ { 136 | if indxr[i] != indxc[i] { 137 | for row := 0; row < k; row++ { 138 | swap(&matrix[row*k+indxr[i]], &matrix[row*k+indxc[i]]) 139 | } 140 | } 141 | } 142 | return nil 143 | } 144 | 145 | func createInvertedVdm(vdm []byte, k int) { 146 | if k == 1 { 147 | vdm[0] = 1 148 | return 149 | } 150 | 151 | b := make([]byte, k) 152 | c := make([]byte, k) 153 | 154 | c[k-1] = 0 155 | for i := 1; i < k; i++ { 156 | mul_p_i := gf_mul_table[gf_exp[i]][:] 157 | for j := k - 1 - (i - 1); j < k-1; j++ { 158 | c[j] ^= mul_p_i[c[j+1]] 159 | } 160 | c[k-1] ^= gf_exp[i] 161 | } 162 | 163 | for row := 0; row < k; row++ { 164 | index := 0 165 | if row != 0 { 166 | index = int(gf_exp[row]) 167 | } 168 | mul_p_row := gf_mul_table[index][:] 169 | 170 | t := byte(1) 171 | b[k-1] = 1 172 | for i := k - 2; i >= 0; i-- { 173 | b[i] = c[i+1] ^ mul_p_row[b[i+1]] 174 | t = b[i] ^ mul_p_row[t] 175 | } 176 | 177 | mul_t_inv := gf_mul_table[gf_inverse[t]][:] 178 | for col := 0; col < k; col++ { 179 | vdm[col*k+row] = mul_t_inv[b[col]] 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /tables.go: -------------------------------------------------------------------------------- 1 | // (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it) 2 | // 2009-2010 Jack Lloyd (lloyd@randombit.net) 3 | // 2011 Billy Brumley (billy.brumley@aalto.fi) 4 | // 2016-2017 Vivint, Inc. (jeff.wendling@vivint.com) 5 | // 6 | // Portions derived from code by Phil Karn (karn@ka9q.ampr.org), 7 | // Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari 8 | // Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 9 | // 10 | // Redistribution and use in source and binary forms, with or without 11 | // modification, are permitted provided that the following conditions are 12 | // met: 13 | // 14 | // 1. Redistributions of source code must retain the above copyright 15 | // notice, this list of conditions and the following disclaimer. 16 | // 17 | // 2. Redistributions in binary form must reproduce the above copyright 18 | // notice, this list of conditions and the following disclaimer in the 19 | // documentation and/or other materials provided with the 20 | // distribution. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 23 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | // DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 31 | // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | // POSSIBILITY OF SUCH DAMAGE. 33 | 34 | package infectious 35 | 36 | var ( 37 | gf_exp = [510]byte{ 38 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1D, 0x3A, 0x74, 39 | 0xE8, 0xCD, 0x87, 0x13, 0x26, 0x4C, 0x98, 0x2D, 0x5A, 0xB4, 0x75, 40 | 0xEA, 0xC9, 0x8F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x9D, 41 | 0x27, 0x4E, 0x9C, 0x25, 0x4A, 0x94, 0x35, 0x6A, 0xD4, 0xB5, 0x77, 42 | 0xEE, 0xC1, 0x9F, 0x23, 0x46, 0x8C, 0x05, 0x0A, 0x14, 0x28, 0x50, 43 | 0xA0, 0x5D, 0xBA, 0x69, 0xD2, 0xB9, 0x6F, 0xDE, 0xA1, 0x5F, 0xBE, 44 | 0x61, 0xC2, 0x99, 0x2F, 0x5E, 0xBC, 0x65, 0xCA, 0x89, 0x0F, 0x1E, 45 | 0x3C, 0x78, 0xF0, 0xFD, 0xE7, 0xD3, 0xBB, 0x6B, 0xD6, 0xB1, 0x7F, 46 | 0xFE, 0xE1, 0xDF, 0xA3, 0x5B, 0xB6, 0x71, 0xE2, 0xD9, 0xAF, 0x43, 47 | 0x86, 0x11, 0x22, 0x44, 0x88, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xBD, 48 | 0x67, 0xCE, 0x81, 0x1F, 0x3E, 0x7C, 0xF8, 0xED, 0xC7, 0x93, 0x3B, 49 | 0x76, 0xEC, 0xC5, 0x97, 0x33, 0x66, 0xCC, 0x85, 0x17, 0x2E, 0x5C, 50 | 0xB8, 0x6D, 0xDA, 0xA9, 0x4F, 0x9E, 0x21, 0x42, 0x84, 0x15, 0x2A, 51 | 0x54, 0xA8, 0x4D, 0x9A, 0x29, 0x52, 0xA4, 0x55, 0xAA, 0x49, 0x92, 52 | 0x39, 0x72, 0xE4, 0xD5, 0xB7, 0x73, 0xE6, 0xD1, 0xBF, 0x63, 0xC6, 53 | 0x91, 0x3F, 0x7E, 0xFC, 0xE5, 0xD7, 0xB3, 0x7B, 0xF6, 0xF1, 0xFF, 54 | 0xE3, 0xDB, 0xAB, 0x4B, 0x96, 0x31, 0x62, 0xC4, 0x95, 0x37, 0x6E, 55 | 0xDC, 0xA5, 0x57, 0xAE, 0x41, 0x82, 0x19, 0x32, 0x64, 0xC8, 0x8D, 56 | 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xDD, 0xA7, 0x53, 0xA6, 0x51, 57 | 0xA2, 0x59, 0xB2, 0x79, 0xF2, 0xF9, 0xEF, 0xC3, 0x9B, 0x2B, 0x56, 58 | 0xAC, 0x45, 0x8A, 0x09, 0x12, 0x24, 0x48, 0x90, 0x3D, 0x7A, 0xF4, 59 | 0xF5, 0xF7, 0xF3, 0xFB, 0xEB, 0xCB, 0x8B, 0x0B, 0x16, 0x2C, 0x58, 60 | 0xB0, 0x7D, 0xFA, 0xE9, 0xCF, 0x83, 0x1B, 0x36, 0x6C, 0xD8, 0xAD, 61 | 0x47, 0x8E, 62 | 63 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1D, 0x3A, 0x74, 64 | 0xE8, 0xCD, 0x87, 0x13, 0x26, 0x4C, 0x98, 0x2D, 0x5A, 0xB4, 0x75, 65 | 0xEA, 0xC9, 0x8F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x9D, 66 | 0x27, 0x4E, 0x9C, 0x25, 0x4A, 0x94, 0x35, 0x6A, 0xD4, 0xB5, 0x77, 67 | 0xEE, 0xC1, 0x9F, 0x23, 0x46, 0x8C, 0x05, 0x0A, 0x14, 0x28, 0x50, 68 | 0xA0, 0x5D, 0xBA, 0x69, 0xD2, 0xB9, 0x6F, 0xDE, 0xA1, 0x5F, 0xBE, 69 | 0x61, 0xC2, 0x99, 0x2F, 0x5E, 0xBC, 0x65, 0xCA, 0x89, 0x0F, 0x1E, 70 | 0x3C, 0x78, 0xF0, 0xFD, 0xE7, 0xD3, 0xBB, 0x6B, 0xD6, 0xB1, 0x7F, 71 | 0xFE, 0xE1, 0xDF, 0xA3, 0x5B, 0xB6, 0x71, 0xE2, 0xD9, 0xAF, 0x43, 72 | 0x86, 0x11, 0x22, 0x44, 0x88, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xBD, 73 | 0x67, 0xCE, 0x81, 0x1F, 0x3E, 0x7C, 0xF8, 0xED, 0xC7, 0x93, 0x3B, 74 | 0x76, 0xEC, 0xC5, 0x97, 0x33, 0x66, 0xCC, 0x85, 0x17, 0x2E, 0x5C, 75 | 0xB8, 0x6D, 0xDA, 0xA9, 0x4F, 0x9E, 0x21, 0x42, 0x84, 0x15, 0x2A, 76 | 0x54, 0xA8, 0x4D, 0x9A, 0x29, 0x52, 0xA4, 0x55, 0xAA, 0x49, 0x92, 77 | 0x39, 0x72, 0xE4, 0xD5, 0xB7, 0x73, 0xE6, 0xD1, 0xBF, 0x63, 0xC6, 78 | 0x91, 0x3F, 0x7E, 0xFC, 0xE5, 0xD7, 0xB3, 0x7B, 0xF6, 0xF1, 0xFF, 79 | 0xE3, 0xDB, 0xAB, 0x4B, 0x96, 0x31, 0x62, 0xC4, 0x95, 0x37, 0x6E, 80 | 0xDC, 0xA5, 0x57, 0xAE, 0x41, 0x82, 0x19, 0x32, 0x64, 0xC8, 0x8D, 81 | 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xDD, 0xA7, 0x53, 0xA6, 0x51, 82 | 0xA2, 0x59, 0xB2, 0x79, 0xF2, 0xF9, 0xEF, 0xC3, 0x9B, 0x2B, 0x56, 83 | 0xAC, 0x45, 0x8A, 0x09, 0x12, 0x24, 0x48, 0x90, 0x3D, 0x7A, 0xF4, 84 | 0xF5, 0xF7, 0xF3, 0xFB, 0xEB, 0xCB, 0x8B, 0x0B, 0x16, 0x2C, 0x58, 85 | 0xB0, 0x7D, 0xFA, 0xE9, 0xCF, 0x83, 0x1B, 0x36, 0x6C, 0xD8, 0xAD, 86 | 0x47, 0x8E, 87 | } 88 | gf_log = [256]byte{ 89 | 0xFF, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1A, 0xC6, 0x03, 0xDF, 0x33, 90 | 0xEE, 0x1B, 0x68, 0xC7, 0x4B, 0x04, 0x64, 0xE0, 0x0E, 0x34, 0x8D, 91 | 0xEF, 0x81, 0x1C, 0xC1, 0x69, 0xF8, 0xC8, 0x08, 0x4C, 0x71, 0x05, 92 | 0x8A, 0x65, 0x2F, 0xE1, 0x24, 0x0F, 0x21, 0x35, 0x93, 0x8E, 0xDA, 93 | 0xF0, 0x12, 0x82, 0x45, 0x1D, 0xB5, 0xC2, 0x7D, 0x6A, 0x27, 0xF9, 94 | 0xB9, 0xC9, 0x9A, 0x09, 0x78, 0x4D, 0xE4, 0x72, 0xA6, 0x06, 0xBF, 95 | 0x8B, 0x62, 0x66, 0xDD, 0x30, 0xFD, 0xE2, 0x98, 0x25, 0xB3, 0x10, 96 | 0x91, 0x22, 0x88, 0x36, 0xD0, 0x94, 0xCE, 0x8F, 0x96, 0xDB, 0xBD, 97 | 0xF1, 0xD2, 0x13, 0x5C, 0x83, 0x38, 0x46, 0x40, 0x1E, 0x42, 0xB6, 98 | 0xA3, 0xC3, 0x48, 0x7E, 0x6E, 0x6B, 0x3A, 0x28, 0x54, 0xFA, 0x85, 99 | 0xBA, 0x3D, 0xCA, 0x5E, 0x9B, 0x9F, 0x0A, 0x15, 0x79, 0x2B, 0x4E, 100 | 0xD4, 0xE5, 0xAC, 0x73, 0xF3, 0xA7, 0x57, 0x07, 0x70, 0xC0, 0xF7, 101 | 0x8C, 0x80, 0x63, 0x0D, 0x67, 0x4A, 0xDE, 0xED, 0x31, 0xC5, 0xFE, 102 | 0x18, 0xE3, 0xA5, 0x99, 0x77, 0x26, 0xB8, 0xB4, 0x7C, 0x11, 0x44, 103 | 0x92, 0xD9, 0x23, 0x20, 0x89, 0x2E, 0x37, 0x3F, 0xD1, 0x5B, 0x95, 104 | 0xBC, 0xCF, 0xCD, 0x90, 0x87, 0x97, 0xB2, 0xDC, 0xFC, 0xBE, 0x61, 105 | 0xF2, 0x56, 0xD3, 0xAB, 0x14, 0x2A, 0x5D, 0x9E, 0x84, 0x3C, 0x39, 106 | 0x53, 0x47, 0x6D, 0x41, 0xA2, 0x1F, 0x2D, 0x43, 0xD8, 0xB7, 0x7B, 107 | 0xA4, 0x76, 0xC4, 0x17, 0x49, 0xEC, 0x7F, 0x0C, 0x6F, 0xF6, 0x6C, 108 | 0xA1, 0x3B, 0x52, 0x29, 0x9D, 0x55, 0xAA, 0xFB, 0x60, 0x86, 0xB1, 109 | 0xBB, 0xCC, 0x3E, 0x5A, 0xCB, 0x59, 0x5F, 0xB0, 0x9C, 0xA9, 0xA0, 110 | 0x51, 0x0B, 0xF5, 0x16, 0xEB, 0x7A, 0x75, 0x2C, 0xD7, 0x4F, 0xAE, 111 | 0xD5, 0xE9, 0xE6, 0xE7, 0xAD, 0xE8, 0x74, 0xD6, 0xF4, 0xEA, 0xA8, 112 | 0x50, 0x58, 0xAF, 113 | } 114 | gf_inverse = [256]byte{ 115 | 0x00, 0x01, 0x8E, 0xF4, 0x47, 0xA7, 0x7A, 0xBA, 0xAD, 0x9D, 0xDD, 116 | 0x98, 0x3D, 0xAA, 0x5D, 0x96, 0xD8, 0x72, 0xC0, 0x58, 0xE0, 0x3E, 117 | 0x4C, 0x66, 0x90, 0xDE, 0x55, 0x80, 0xA0, 0x83, 0x4B, 0x2A, 0x6C, 118 | 0xED, 0x39, 0x51, 0x60, 0x56, 0x2C, 0x8A, 0x70, 0xD0, 0x1F, 0x4A, 119 | 0x26, 0x8B, 0x33, 0x6E, 0x48, 0x89, 0x6F, 0x2E, 0xA4, 0xC3, 0x40, 120 | 0x5E, 0x50, 0x22, 0xCF, 0xA9, 0xAB, 0x0C, 0x15, 0xE1, 0x36, 0x5F, 121 | 0xF8, 0xD5, 0x92, 0x4E, 0xA6, 0x04, 0x30, 0x88, 0x2B, 0x1E, 0x16, 122 | 0x67, 0x45, 0x93, 0x38, 0x23, 0x68, 0x8C, 0x81, 0x1A, 0x25, 0x61, 123 | 0x13, 0xC1, 0xCB, 0x63, 0x97, 0x0E, 0x37, 0x41, 0x24, 0x57, 0xCA, 124 | 0x5B, 0xB9, 0xC4, 0x17, 0x4D, 0x52, 0x8D, 0xEF, 0xB3, 0x20, 0xEC, 125 | 0x2F, 0x32, 0x28, 0xD1, 0x11, 0xD9, 0xE9, 0xFB, 0xDA, 0x79, 0xDB, 126 | 0x77, 0x06, 0xBB, 0x84, 0xCD, 0xFE, 0xFC, 0x1B, 0x54, 0xA1, 0x1D, 127 | 0x7C, 0xCC, 0xE4, 0xB0, 0x49, 0x31, 0x27, 0x2D, 0x53, 0x69, 0x02, 128 | 0xF5, 0x18, 0xDF, 0x44, 0x4F, 0x9B, 0xBC, 0x0F, 0x5C, 0x0B, 0xDC, 129 | 0xBD, 0x94, 0xAC, 0x09, 0xC7, 0xA2, 0x1C, 0x82, 0x9F, 0xC6, 0x34, 130 | 0xC2, 0x46, 0x05, 0xCE, 0x3B, 0x0D, 0x3C, 0x9C, 0x08, 0xBE, 0xB7, 131 | 0x87, 0xE5, 0xEE, 0x6B, 0xEB, 0xF2, 0xBF, 0xAF, 0xC5, 0x64, 0x07, 132 | 0x7B, 0x95, 0x9A, 0xAE, 0xB6, 0x12, 0x59, 0xA5, 0x35, 0x65, 0xB8, 133 | 0xA3, 0x9E, 0xD2, 0xF7, 0x62, 0x5A, 0x85, 0x7D, 0xA8, 0x3A, 0x29, 134 | 0x71, 0xC8, 0xF6, 0xF9, 0x43, 0xD7, 0xD6, 0x10, 0x73, 0x76, 0x78, 135 | 0x99, 0x0A, 0x19, 0x91, 0x14, 0x3F, 0xE6, 0xF0, 0x86, 0xB1, 0xE2, 136 | 0xF1, 0xFA, 0x74, 0xF3, 0xB4, 0x6D, 0x21, 0xB2, 0x6A, 0xE3, 0xE7, 137 | 0xB5, 0xEA, 0x03, 0x8F, 0xD3, 0xC9, 0x42, 0xD4, 0xE8, 0x75, 0x7F, 138 | 0xFF, 0x7E, 0xFD, 139 | } 140 | gf_mul_table = [256][256]byte{} 141 | ) 142 | 143 | func init() { 144 | for i := 0; i < 256; i++ { 145 | for j := 0; j < 256; j++ { 146 | log_i := int(gf_log[i]) 147 | log_j := int(gf_log[j]) 148 | gf_mul_table[i][j] = gf_exp[(log_i+log_j)%255] 149 | } 150 | } 151 | for i := 0; i < 256; i++ { 152 | gf_mul_table[0][i], gf_mul_table[i][0] = 0, 0 153 | } 154 | } 155 | --------------------------------------------------------------------------------