|
8 | 8 |
|
9 | 9 | % |
10 | 10 | % Standalone tests for the rsecbinary helper (streaming RSEC over GF(2^n)). |
11 | | -% Uses a self-contained GF(8) rsprod so the tests don't depend on any encoder. |
| 11 | +% Uses self-contained GF(8) tables so the tests don't depend on any encoder. |
12 | 12 | % GF(8) with primitive polynomial x^3 + x + 1, alpha = 2. |
13 | 13 | % |
| 14 | +% Calling convention: |
| 15 | +% |
| 16 | +% {getdata} nd logcoeffsrev lfsr rslog rsalog //rsecbinary exec |
| 17 | +% |
| 18 | +% where logcoeffsrev holds the generator coefficients reversed and in the |
| 19 | +% log domain with -1 denoting a zero coefficient, and rsalog spans two |
| 20 | +% periods of the multiplicative group so that log-sums resolve without a |
| 21 | +% modulo reduction. |
| 22 | +% |
14 | 23 |
|
15 | 24 | /rsecbinary dup /uk.co.terryburton.bwipp findresource cvx def |
16 | 25 |
|
17 | 26 | % log table: gf8_log[x] = i such that alpha^i = x (log[0] unused; set 0 for safety) |
18 | | -/gf8_log [0 0 1 3 2 6 4 5] def |
19 | | -% alog table: gf8_alog[i] = alpha^i, cycles every 7 |
20 | | -/gf8_alog [1 2 4 3 6 7 5] def |
21 | | - |
22 | | -/gf8_rsprod { |
23 | | - dup 0 ne { 1 index 0 ne { |
24 | | - gf8_log exch get exch gf8_log exch get add 7 mod gf8_alog exch get |
25 | | - } { pop pop 0 } ifelse } { pop pop 0 } ifelse |
26 | | -} bind def |
| 27 | +/gf8_log [0 0 1 3 2 6 4 5] def |
| 28 | +% anti-log table spanning two periods: gf8_alog2[i] = alpha^(i mod 7) |
| 29 | +/gf8_alog2 [1 2 4 3 6 7 5 1 2 4 3 6 7] def |
27 | 30 |
|
28 | 31 | % |
29 | | -% Test 1: Minimal case. nc=2, nd=1, data=[3], coeffs=[2, 3]. |
| 32 | +% Test 1: Minimal case. nc=2, nd=1, data=[3]. |
| 33 | +% Generator coefficients 3 and 2 (logs 3 and 1): logcoeffsrev=[3 1]. |
30 | 34 | % Hand-computed expected ECC = [5, 6]: |
31 | 35 | % lfsr = [0, 0] |
32 | | -% m=0: fb = 3 XOR 0 = 3 |
33 | | -% j=0: lfsr[0] = lfsr[1] XOR (coeffs[1] * fb) = 0 XOR (3*3) = alpha^6 = 5 |
34 | | -% lfsr[1] = coeffs[0] * fb = 2*3 = alpha^4 = 6 |
| 36 | +% m=0: fb = 3 XOR 0 = 3, log(fb) = 3 |
| 37 | +% j=0: lfsr[0] = lfsr[1] XOR alog[3+3] = 0 XOR alpha^6 = 5 |
| 38 | +% lfsr[1] = alog[3+1] = alpha^4 = 6 |
35 | 39 | % |
36 | 40 | /rsdata [3] def |
37 | 41 | /lfsr 2 array def |
38 | | -{ rsdata exch get } 1 [2 3] 2 lfsr /gf8_rsprod load //rsecbinary exec |
| 42 | +{ rsdata exch get } 1 [3 1] lfsr gf8_log gf8_alog2 //rsecbinary exec |
39 | 43 | lfsr 0 2 getinterval |
40 | 44 | [5 6] debugIsEqual |
41 | 45 |
|
42 | 46 | % |
43 | 47 | % Test 2: Two-codeword input. nc=2, nd=2, data=[3, 1]. |
44 | 48 | % After m=0 (as Test 1): lfsr = [5, 6] |
45 | | -% m=1: fb = 1 XOR 5 = 4 |
46 | | -% j=0: lfsr[0] = lfsr[1] XOR (3*4) = 6 XOR (alpha^3*alpha^2) = 6 XOR alpha^5 = 6 XOR 7 = 1 |
47 | | -% lfsr[1] = 2*4 = alpha*alpha^2 = alpha^3 = 3 |
| 49 | +% m=1: fb = 1 XOR 5 = 4, log(fb) = 2 |
| 50 | +% j=0: lfsr[0] = lfsr[1] XOR alog[2+3] = 6 XOR alpha^5 = 6 XOR 7 = 1 |
| 51 | +% lfsr[1] = alog[2+1] = alpha^3 = 3 |
48 | 52 | % Expected ECC = [1, 3] |
49 | 53 | % |
50 | 54 | /rsdata [3 1] def |
51 | | -{ rsdata exch get } 2 [2 3] 2 lfsr /gf8_rsprod load //rsecbinary exec |
| 55 | +{ rsdata exch get } 2 [3 1] lfsr gf8_log gf8_alog2 //rsecbinary exec |
52 | 56 | lfsr 0 2 getinterval |
53 | 57 | [1 3] debugIsEqual |
54 | 58 |
|
55 | 59 | % |
56 | | -% Test 3: nc=1 boundary. data=[5], coeffs=[7]. nd=1. |
| 60 | +% Test 3: nc=1 boundary. data=[5], coefficient 7 (log 5). nd=1. |
57 | 61 | % lfsr = [0] |
58 | | -% m=0: fb = 5 XOR 0 = 5 |
| 62 | +% m=0: fb = 5 XOR 0 = 5, log(fb) = 6 |
59 | 63 | % inner j loop has nc-2 = -1 range -> no iterations |
60 | | -% lfsr[0] = coeffs[0] * fb = 7*5 = alpha^5*alpha^6 = alpha^11 = alpha^4 = 6 |
| 64 | +% lfsr[0] = alog[6+5] = alog[11] = alpha^4 = 6 |
61 | 65 | % Expected ECC = [6] |
62 | 66 | % |
63 | 67 | /rsdata [5] def |
64 | 68 | /lfsr 1 array def |
65 | | -{ rsdata exch get } 1 [7] 1 lfsr /gf8_rsprod load //rsecbinary exec |
| 69 | +{ rsdata exch get } 1 [5] lfsr gf8_log gf8_alog2 //rsecbinary exec |
66 | 70 | lfsr 0 1 getinterval |
67 | 71 | [6] debugIsEqual |
68 | 72 |
|
69 | 73 | % |
70 | 74 | % Test 4: nd=0 boundary. Empty data -> ECC stays all zero. |
71 | 75 | % |
72 | 76 | /lfsr 3 array def |
73 | | -{ rsdata exch get } 0 [1 2 3] 3 lfsr /gf8_rsprod load //rsecbinary exec |
| 77 | +{ rsdata exch get } 0 [3 1 0] lfsr gf8_log gf8_alog2 //rsecbinary exec |
74 | 78 | lfsr 0 3 getinterval |
75 | 79 | [0 0 0] debugIsEqual |
76 | 80 |
|
77 | 81 | % |
78 | 82 | % Test 5: Reuse of lfsr across calls. Same buffer, two calls with different |
79 | | -% nc/coeffs. Validates the helper zero-fills on entry (no state leak). |
| 83 | +% nc/coefficients. Validates the helper zero-fills on entry (no state leak). |
80 | 84 | % |
81 | 85 | /lfsr 4 array def % sized for the larger call |
82 | 86 | /rsdata [2] def |
83 | | -% First: nc=2 |
84 | | -{ rsdata exch get } 1 [2 3] 2 lfsr /gf8_rsprod load //rsecbinary exec |
| 87 | +% First: nc=2, logcoeffsrev=[3 1] |
| 88 | +{ rsdata exch get } 1 [3 1] lfsr gf8_log gf8_alog2 //rsecbinary exec |
85 | 89 | lfsr 0 2 getinterval |
86 | 90 | [6 4] debugIsEqual |
87 | 91 | % Second: nc=3 using same lfsr. First 3 cells overwritten; cell 3 stale |
88 | 92 | % but irrelevant because caller reads only the first nc cells. |
89 | | -{ rsdata exch get } 1 [1 2 3] 3 lfsr /gf8_rsprod load //rsecbinary exec |
90 | | -% Hand-compute: fb=2, j=0: S[0] = S[1] ^ (3*2) = 0 ^ alpha^4 = 6 |
91 | | -% j=1: S[1] = S[2] ^ (2*2) = 0 ^ alpha^2 = 4 |
92 | | -% S[2] = 1*2 = alpha^1 = 2 |
| 93 | +% Coefficients 3, 2 and 1 (logs 3, 1 and 0): logcoeffsrev=[3 1 0] |
| 94 | +{ rsdata exch get } 1 [3 1 0] lfsr gf8_log gf8_alog2 //rsecbinary exec |
| 95 | +% Hand-compute: fb=2, log(fb)=1 |
| 96 | +% j=0: S[0] = S[1] XOR alog[1+3] = 0 XOR alpha^4 = 6 |
| 97 | +% j=1: S[1] = S[2] XOR alog[1+1] = 0 XOR alpha^2 = 4 |
| 98 | +% S[2] = alog[1+0] = alpha^1 = 2 |
93 | 99 | lfsr 0 3 getinterval |
94 | 100 | [6 4 2] debugIsEqual |
95 | 101 |
|
96 | 102 | % |
97 | 103 | % Test 6: getdata procedure isn't assumed to be a trivial array read. |
98 | 104 | % Use a stride-2 access into a flat array to prove the helper is indifferent. |
99 | 105 | % |
| 106 | +/lfsr 2 array def |
100 | 107 | /rsflat [0 3 0 1] def % stride 2 starting at 1: [3, 1] |
101 | | -{ 2 mul 1 add rsflat exch get } 2 [2 3] 2 lfsr /gf8_rsprod load //rsecbinary exec |
| 108 | +{ 2 mul 1 add rsflat exch get } 2 [3 1] lfsr gf8_log gf8_alog2 //rsecbinary exec |
102 | 109 | lfsr 0 2 getinterval |
103 | 110 | [1 3] debugIsEqual % same result as Test 2 |
| 111 | + |
| 112 | +% |
| 113 | +% Test 7: Zero feedback. data=[3, 5] makes fb vanish at m=1, exercising the |
| 114 | +% pure-shift branch. |
| 115 | +% After m=0 (as Test 1): lfsr = [5, 6] |
| 116 | +% m=1: fb = 5 XOR 5 = 0 -> shift: lfsr[0] = lfsr[1] = 6, lfsr[1] = 0 |
| 117 | +% Expected ECC = [6, 0] |
| 118 | +% |
| 119 | +/rsdata [3 5] def |
| 120 | +{ rsdata exch get } 2 [3 1] lfsr gf8_log gf8_alog2 //rsecbinary exec |
| 121 | +lfsr 0 2 getinterval |
| 122 | +[6 0] debugIsEqual |
| 123 | + |
| 124 | +% |
| 125 | +% Test 8: Zero coefficient. logcoeffsrev=[3 -1 1] has a -1 sentinel in the |
| 126 | +% middle, exercising the zero-product branch. |
| 127 | +% lfsr = [0, 0, 0] |
| 128 | +% m=0: fb = 3 XOR 0 = 3, log(fb) = 3 |
| 129 | +% j=0: lfsr[0] = lfsr[1] XOR alog[3+3] = 0 XOR alpha^6 = 5 |
| 130 | +% j=1: lfsr[1] = lfsr[2] XOR 0 = 0 |
| 131 | +% lfsr[2] = alog[3+1] = alpha^4 = 6 |
| 132 | +% Expected ECC = [5, 0, 6] |
| 133 | +% |
| 134 | +/rsdata [3] def |
| 135 | +/lfsr 3 array def |
| 136 | +{ rsdata exch get } 1 [3 -1 1] lfsr gf8_log gf8_alog2 //rsecbinary exec |
| 137 | +lfsr 0 3 getinterval |
| 138 | +[5 0 6] debugIsEqual |
0 commit comments