public void Evaluate(MultiFunctor f, CoordinateTransform[] map, IntegrationRegion r)
        {
            if (map == null) {
                Evaluate(f, r);
                return;
            }

            // Evaluation at origin. Keep a vector of origin coordinates and jacobian values.
            double[] x0 = new double[d];
            double[] x = new double[d];
            double[] v0 = new double[d];
            double[] v = new double[d];
            for (int i = 0; i < x.Length; i++) {
                x0[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, 0.0), out v0[i]);
                x[i] = x0[i];
                v[i] = v0[i];
            }
            double f0 = Jacobian(v) * f.Evaluate(x);
            double I7 = w70 * f0;
            double I5 = w50 * f0;

            // near off-one-axis evaluations
            double f1 = 0.0;
            for (int i = 0; i < d; i++) {
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a1), out v[i]);
                f1 += Jacobian(v) * f.Evaluate(x);
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a1), out v[i]);
                f1 += Jacobian(v) * f.Evaluate(x);
                x[i] = x0[i];
                v[i] = v0[i];
            }
            I7 += w71 * f1;
            I5 += w51 * f1;

            // far off-one-axis evaluations
            int sIndex = 0; double sMax = 0.0;
            double f2 = 0.0;
            for (int i = 0; i < d; i++) {
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a2), out v[i]);
                double fp2 = Jacobian(v) * f.Evaluate(x);
                f2 += fp2;
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a2), out v[i]);
                double fm2 = Jacobian(v) * f.Evaluate(x);
                f2 += fm2;
                x[i] = x0[i];
                v[i] = v0[i];

                double s = Math.Abs(fm2 + fp2 - 2.0 * f0);
                if (s > sMax) {
                    sMax = s;
                    sIndex = i;
                } else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex))) {
                    sIndex = i;
                }
            }
            I7 += w72 * f2;
            I5 += w52 * f2;

            // far off-two-axis evaluations
            double f3 = 0.0;
            for (int i = 0; i < d; i++) {
                for (int j = 0; j < i; j++) {
                    // ++
                    x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a3), out v[i]);
                    x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]);
                    f3 += Jacobian(v) * f.Evaluate(x);
                    // +-
                    x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, -a3), out v[j]);
                    f3 += Jacobian(v) * f.Evaluate(x);
                    // --
                    x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a3), out v[i]);
                    f3 += Jacobian(v) * f.Evaluate(x);
                    // -+
                    x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]);
                    f3 += Jacobian(v) * f.Evaluate(x);
                    x[i] = x0[i];
                    x[j] = x0[j];
                    v[i] = v0[i];
                    v[j] = v0[j];
                }
            }
            I7 += w73 * f3;
            I5 += w53 * f3;

            // mid off-all-axis evaluations
            // We need all 2^d permutations of + and - in each position.
            // So that we only need to change one component each time, we proceed in Gray code order.
            // We use a bit-vector to keep track of which component is + (0) and which is - (1).
            int state = 0;
            for (int j = 0; j < d; j++) {
                x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a4), out v[j]);
            }
            double f4 = Jacobian(v) * f.Evaluate(x);
            for (int i = 0; i < (td - 1); i++) {
                int j = GrayFlipIndex(i);
                int mask = 1 << j;
                state = state ^ mask;
                x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4), out v[j]);
                f4 += Jacobian(v) * f.Evaluate(x);
            }
            I7 += w74 * f4;

            double V = r.Volume();

            r.Value = V * I7;
            r.Error = V * Math.Abs(I7 - I5);
            r.SplitIndex = sIndex;
        }
        public void Evaluate(MultiFunctor f, CoordinateTransform[] map, IntegrationRegion r)
        {
            if (map == null)
            {
                Evaluate(f, r);
                return;
            }

            // Evaluation at origin. Keep a vector of origin coordinates and jacobian values.
            double[] x0 = new double[d];
            double[] x  = new double[d];
            double[] v0 = new double[d];
            double[] v  = new double[d];
            for (int i = 0; i < x.Length; i++)
            {
                x0[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, 0.0), out v0[i]);
                x[i]  = x0[i];
                v[i]  = v0[i];
            }
            double f0 = Jacobian(v) * f.Evaluate(x);
            double I7 = w70 * f0;
            double I5 = w50 * f0;

            // near off-one-axis evaluations
            double f1 = 0.0;

            for (int i = 0; i < d; i++)
            {
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a1), out v[i]);
                f1  += Jacobian(v) * f.Evaluate(x);
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a1), out v[i]);
                f1  += Jacobian(v) * f.Evaluate(x);
                x[i] = x0[i];
                v[i] = v0[i];
            }
            I7 += w71 * f1;
            I5 += w51 * f1;

            // far off-one-axis evaluations
            int    sIndex = 0; double sMax = 0.0;
            double f2 = 0.0;

            for (int i = 0; i < d; i++)
            {
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a2), out v[i]);
                double fp2 = Jacobian(v) * f.Evaluate(x);
                f2  += fp2;
                x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a2), out v[i]);
                double fm2 = Jacobian(v) * f.Evaluate(x);
                f2  += fm2;
                x[i] = x0[i];
                v[i] = v0[i];

                double s = Math.Abs(fm2 + fp2 - 2.0 * f0);
                if (s > sMax)
                {
                    sMax   = s;
                    sIndex = i;
                }
                else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex)))
                {
                    sIndex = i;
                }
            }
            I7 += w72 * f2;
            I5 += w52 * f2;

            // far off-two-axis evaluations
            double f3 = 0.0;

            for (int i = 0; i < d; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    // ++
                    x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a3), out v[i]);
                    x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]);
                    f3  += Jacobian(v) * f.Evaluate(x);
                    // +-
                    x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, -a3), out v[j]);
                    f3  += Jacobian(v) * f.Evaluate(x);
                    // --
                    x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a3), out v[i]);
                    f3  += Jacobian(v) * f.Evaluate(x);
                    // -+
                    x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]);
                    f3  += Jacobian(v) * f.Evaluate(x);
                    x[i] = x0[i];
                    x[j] = x0[j];
                    v[i] = v0[i];
                    v[j] = v0[j];
                }
            }
            I7 += w73 * f3;
            I5 += w53 * f3;

            // mid off-all-axis evaluations
            // We need all 2^d permutations of + and - in each position.
            // So that we only need to change one component each time, we proceed in Gray code order.
            // We use a bit-vector to keep track of which component is + (0) and which is - (1).
            int state = 0;

            for (int j = 0; j < d; j++)
            {
                x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a4), out v[j]);
            }
            double f4 = Jacobian(v) * f.Evaluate(x);

            for (int i = 0; i < (td - 1); i++)
            {
                int j    = GrayFlipIndex(i);
                int mask = 1 << j;
                state = state ^ mask;
                x[j]  = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4), out v[j]);
                f4   += Jacobian(v) * f.Evaluate(x);
            }
            I7 += w74 * f4;

            double V = r.Volume();

            r.Value      = V * I7;
            r.Error      = V * Math.Abs(I7 - I5);
            r.SplitIndex = sIndex;
        }
        public void Evaluate(MultiFunctor f, IntegrationRegion r)
        {
            // evaluation at origin
            // Keep a vector of origin coordinates, we will often re-set components to them.
            double[] x0 = new double[d];
            double[] x = new double[d];
            for (int i = 0; i < x.Length; i++) {
                x0[i] = r.MapCoordinateFromSymmetricUnitInterval(i, 0.0);
                x[i] = x0[i];
            }
            double f0 = f.Evaluate(x);
            double I7 = w70 * f0;
            double I5 = w50 * f0;

            // near off-one-axis evaluations
            double f1 = 0.0;
            for (int i = 0; i < d; i++) {
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a1);
                f1 += f.Evaluate(x);
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a1);
                f1 += f.Evaluate(x);
                x[i] = x0[i];
            }
            I7 += w71 * f1;
            I5 += w51 * f1;

            // far off-one-axis evaluations
            // while doing this, determine along which direction we will split, if asked
            int sIndex = 0; double sMax = 0.0;
            double f2 = 0.0;
            for (int i = 0; i < d; i++) {
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a2);
                double fp2 = f.Evaluate(x);
                f2 += fp2;
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a2);
                double fm2 = f.Evaluate(x);
                f2 += fm2;
                x[i] = x0[i];
                // One way to view fm2 + fp2 - 2 f0 is as numerical 2nd derivative
                // Another way is as difference of f0 from value predicted by linear interpolation from fm2 and fp2
                double s = Math.Abs(fm2 + fp2 - 2.0 * f0);
                if (s > sMax) {
                    // Use the direction of largest difference for future splits.
                    sMax = s;
                    sIndex = i;
                } else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex))) {
                    // If two directions both have the same difference, pick the one with the larger width to split.
                    // This may seem like an unimportant corner case, but it turns out to be critical to convergence.
                    // Without this clause we get into cycles in which we split along the same direction forever.
                    // For example, given the 3D watson integral of [1 - \cos(x) \cos(y) \cos(z)]^{-1} over [0,\pi]^3,
                    // all directions have zero deficit from starting point because one of the cosines is always zero,
                    // and without this branch we split along the x-axis forever.
                    sIndex = i;
                }
            }
            I7 += w72 * f2;
            I5 += w52 * f2;
            r.SplitIndex = sIndex;

            // far off-two-axis evaluations
            double f3 = 0.0;
            for (int i = 0; i < d; i++) {
                for (int j = 0; j < i; j++) {
                    // ++
                    x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a3);
                    x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3);
                    f3 += f.Evaluate(x);
                    // +-
                    x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, -a3);
                    f3 += f.Evaluate(x);
                    // --
                    x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a3);
                    f3 += f.Evaluate(x);
                    // -+
                    x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3);
                    f3 += f.Evaluate(x);
                    x[i] = x0[i];
                    x[j] = x0[j];
                }
            }
            I7 += w73 * f3;
            I5 += w53 * f3;

            // mid off-all-axis evaluations
            // We need all 2^d permutations of + and - in each position.
            // So that we only need to change one component each time, we proceed in Gray code order.
            // We use a bit-vector to keep track of which component is + (0) and which is - (1).
            int state = 0;
            for (int j = 0; j < d; j++) {
                x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a4);
            }
            double f4 = f.Evaluate(x);
            for (int i = 0; i < (td - 1); i++) {
                int j = GrayFlipIndex(i);
                int mask = 1 << j;
                state = state ^ mask;
                x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4);
                f4 += f.Evaluate(x);
            }
            I7 += w74 * f4;

            double V = r.Volume();

            r.Value = V * I7;
            r.Error = V * Math.Abs(I7 - I5);
        }
        public void Evaluate(MultiFunctor f, IntegrationRegion r)
        {
            // evaluation at origin
            // Keep a vector of origin coordinates, we will often re-set components to them.
            double[] x0 = new double[d];
            double[] x  = new double[d];
            for (int i = 0; i < x.Length; i++)
            {
                x0[i] = r.MapCoordinateFromSymmetricUnitInterval(i, 0.0);
                x[i]  = x0[i];
            }
            double f0 = f.Evaluate(x);
            double I7 = w70 * f0;
            double I5 = w50 * f0;

            // near off-one-axis evaluations
            double f1 = 0.0;

            for (int i = 0; i < d; i++)
            {
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a1);
                f1  += f.Evaluate(x);
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a1);
                f1  += f.Evaluate(x);
                x[i] = x0[i];
            }
            I7 += w71 * f1;
            I5 += w51 * f1;

            // far off-one-axis evaluations
            // while doing this, determine along which direction we will split, if asked
            int    sIndex = 0; double sMax = 0.0;
            double f2 = 0.0;

            for (int i = 0; i < d; i++)
            {
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a2);
                double fp2 = f.Evaluate(x);
                f2  += fp2;
                x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a2);
                double fm2 = f.Evaluate(x);
                f2  += fm2;
                x[i] = x0[i];
                // One way to view fm2 + fp2 - 2 f0 is as numerical 2nd derivative
                // Another way is as difference of f0 from value predicted by linear interpolation from fm2 and fp2
                double s = Math.Abs(fm2 + fp2 - 2.0 * f0);
                if (s > sMax)
                {
                    // Use the direction of largest difference for future splits.
                    sMax   = s;
                    sIndex = i;
                }
                else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex)))
                {
                    // If two directions both have the same difference, pick the one with the larger width to split.
                    // This may seem like an unimportant corner case, but it turns out to be critical to convergence.
                    // Without this clause we get into cycles in which we split along the same direction forever.
                    // For example, given the 3D watson integral of [1 - \cos(x) \cos(y) \cos(z)]^{-1} over [0,\pi]^3,
                    // all directions have zero deficit from starting point because one of the cosines is always zero,
                    // and without this branch we split along the x-axis forever.
                    sIndex = i;
                }
            }
            I7          += w72 * f2;
            I5          += w52 * f2;
            r.SplitIndex = sIndex;

            // far off-two-axis evaluations
            double f3 = 0.0;

            for (int i = 0; i < d; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    // ++
                    x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a3);
                    x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3);
                    f3  += f.Evaluate(x);
                    // +-
                    x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, -a3);
                    f3  += f.Evaluate(x);
                    // --
                    x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a3);
                    f3  += f.Evaluate(x);
                    // -+
                    x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3);
                    f3  += f.Evaluate(x);
                    x[i] = x0[i];
                    x[j] = x0[j];
                }
            }
            I7 += w73 * f3;
            I5 += w53 * f3;

            // mid off-all-axis evaluations
            // We need all 2^d permutations of + and - in each position.
            // So that we only need to change one component each time, we proceed in Gray code order.
            // We use a bit-vector to keep track of which component is + (0) and which is - (1).
            int state = 0;

            for (int j = 0; j < d; j++)
            {
                x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a4);
            }
            double f4 = f.Evaluate(x);

            for (int i = 0; i < (td - 1); i++)
            {
                int j    = GrayFlipIndex(i);
                int mask = 1 << j;
                state = state ^ mask;
                x[j]  = r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4);
                f4   += f.Evaluate(x);
            }
            I7 += w74 * f4;

            double V = r.Volume();

            r.Value = V * I7;
            r.Error = V * Math.Abs(I7 - I5);
        }