public void Test(double[] recorded)
        {
            // 動作テスト

            int N = mOrder;
            int P = (1 << N) - 1;

            var from = new double[P + 1];

            {
                int copySize = recorded.Length;
                if (from.Length < copySize)
                {
                    copySize = from.Length;
                }
                Array.Copy(recorded, from, copySize);
            }

            byte[] mlsSeq;
            {
                var mls = new MaximumLengthSequence(N);
                mlsSeq = mls.Sequence();
                for (int i = 0; i < mlsSeq.Length; ++i)
                {
                    Console.Write("{0} ", mlsSeq[i]);
                }
                Console.WriteLine("");
            }

            {
                var mlsD = new double[mlsSeq.Length];
                for (int i = 0; i < mlsD.Length; ++i)
                {
                    mlsD[i] = mlsSeq[i] * 2.0 - 1.0;
                }
                var ccc = CrossCorrelation.CalcCircularCrossCorrelation(mlsD, mlsD);
                for (int i = 0; i < ccc.Length; ++i)
                {
                    Console.Write("{0:g2} ", ccc[i]);
                }
                Console.WriteLine("");
            }

            var mlsMat = new MatrixGF2(P, P);

            {
                for (int y = 0; y < P; ++y)
                {
                    for (int x = 0; x < P; ++x)
                    {
                        mlsMat.Set(y, x, (0 != mlsSeq[(x + y) % P]) ? GF2.One : GF2.Zero);
                    }
                }
            }
            mlsMat.Print("MLS matrix");

            // σ: mlsMatの左上N*N要素
            var σ = mlsMat.Subset(0, 0, N, N);

            σ.Print("σ");

            var σInv = σ.Inverse();

            σInv.Print("σ^-1");

            // S: MLS行列の上N行。
            var S = mlsMat.Subset(0, 0, N, P);

            S.Print("S");

            // Sの転置S^T
            S.Transpose().Print("S^T");

            // L: Sの転置 x σInv
            var L = S.Transpose().Mul(σInv);

            L.Print("L");

            // L x S == MLS行列
            var LS = L.Mul(S);

            LS.Print("L x S");

            int diff = LS.CompareTo(mlsMat);

            System.Diagnostics.Debug.Assert(diff == 0);

            // 2進で0~P-1までの値が入っている行列
            var B  = new MatrixGF2(P + 1, N);
            var Bt = new MatrixGF2(N, P + 1);

            for (int r = 0; r < P + 1; ++r)
            {
                for (int c = 0; c < N; ++c)
                {
                    int v = r & (1 << (N - 1 - c));
                    var b = (v == 0) ? GF2.Zero : GF2.One;
                    B.Set(r, c, b);
                    Bt.Set(c, r, b);
                }
            }

            B.Print("B");
            Bt.Print("Bt");

            // アダマール行列H8
            var H8 = MatrixGF2.Mul(B, Bt);

            H8.Print("H8");

            var vTest = new double[P + 1];

            for (int i = 0; i < P + 1; ++i)
            {
                vTest[i] = i;
            }

            var r1 = H8.ToMatrix().Mul(vTest);

            Print(r1, "R1");

            var r2 = FastWalshHadamardTransform.Transform(vTest);

            Print(r2, "R2");

            // Ps: S行列の列の値を2進数として、順番入れ替え行列を作る
            var Ps        = new MatrixGF2(P + 1, P + 1);
            var PsReorder = new List <int>();

            PsReorder.Add(0);
            for (int c = 0; c < P; ++c)
            {
                int sum = 0;
                for (int r = 0; r < N; ++r)
                {
                    sum += (1 << (N - 1 - r)) * S.At(r, c).Val;
                }
                Console.WriteLine("Ps: c={0} sum={1}", c, sum);
                Ps.Set(sum, c + 1, GF2.One);
                PsReorder.Add(sum);
            }
            Ps.Print("Ps");

            {
                var testMat = new WWMatrix(P + 1, 1);
                for (int r = 0; r < P + 1; ++r)
                {
                    testMat.Set(r, 0, PsReorder[r]);
                }

                var PsTest = Ps.ToMatrix().Mul(testMat);
                PsTest.Print("Ps x n");
            }

            // Pl: L行列の列の値を2進数として、順番入れ替え行列を作る
            var Pl        = new MatrixGF2(P + 1, P + 1);
            var PlReorder = new List <int>();

            PlReorder.Add(0);
            for (int r = 0; r < P; ++r)
            {
                int sum = 0;
                for (int c = 0; c < N; ++c)
                {
                    sum += (1 << (N - 1 - c)) * L.At(r, c).Val;
                }
                Console.WriteLine("Pl: r={0} sum={1}", r, sum);
                Pl.Set(r + 1, sum, GF2.One);
                PlReorder.Add(sum);
            }
            Pl.Print("Pl");

            S.Print("S");
            var BtPs = Bt.Mul(Ps);

            BtPs.Print("BtPs");

            L.Print("L");
            var PlB = Pl.Mul(B);

            PlB.Print("PlB");

            {
                var test2Mat = new WWMatrix(P + 1, 1);
                for (int r = 0; r < P + 1; ++r)
                {
                    test2Mat.Set(r, 0, r);
                }

                var PlTest = Pl.ToMatrix().Mul(test2Mat);
                PlTest.Print("Pl x n");
            }

            mlsMat.Print("MLS mat");
            var Mhat = Pl.Mul(H8).Mul(Ps);

            Mhat.Print("Mhat");

            // MLS deconvolution
            var decon = Mhat.ToMatrix().Mul(from);

            Print(decon, "decon");

            // 同じ処理をWalsh-Hadamard変換を使って行う。
            var reorderedFrom = new double[P + 1];

            for (int i = 0; i < P + 1; ++i)
            {
                reorderedFrom[PsReorder[i]] = from[i];
            }

#if true
            var hR = FastWalshHadamardTransform.Transform(reorderedFrom);
#else
            var hR = H8.ToMatrix().Mul(reorderedFrom);
#endif

            var hTo = new double[P + 1];
            for (int i = 0; i < P + 1; ++i)
            {
                hTo[i] = hR[PlReorder[i]];
            }
            Print(hTo, "hTo");
        }
        public MLSDeconvolution(int order)
        {
            mOrder = order;
            int N = mOrder;
            int P = (1 << N) - 1;

            byte[] mlsSeq;
            {
                var mls = new MaximumLengthSequence(N);
                mlsSeq = mls.Sequence();

                /*
                 * for (int i = 0; i < mlsSeq.Length; ++i) {
                 *  Console.Write("{0} ", mlsSeq[i]);
                 * }
                 * Console.WriteLine("");
                 */
            }

            // S: MLS行列の上N行。
            var S = new MatrixGF2(N, P);
            {
                for (int y = 0; y < N; ++y)
                {
                    for (int x = 0; x < P; ++x)
                    {
                        S.Set(y, x, (0 != mlsSeq[(x + y) % P]) ? GF2.One : GF2.Zero);
                    }
                }
            }
            //S.Print("S");

            // σ: MLS行列の左上NxN
            var σ = S.Subset(0, 0, N, N);
            //σ.Print("σ");

            var σInv = σ.Inverse();
            //σInv.Print("σ^-1");

            // L: Psの転置 x σInv
            var L = S.Transpose().Mul(σInv);

            //L.Print("L");

            // Ps: S行列の列の値を2進数として、順番入れ替えのための情報PsReorderを作る
            mFromReorder = new List <int>();
            mFromReorder.Add(0);
            for (int c = 0; c < P; ++c)
            {
                int sum = 0;
                for (int r = 0; r < N; ++r)
                {
                    sum += (1 << (N - 1 - r)) * S.At(r, c).Val;
                }
                //Console.WriteLine("Ps: c={0} sum={1}", c, sum);
                mFromReorder.Add(sum);
            }

            // Pl: L行列の列の値を2進数として、順番入れ替えのための情報PlReorderを作る
            mToReorder = new List <int>();
            mToReorder.Add(0);
            for (int r = 0; r < P; ++r)
            {
                int sum = 0;
                for (int c = 0; c < N; ++c)
                {
                    sum += (1 << (N - 1 - c)) * L.At(r, c).Val;
                }
                //Console.WriteLine("Pl: r={0} sum={1}", r, sum);
                mToReorder.Add(sum);
            }
        }