using System;
using System.Collections.Generic;
using System.Text;
namespace CUETools.Parity
{
/**
* タイトル: RSコード・デコーダ
*
* @author Masayuki Miyazaki
* http://sourceforge.jp/projects/reedsolomon/
*/
public class RsDecode
{
protected Galois galois;
protected int npar;
public ushort[, ,] chienTable;
public RsDecode(int npar, Galois galois)
{
this.npar = npar;
this.galois = galois;
//if (galois.Max == 65535)
this.makeChienTable();
}
///
/// chienTable[xx, 0, i] = mul(00xx, α^-i)
/// chienTable[xx, 1, i] = mul(xx00, α^-i)
///
///
///
private void makeChienTable()
{
this.chienTable = new ushort[256, 2, this.npar];
for (int i = 0; i < this.npar; i++)
{
this.chienTable[0, 0, i] = 0;
this.chienTable[0, 1, i] = 0;
}
for (int ib = 1; ib < 256; ib++)
{
int logib0 = this.galois.LogTbl[ib];
int logib1 = this.galois.LogTbl[ib << 8];
for (int i = 0; i < this.npar; i++)
{
this.chienTable[ib, 0, i] = this.galois.ExpTbl[logib0 + this.galois.Max - i];
this.chienTable[ib, 1, i] = this.galois.ExpTbl[logib1 + this.galois.Max - i];
}
}
}
///
/// Modified Berlekamp-Massey
///
///
/// σ(z)格納用配列、最大npar/2 + 2個の領域が必要
/// σ0,σ1,σ2, ... σ[jisu]
///
///
/// ω(z)格納用配列、最大npar/2 + 1個の領域が必要
/// ω0,ω1,ω2, ... ω[jisu-1]
///
///
/// シンドローム配列
/// s0,s1,s2, ... s[npar-1]
///
///
/// >= 0: σの次数
/// else: エラー
///
public unsafe int calcSigmaMBM(int* sigma, int* syn)
{
int* sg0 = stackalloc int[npar + 1];
int* sg1 = stackalloc int[npar + 1];
int* wk = stackalloc int[npar + 1];
sg0[1] = 1;
sg1[0] = 1;
int jisu0 = 1;
int jisu1 = 0;
int m = -1;
for (int n = 0; n < npar; n++)
{
// 判別式を計算
int d = syn[n];
for (int i = 1; i <= jisu1; i++)
d ^= galois.mul(sg1[i], syn[n - i]);
if (d != 0)
{
int logd = galois.toLog(d);
for (int i = 0; i <= n; i++)
wk[i] = sg1[i] ^ galois.mulExp(sg0[i], logd);
int js = n - m;
if (js > jisu1)
{
for (int i = 0; i <= jisu0; i++)
sg0[i] = galois.divExp(sg1[i], logd);
m = n - jisu1;
jisu1 = js;
jisu0 = js;
}
for (int i = 0; i < npar; i++)
sg1[i] = wk[i];
}
for (int i = jisu0; i > 0; i--)
sg0[i] = sg0[i - 1];
sg0[0] = 0;
jisu0++;
}
if (sg1[jisu1] == 0)
return -1;
//galois.mulPoly(omega, sg1, syn, npar / 2 + 1, npar, npar);
for (int i = 0; i < Math.Min(npar / 2 + 2, npar); i++)
sigma[i] = sg1[i];
return jisu1;
}
public unsafe int calcSigmaMBM(int[] sigma, int[] omega, int[] syn)
{
fixed (int* s = sigma, o = omega, y = syn)
{
int res = calcSigmaMBM(s, y);
if (res >= 0) galois.mulPoly(o, s, y, npar / 2 + 1, npar, npar);
return res;
}
}
/**
* 最終エラー位置のセット
* @param pos
* 誤り位置格納用初列
* @param n
* データ長
* @param last
* エラー位置
* @return
* 0: 正常終了
* < 0: エラー
*/
private unsafe bool setLastErrorPos(int* pos, int n, int last)
{
if (galois.toLog(last) >= n)
return false; // 範囲外なのでエラー
pos[0] = last;
return true;
}
/**
* チェン探索により誤り位置を求める
* σ(z) = 0の解を探索する
* ただし、探索はデータ長以内の解のみで
* jisu個の解が見つからなければ、エラーとする
* @param pos int[]
* 誤り位置格納用配列、jisu個の領域が必要
* @param n int
* データ長
* @param jisu int
* σの次数
* @param sigma int[]
* σ0,σ1,σ2, ... σ
* @return int
* 0: 正常終了
* < 0: エラー
*/
public unsafe bool chienSearch(int* pos, int n, int jisu, int* sigma, ushort *chT)
{
/*
* σ(z) = (1-α^i*z)(1-α^j*z)(1-α^k*z)
* = 1 + σ1z + σ2z^2 +...
* σ1 = α^i + α^j + α^k
* つまりσ1は全ての解の合計となっている。上記の性質を利用して、プチ最適化
* last = σ1から、見つけた解を次々と引いていくことにより、最後の解はlastとなる
*/
int last = sigma[1];
if (jisu == 1)
{
// 次数が1ならばlastがその解である
return setLastErrorPos(pos, n, last);
}
ushort* sg = stackalloc ushort[jisu + 1];
for (int j = 1; j <= jisu; j++)
sg[j] = (ushort)sigma[j];
int posIdx = jisu - 1; // 誤り位置格納用インデックス
if (npar == 8)
{
for (int i = 0; i < n; i++)
{
int wk = 1;
for (int j = 1; j <= jisu; j++)
wk ^= sg[j];
if (wk == 0)
{
int pv = galois.toExp(i);
last ^= pv;
pos[posIdx--] = pv;
if (posIdx == 0)
return setLastErrorPos(pos, n, last);
}
for (int j = 1; j <= jisu; j++)
{
ushort sgj = sg[j];
sg[j] = (ushort)(chT[(sgj & 0xff) * 8 * 2 + j] ^ chT[(sgj >> 8) * 8 * 2 + 8 + j]);
}
}
return false;
}
for (int i = 0; i < n; i++)
{
/*
* σ(z)の計算
* w を1(0乗の項)に初期化した後、残りの項<1..jisu>を加算
* z = 1/α^i = α^Iとすると
* σ(z) = 1 + σ1α^I + σ2(α^I)^2 + σ3(α^I)^3 + ... + σ/(α^I)^
* = 1 + σ1α^I + σ2α^(I*2) + σ3α^(I*3) + ... + σα^(I*)
*/
int wk = 1;
for (int j = 1; j <= jisu; j++)
wk ^= sg[j];
for (int j = 1; j <= jisu; j++)
{
ushort sgj = sg[j];
sg[j] = (ushort)(chT[(sgj & 0xff) * npar * 2 + j] ^ chT[(sgj >> 8) * npar * 2 + npar + j]);
}
if (wk == 0)
{
int pv = galois.toExp(i); // σ(z) = 0の解
last ^= pv; // lastから今見つかった解を引く
pos[posIdx--] = pv;
if (posIdx == 0)
{
// 残りが一つならば、lastがその解である
return setLastErrorPos(pos, n, last);
}
}
}
// 探索によりデータ長以内に、jisu個の解が見つからなかった
return false;
}
public unsafe bool chienSearch(int[] pos, int n, int jisu, int[] sigma)
{
fixed (int* p = pos, s = sigma)
fixed (ushort * chT = chienTable)
return chienSearch(p, n, jisu, s, chT);
}
public unsafe int doForney(int jisu, int ps, int* sigma, int* omega)
{
int zlog = galois.Max - galois.toLog(ps); // zのスカラー
// ω(z)の計算
int ov = omega[0];
for (int j = 1; j < jisu; j++)
{
ov ^= galois.mulExp(omega[j], (zlog * j) % galois.Max); // ov += ωi * z^j
}
// σ'(z)の値を計算(σ(z)の形式的微分)
int dv = sigma[1];
for (int j = 2; j < jisu; j += 2)
{
dv ^= galois.mulExp(sigma[j + 1], (zlog * j) % galois.Max); // dv += σ * z^j
}
/*
* 誤り訂正 E^i = α^i * ω(z) / σ'(z)
* 誤り位置の範囲はチェン探索のときに保証されているので、
* ここではチェックしない
*/
return galois.mul(ps, galois.div(ov, dv));
}
}
public class RsDecode8: RsDecode
{
public RsDecode8(int npar)
: base(npar, Galois81D.instance)
{
}
/**
* Forney法で誤り訂正を行う
* σ(z) = (1-α^i*z)(1-α^j*z)(1-α^k*z)
* σ'(z) = α^i * (1-α^j*z)(1-α^k*z)...
* + α^j * (1-α^i*z)(1-α^k*z)...
* + α^k * (1-α^i*z)(1-α^j*z)...
* ω(z) = (E^i/(1-α^i*z) + E^j/(1-α^j*z) + ...) * σ(z)
* = E^i*(1-α^j*z)(1-α^k*z)...
* + E^j*(1-α^i*z)(1-α^k*z)...
* + E^k*(1-α^i*z)(1-α^j*z)...
* ∴ E^i = α^i * ω(z) / σ'(z)
* @param data int[]
* 入力データ配列
* @param length int
* 入力データ長さ
* @param jisu int
* σの次数
* @param pos int[]
* 誤り位置配列
* @param sigma int[]
* σ0,σ1,σ2, ... σ
* @param omega int[]
* ω0,ω1,ω2, ... ω
*/
private unsafe void doForney(byte[] data, int length, int jisu, int[] pos, int[] sigma, int[] omega)
{
fixed (int* s = sigma, o = omega)
for (int i = 0; i < jisu; i++)
{
int ps = pos[i];
data[galois.toPos(length, ps)] ^= (byte)doForney(jisu, ps, s, o);
}
}
/**
* RSコードのデコード
*
* @param data int[]
* 入力データ配列
* @param length int
* パリティを含めたデータ長
* @param noCorrect boolean
* チェックのみで訂正は行わない
* @return bool
* 0: エラーなし
* > 0: 戻り値個の誤りを訂正した
* < 0: 訂正不能
*/
public bool decode(byte[] data, int length, bool noCorrect, out int errors)
{
if (length < npar || length > galois.Max)
throw new Exception("RsDecode: wrong length");
errors = 0;
// シンドロームを計算
int[] syn = new int[npar];
if (galois.calcSyndrome(data, length, syn))
return true; // エラー無し
// シンドロームよりσとωを求める
int[] sigma = new int[npar / 2 + 2];
int[] omega = new int[npar / 2 + 1];
int jisu = calcSigmaMBM(sigma, omega, syn);
if (jisu <= 0)
return false;
// チェン探索により誤り位置を求める
int[] pos = new int[jisu];
if (!chienSearch(pos, length, jisu, sigma))
return false;
if (!noCorrect) // 誤り訂正
doForney(data, length, jisu, pos, sigma, omega);
errors = jisu;
return true;
}
}
public class RsDecode16 : RsDecode
{
public RsDecode16(int npar, Galois galois)
: base(npar, galois)
{
}
public RsDecode16(int npar)
: this(npar, Galois16.instance)
{
}
/**
* Forney法で誤り訂正を行う
* σ(z) = (1-α^i*z)(1-α^j*z)(1-α^k*z)
* σ'(z) = α^i * (1-α^j*z)(1-α^k*z)...
* + α^j * (1-α^i*z)(1-α^k*z)...
* + α^k * (1-α^i*z)(1-α^j*z)...
* ω(z) = (E^i/(1-α^i*z) + E^j/(1-α^j*z) + ...) * σ(z)
* = E^i*(1-α^j*z)(1-α^k*z)...
* + E^j*(1-α^i*z)(1-α^k*z)...
* + E^k*(1-α^i*z)(1-α^j*z)...
* ∴ E^i = α^i * ω(z) / σ'(z)
* @param data int[]
* 入力データ配列
* @param length int
* 入力データ長さ
* @param jisu int
* σの次数
* @param pos int[]
* 誤り位置配列
* @param sigma int[]
* σ0,σ1,σ2, ... σ
* @param omega int[]
* ω0,ω1,ω2, ... ω
*/
private unsafe void doForney(ushort* data, int length, int jisu, int[] pos, int[] sigma, int[] omega)
{
fixed (int* s = sigma, o = omega)
for (int i = 0; i < jisu; i++)
{
int ps = pos[i];
data[galois.toPos(length, ps)] ^= (ushort)doForney(jisu, ps, s, o);
}
}
/**
* RSコードのデコード
*
* @param data int[]
* 入力データ配列
* @param length int
* パリティを含めたデータ長
* @param noCorrect boolean
* チェックのみで訂正は行わない
* @return bool
* 0: エラーなし
* > 0: 戻り値個の誤りを訂正した
* < 0: 訂正不能
*/
public unsafe bool decode(ushort* data, int length, bool noCorrect, out int errors)
{
if (length < npar || length > galois.Max)
throw new Exception("RsDecode: wrong length");
errors = 0;
// シンドロームを計算
int[] syn = new int[npar];
if (galois.calcSyndrome(data, length, syn))
return true; // エラー無し
// シンドロームよりσとωを求める
int[] sigma = new int[npar / 2 + 2];
int[] omega = new int[npar / 2 + 1];
int jisu = calcSigmaMBM(sigma, omega, syn);
if (jisu <= 0)
return false;
// チェン探索により誤り位置を求める
int[] pos = new int[jisu];
if (!chienSearch(pos, length, jisu, sigma))
return false;
if (!noCorrect) // 誤り訂正
doForney(data, length, jisu, pos, sigma, omega);
errors = jisu;
return true;
}
public unsafe bool decode(byte[] data, int datapos, int length, bool noCorrect, out int errors)
{
if ((length & 1) != 0)
throw new Exception("RsDecode: wrong length");
fixed (byte* bytes = &data[datapos])
return decode((ushort*)bytes, length >> 1, noCorrect, out errors);
}
}
}