void PrecomputeDistanceMatrix2D(int W, int H, bool show = true)
    {
        int N = area_cnt;

        ////////////////////////////////////////////////////////////
        // step III-1. build matrix A for distance computation
        // [CAUTION] this is NEGATED matrix to use Cholesky decomp.
        ////////////////////////////////////////////////////////////
        var C_dist = new CoordinateStorage <double>(N, N, 5 * N); // banded matrix without exception

        for (int matid_this = 0; matid_this < N; matid_this++)
        {
            List <int> matid_neighbor;
            if (!MatrixNeighbors.TryGetValue(matid_this, out matid_neighbor))
            {
                continue;
            }

            double A_diag = -1e-6; // small value to use Cholesky decomposition

            Action <int> CheckNeighbor = (id) =>
            {
                int matid_next = matid_neighbor[id];
                if (matid_next != -1)
                {
                    A_diag += -1.0;
                    C_dist.At(matid_this, matid_next, -1.0);
                }
            };

            CheckNeighbor(0);
            CheckNeighbor(1);
            CheckNeighbor(2);
            CheckNeighbor(3);

            C_dist.At(matid_this, matid_this, -A_diag);
        }

        var A_dist = Converter.ToCompressedColumnStorage(C_dist) as SparseMatrix;

        ////////////////////////////////////////////////////////////
        // step III-2. build matrix A for heat diffusion
        ////////////////////////////////////////////////////////////
#if USE_3RDPARTY
        solver_dist = new SuperLU(A_dist);
        var options = solver_dist.Options;
        options.SymmetricMode = true;
        solver_dist.Factorize();
#else
        solver_dist = SparseCholesky.Create(A_dist, ColumnOrdering.MinimumDegreeAtPlusA);
#endif
    }
    void PrecomputeHeatMatrix2D(double dt)
    {
        int N = area_cnt;

        ////////////////////////////////////////////////////////////
        // step I-1. build matrix A for heat diffusion
        ////////////////////////////////////////////////////////////
        var C_heat = new CoordinateStorage <double>(N, N, 5 * N); // banded matrix without exception

        for (int matid_this = 0; matid_this < N; matid_this++)
        {
            List <int> matid_neighbor;
            if (!MatrixNeighbors.TryGetValue(matid_this, out matid_neighbor))
            {
                continue;
            }

            if (matid_neighbor[0] != -1)
            {
                C_heat.At(matid_this, matid_neighbor[0], -1.0 * dt);
            }
            if (matid_neighbor[1] != -1)
            {
                C_heat.At(matid_this, matid_neighbor[1], -1.0 * dt);
            }
            if (matid_neighbor[2] != -1)
            {
                C_heat.At(matid_this, matid_neighbor[2], -1.0 * dt);
            }
            if (matid_neighbor[3] != -1)
            {
                C_heat.At(matid_this, matid_neighbor[3], -1.0 * dt);
            }

            C_heat.At(matid_this, matid_this, 1.0 + 4.0 * dt);
        }

        var A_heat = Converter.ToCompressedColumnStorage(C_heat) as SparseMatrix;

        ////////////////////////////////////////////////////////////
        // step I-2. build matrix A for heat diffusion
        ////////////////////////////////////////////////////////////
#if USE_3RDPARTY
        solver_heat = new SuperLU(A_heat);
        var options = solver_heat.Options;
        options.SymmetricMode = true;
        solver_heat.Factorize();
#else
        solver_heat = SparseCholesky.Create(A_heat, ColumnOrdering.MinimumDegreeAtPlusA);
#endif
    }