static void TestLU()
    {
        var mat = new double[2, 2];

        mat[0, 0] = 1;
        mat[0, 1] = 2;
        mat[1, 0] = 3;
        mat[1, 1] = -2;
        var lu  = new LUDecomposition(mat, true);
        var ans = lu.SolveLinearEquations(new[] { 1.0, -2.0 });

        Debug.Log(lu.Substitutions[0] + ", " + lu.Substitutions[1]);
        Debug.Log(ans[0] + ", " + ans[1]);
    }
    public SubstituteChargeMethod(
        ParametricBound counterclock, ParametricDirichlet dirichlet,
        int chargeCount, double distance)
    {
        // determin charge and sample positions
        Charges = new Vector2D[chargeCount];
        Samples = new Vector2D[chargeCount];
        var sampleValues = new double[chargeCount];
        var unit         = 1.0 / chargeCount;
        var dt           = unit * 0.5;

        for (var i = 0; i < chargeCount; i++)
        {
            Samples[i]      = counterclock(unit * i);
            sampleValues[i] = dirichlet(unit * i);
            var diff = counterclock(unit * i + dt)
                       - counterclock(unit * i - dt);
            var dir = new Vector2D(diff.Y, -diff.X).Normalized;
            Charges[i] = Samples[i] + dir * distance;
        }

        // calculate Q values
        var mat = new double[chargeCount, chargeCount];
        var p2m = -1.0 / (Math.PI * 2.0);

        for (var i = 0; i < chargeCount; i++)
        {
            for (var j = 0; j < chargeCount; j++)
            {
                mat[i, j] = Math.Log((Samples[i] - Charges[j]).Length) * p2m;
            }
        }
        var lu = new LUDecomposition(mat);

        Qs = lu.SolveLinearEquations(sampleValues);

        this.bound     = counterclock;
        this.dirichlet = dirichlet;
    }