Example usage for org.apache.commons.math3.exception DimensionMismatchException DimensionMismatchException

List of usage examples for org.apache.commons.math3.exception DimensionMismatchException DimensionMismatchException

Introduction

In this page you can find the example usage for org.apache.commons.math3.exception DimensionMismatchException DimensionMismatchException.

Prototype

public DimensionMismatchException(int wrong, int expected) 

Source Link

Document

Construct an exception from the mismatched dimensions.

Usage

From source file:org.gitools.analysis.groupcomparison.format.math33Preview.MathArrays.java

/**
 * Creates an array whose contents will be the element-by-element
 * division of the first argument by the second.
 *
 * @param a Numerator of the division.//www  .  j ava 2s  . c  om
 * @param b Denominator of the division.
 * @return a new array {@code r} where {@code r[i] = a[i] / b[i]}.
 * @throws DimensionMismatchException if the array lengths differ.
 * @since 3.1
 */
public static double[] ebeDivide(double[] a, double[] b) throws DimensionMismatchException {
    if (a.length != b.length) {
        throw new DimensionMismatchException(a.length, b.length);
    }

    final double[] result = a.clone();
    for (int i = 0; i < a.length; i++) {
        result[i] /= b[i];
    }
    return result;
}

From source file:org.gitools.analysis.groupcomparison.format.math33Preview.MathArrays.java

/**
 * Sort an array in place and perform the same reordering of entries on
 * other arrays.  This method works the same as the other
 * {@link #sortInPlace(double[], double[][]) sortInPlace} method, but
 * allows the order of the sort to be provided in the {@code dir}
 * parameter./* w  w  w.  j a va  2  s.  c om*/
 *
 * @param x     Array to be sorted and used as a pattern for permutation
 *              of the other arrays.
 * @param dir   Order direction.
 * @param yList Set of arrays whose permutations of entries will follow
 *              those performed on {@code x}.
 * @throws DimensionMismatchException if any {@code y} is not the same
 *                                    size as {@code x}.
 * @throws NullArgumentException      if {@code x} or any {@code y} is null
 * @since 3.0
 */
public static void sortInPlace(double[] x, final OrderDirection dir, double[]... yList)
        throws NullArgumentException, DimensionMismatchException {

    // Consistency checks.
    if (x == null) {
        throw new NullArgumentException();
    }

    final int yListLen = yList.length;
    final int len = x.length;

    for (int j = 0; j < yListLen; j++) {
        final double[] y = yList[j];
        if (y == null) {
            throw new NullArgumentException();
        }
        if (y.length != len) {
            throw new DimensionMismatchException(y.length, len);
        }
    }

    // Associate each abscissa "x[i]" with its index "i".
    final List<Pair<Double, Integer>> list = new ArrayList<Pair<Double, Integer>>(len);
    for (int i = 0; i < len; i++) {
        list.add(new Pair<Double, Integer>(x[i], i));
    }

    // Create comparators for increasing and decreasing orders.
    final Comparator<Pair<Double, Integer>> comp = dir == MathArrays.OrderDirection.INCREASING
            ? new Comparator<Pair<Double, Integer>>() {
                public int compare(Pair<Double, Integer> o1, Pair<Double, Integer> o2) {
                    return o1.getKey().compareTo(o2.getKey());
                }
            }
            : new Comparator<Pair<Double, Integer>>() {
                public int compare(Pair<Double, Integer> o1, Pair<Double, Integer> o2) {
                    return o2.getKey().compareTo(o1.getKey());
                }
            };

    // Sort.
    Collections.sort(list, comp);

    // Modify the original array so that its elements are in
    // the prescribed order.
    // Retrieve indices of original locations.
    final int[] indices = new int[len];
    for (int i = 0; i < len; i++) {
        final Pair<Double, Integer> e = list.get(i);
        x[i] = e.getKey();
        indices[i] = e.getValue();
    }

    // In each of the associated arrays, move the
    // elements to their new location.
    for (int j = 0; j < yListLen; j++) {
        // Input array will be modified in place.
        final double[] yInPlace = yList[j];
        final double[] yOrig = yInPlace.clone();

        for (int i = 0; i < len; i++) {
            yInPlace[i] = yOrig[indices[i]];
        }
    }
}

From source file:org.gitools.analysis.groupcomparison.format.math33Preview.MathArrays.java

/**
 * Compute a linear combination accurately.
 * This method computes the sum of the products
 * <code>a<sub>i</sub> b<sub>i</sub></code> to high accuracy.
 * It does so by using specific multiplication and addition algorithms to
 * preserve accuracy and reduce cancellation effects.
 * <br/>//from  www .j a  va2 s  . c  o m
 * It is based on the 2005 paper
 * <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">
 * Accurate Sum and Dot Product</a> by Takeshi Ogita, Siegfried M. Rump,
 * and Shin'ichi Oishi published in SIAM J. Sci. Comput.
 *
 * @param a Factors.
 * @param b Factors.
 * @return <code>&Sigma;<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
 * @throws DimensionMismatchException if arrays dimensions don't match
 */
public static double linearCombination(final double[] a, final double[] b) throws DimensionMismatchException {
    final int len = a.length;
    if (len != b.length) {
        throw new DimensionMismatchException(len, b.length);
    }

    if (len == 1) {
        // Revert to scalar multiplication.
        return a[0] * b[0];
    }

    final double[] prodHigh = new double[len];
    double prodLowSum = 0;

    for (int i = 0; i < len; i++) {
        final double ai = a[i];
        final double ca = SPLIT_FACTOR * ai;
        final double aHigh = ca - (ca - ai);
        final double aLow = ai - aHigh;

        final double bi = b[i];
        final double cb = SPLIT_FACTOR * bi;
        final double bHigh = cb - (cb - bi);
        final double bLow = bi - bHigh;
        prodHigh[i] = ai * bi;
        final double prodLow = aLow * bLow - (((prodHigh[i] - aHigh * bHigh) - aLow * bHigh) - aHigh * bLow);
        prodLowSum += prodLow;
    }

    final double prodHighCur = prodHigh[0];
    double prodHighNext = prodHigh[1];
    double sHighPrev = prodHighCur + prodHighNext;
    double sPrime = sHighPrev - prodHighNext;
    double sLowSum = (prodHighNext - (sHighPrev - sPrime)) + (prodHighCur - sPrime);

    final int lenMinusOne = len - 1;
    for (int i = 1; i < lenMinusOne; i++) {
        prodHighNext = prodHigh[i + 1];
        final double sHighCur = sHighPrev + prodHighNext;
        sPrime = sHighCur - prodHighNext;
        sLowSum += (prodHighNext - (sHighCur - sPrime)) + (sHighPrev - sPrime);
        sHighPrev = sHighCur;
    }

    double result = sHighPrev + (prodLowSum + sLowSum);

    if (Double.isNaN(result)) {
        // either we have split infinite numbers or some coefficients were NaNs,
        // just rely on the naive implementation and let IEEE754 handle this
        result = 0;
        for (int i = 0; i < len; ++i) {
            result += a[i] * b[i];
        }
    }

    return result;
}

From source file:org.gitools.analysis.groupcomparison.format.math33Preview.MathArrays.java

/**
 * This method is used/*from  ww w  .j a  va2s  .  co m*/
 * to verify that the begin and length parameters designate a subarray of positive length
 * and the weights are all non-negative, non-NaN, finite, and not all zero.
 * <p>
 * <ul>
 * <li>returns <code>true</code> iff the parameters designate a subarray of
 * non-negative length and the weights array contains legitimate values.</li>
 * <li>throws <code>MathIllegalArgumentException</code> if any of the following are true:
 * <ul><li>the values array is null</li>
 * <li>the weights array is null</li>
 * <li>the weights array does not have the same length as the values array</li>
 * <li>the weights array contains one or more infinite values</li>
 * <li>the weights array contains one or more NaN values</li>
 * <li>the weights array contains negative values</li>
 * <li>the start and length arguments do not determine a valid array</li></ul>
 * </li>
 * <li>returns <code>false</li> if the array is non-null, but
 * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>.
 * </ul></p>
 *
 * @param values     the input array.
 * @param weights    the weights array.
 * @param begin      index of the first array element to include.
 * @param length     the number of elements to include.
 * @param allowEmpty if {@code true} than allow zero length arrays to pass.
 * @return {@code true} if the parameters are valid.
 * @throws NullArgumentException        if either of the arrays are null
 * @throws MathIllegalArgumentException if the array indices are not valid,
 *                                      the weights array contains NaN, infinite or negative elements, or there
 *                                      are no positive weights.
 * @since 3.3
 */
public static boolean verifyValues(final double[] values, final double[] weights, final int begin,
        final int length, final boolean allowEmpty) throws MathIllegalArgumentException {

    if (weights == null || values == null) {
        throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
    }

    if (weights.length != values.length) {
        throw new DimensionMismatchException(weights.length, values.length);
    }

    boolean containsPositiveWeight = false;
    for (int i = begin; i < begin + length; i++) {
        final double weight = weights[i];
        if (Double.isNaN(weight)) {
            throw new MathIllegalArgumentException(LocalizedFormats.NAN_ELEMENT_AT_INDEX, Integer.valueOf(i));
        }
        if (Double.isInfinite(weight)) {
            throw new MathIllegalArgumentException(LocalizedFormats.INFINITE_ARRAY_ELEMENT,
                    Double.valueOf(weight), Integer.valueOf(i));
        }
        if (weight < 0) {
            throw new MathIllegalArgumentException(LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX,
                    Integer.valueOf(i), Double.valueOf(weight));
        }
        if (!containsPositiveWeight && weight > 0.0) {
            containsPositiveWeight = true;
        }
    }

    if (!containsPositiveWeight) {
        throw new MathIllegalArgumentException(LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
    }

    return verifyValues(values, begin, length, allowEmpty);
}

From source file:org.orekit.propagation.SpacecraftState.java

/** Check if two instances have the same set of additional states available.
 * <p>/*from  w  ww .jav  a  2 s.co  m*/
 * Only the names and dimensions of the additional states are compared,
 * not their values.
 * </p>
 * @param state state to compare to instance
 * @exception OrekitException if either instance or state supports an additional
 * state not supported by the other one
 * @exception DimensionMismatchException if an additional state does not have
 * the same dimension in both states
 */
public void ensureCompatibleAdditionalStates(final SpacecraftState state)
        throws OrekitException, DimensionMismatchException {

    // check instance additional states is a subset of the other one
    for (final Map.Entry<String, double[]> entry : additional.entrySet()) {
        final double[] other = state.additional.get(entry.getKey());
        if (other == null) {
            throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, entry.getKey());
        }
        if (other.length != entry.getValue().length) {
            throw new DimensionMismatchException(other.length, entry.getValue().length);
        }
    }

    if (state.additional.size() > additional.size()) {
        // the other state has more additional states
        for (final String name : state.additional.keySet()) {
            if (!additional.containsKey(name)) {
                throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
            }
        }
    }

}

From source file:org.pmad.gmm.MyMixMNDEM.java

/**
 * Creates an object to fit a multivariate normal mixture model to data.
 *
 * @param data Data to use in fitting procedure
 * @throws NotStrictlyPositiveException if data has no rows
 * @throws DimensionMismatchException if rows of data have different numbers
 *             of columns//  w  w  w . j av  a 2s  .co m
 * @throws NumberIsTooSmallException if the number of columns in the data is
 *             less than 2
 */
public MyMixMNDEM(double[][] data)
        throws NotStrictlyPositiveException, DimensionMismatchException, NumberIsTooSmallException {
    if (data.length < 1) {
        throw new NotStrictlyPositiveException(data.length);
    }

    this.data = new double[data.length][data[0].length];

    for (int i = 0; i < data.length; i++) {
        if (data[i].length != data[0].length) {
            // Jagged arrays not allowed
            throw new DimensionMismatchException(data[i].length, data[0].length);
        }
        if (data[i].length < 2) {
            throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_TOO_SMALL, data[i].length, 2, true);
        }
        this.data[i] = MathArrays.copyOf(data[i], data[i].length);
    }
}

From source file:org.pmad.gmm.MyMixMNDEM.java

/**
 * Fit a mixture model to the data supplied to the constructor.
 *
 * The quality of the fit depends on the concavity of the data provided to
 * the constructor and the initial mixture provided to this function. If the
 * data has many local optima, multiple runs of the fitting function with
 * different initial mixtures may be required to find the optimal solution.
 * If a SingularMatrixException is encountered, it is possible that another
 * initialization would work./*from  w w w .java  2 s.c  o  m*/
 *
 * @param initialMixture Model containing initial values of weights and
 *            multivariate normals
 * @param maxIterations Maximum iterations allowed for fit
 * @param threshold Convergence threshold computed as difference in
 *             logLikelihoods between successive iterations
 * @throws SingularMatrixException if any component's covariance matrix is
 *             singular during fitting
 * @throws NotStrictlyPositiveException if numComponents is less than one
 *             or threshold is less than Double.MIN_VALUE
 * @throws DimensionMismatchException if initialMixture mean vector and data
 *             number of columns are not equal
 */
public void fit(final MyMixMND initialMixture, final int maxIterations, final double threshold)
        throws SingularMatrixException, NotStrictlyPositiveException, DimensionMismatchException {
    if (maxIterations < 1) {
        throw new NotStrictlyPositiveException(maxIterations);
    }

    if (threshold < Double.MIN_VALUE) {
        throw new NotStrictlyPositiveException(threshold);
    }

    final int n = data.length;

    // Number of data columns. Jagged data already rejected in constructor,
    // so we can assume the lengths of each row are equal.
    final int numCols = data[0].length;
    final int k = initialMixture.getComponents().size();

    final int numMeanColumns = initialMixture.getComponents().get(0).getSecond().getMeans().length;

    if (numMeanColumns != numCols) {
        throw new DimensionMismatchException(numMeanColumns, numCols);
    }

    int numIterations = 0;
    double previousLogLikelihood = 0d;

    logLikelihood = Double.NEGATIVE_INFINITY;

    // Initialize model to fit to initial mixture.
    fittedModel = new MyMixMND(initialMixture.getComponents());

    while (numIterations++ <= maxIterations
            && FastMath.abs(previousLogLikelihood - logLikelihood) > threshold) {
        System.out.println(numIterations);
        previousLogLikelihood = logLikelihood;
        double sumLogLikelihood = 0d;

        // Mixture components
        final List<Pair<Double, MyMND>> components = fittedModel.getComponents();

        // Weight and distribution of each component
        final double[] weights = new double[k];

        final MyMND[] mvns = new MyMND[k];

        for (int j = 0; j < k; j++) {
            weights[j] = components.get(j).getFirst();
            mvns[j] = components.get(j).getSecond();
        }

        // E-step: compute the data dependent parameters of the expectation
        // function.
        // The percentage of row's total density between a row and a
        // component
        final double[][] gamma = new double[n][k];

        // Sum of gamma for each component
        final double[] gammaSums = new double[k];
        //            for (double d : weights) {
        //            System.out.print(d+" ");
        //         }
        //            System.out.println();
        // Sum of gamma times its row for each each component
        final double[][] gammaDataProdSums = new double[k][numCols];

        for (int i = 0; i < n; i++) {
            final double rowDensity = fittedModel.density(data[i]);
            sumLogLikelihood += FastMath.log(rowDensity);

            for (int j = 0; j < k; j++) {
                gamma[i][j] = weights[j] * mvns[j].density(data[i]);
                if (rowDensity == 0) {
                    if (gamma[i][j] == 0) {
                        gamma[i][j] = weights[j];
                    } else {
                        System.err.println("ele");
                    }
                } else {
                    gamma[i][j] /= rowDensity;
                }
                gammaSums[j] += gamma[i][j];

                for (int col = 0; col < numCols; col++) {
                    gammaDataProdSums[j][col] += gamma[i][j] * data[i][col];
                }
            }
        }

        if (Utils.hasNaN(gamma)) {
            System.out.println("gamma has NaN");
        }
        logLikelihood = sumLogLikelihood / n;

        // M-step: compute the new parameters based on the expectation
        // function.
        final double[] newWeights = new double[k];
        final double[][] newMeans = new double[k][numCols];

        for (int j = 0; j < k; j++) {
            newWeights[j] = gammaSums[j] / n;
            for (int col = 0; col < numCols; col++) {
                newMeans[j][col] = gammaDataProdSums[j][col] / gammaSums[j];
            }
        }
        // Compute new covariance matrices
        final RealMatrix[] newCovMats = new RealMatrix[k];
        for (int j = 0; j < k; j++) {
            newCovMats[j] = new Array2DRowRealMatrix(numCols, numCols);
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < k; j++) {
                final RealMatrix vec = new Array2DRowRealMatrix(MathArrays.ebeSubtract(data[i], newMeans[j]));
                final RealMatrix dataCov = vec.multiply(vec.transpose()).scalarMultiply(gamma[i][j]);
                newCovMats[j] = newCovMats[j].add(dataCov);
            }
        }

        // Converting to arrays for use by fitted model
        final double[][][] newCovMatArrays = new double[k][numCols][numCols];
        for (int j = 0; j < k; j++) {
            newCovMats[j] = newCovMats[j].scalarMultiply(1d / gammaSums[j]);
            newCovMatArrays[j] = newCovMats[j].getData();
            //                if (Utils.hasNaN(newCovMatArrays[j])) {
            //               System.out.println("covmet nan");
            //            }
        }
        //            if (Utils.hasNaN(newMeans)) {
        //            System.out.println("neams nan");
        //         }

        // Update current model
        fittedModel = new MyMixMND(newWeights, newMeans, newCovMatArrays);
    }

    if (FastMath.abs(previousLogLikelihood - logLikelihood) > threshold) {
        // Did not converge before the maximum number of iterations
        //            throw new ConvergenceException();
        System.out.println("Did not converge");
    }
}

From source file:org.pmad.gmm.MyMND.java

/**
 * Creates a multivariate normal distribution with the given mean vector and
 * covariance matrix.//from w ww  .  ja  v  a 2  s  . c  o  m
 * <br/>
 * The number of dimensions is equal to the length of the mean vector
 * and to the number of rows and columns of the covariance matrix.
 * It is frequently written as "p" in formulae.
 *
 * @param rng Random Number Generator.
 * @param means Vector of means.
 * @param covariances Covariance matrix.
 * @throws DimensionMismatchException if the arrays length are
 * inconsistent.
 * @throws SingularMatrixException if the eigenvalue decomposition cannot
 * be performed on the provided covariance matrix.
 * @throws NonPositiveDefiniteMatrixException if any of the eigenvalues is
 * negative.
 */
public MyMND(RandomGenerator rng, final double[] means, final double[][] covariances)
        throws SingularMatrixException, DimensionMismatchException, NonPositiveDefiniteMatrixException {
    super(rng, means.length);

    final int dim = means.length;

    if (covariances.length != dim) {
        throw new DimensionMismatchException(covariances.length, dim);
    }

    for (int i = 0; i < dim; i++) {
        if (dim != covariances[i].length) {
            throw new DimensionMismatchException(covariances[i].length, dim);
        }
    }

    this.means = MathArrays.copyOf(means);
    double msum = 0;
    for (int i = 0; i < covariances.length; i++) {
        for (int j = 0; j < covariances.length; j++) {
            msum += covariances[i][j];
        }
    }
    msum /= covariances.length * covariances.length;
    //        System.out.print("in");
    MyEDC covMatDec = null;
    double a = -1;
    while (true) {
        try {

            covarianceMatrix = new Array2DRowRealMatrix(covariances);

            covMatDec = new MyEDC(covarianceMatrix);

            // Compute and store the inverse.
            covarianceMatrixInverse = covMatDec.getSolver().getInverse();
            a *= -1;
            break;
        } catch (NoDataException e) {
            e.printStackTrace();
        } catch (NullArgumentException e) {
            e.printStackTrace();
        } catch (MathArithmeticException e) {
            e.printStackTrace();
        } catch (SingularMatrixException e) {
            //            System.out.print("S");
            for (int i = 0; i < covariances.length; i++) {
                double add = covariances[i][i] == 0 ? msum : covariances[i][i];
                covariances[i][i] += new Random().nextDouble() * add * 0.01;
            }
        }
        //         catch (MaxCountExceededException e) {
        ////            e.printStackTrace();
        ////            System.out.print("M"+msum);
        //            for (int i = 0; i < covariances.length; i++) {
        //               for (int j = i; j < covariances.length; j++) {
        //                  double add = covariances[i][j] == 0?msum:covariances[i][j];
        //                  add = new Random().nextDouble()*add*0.1*a;
        //                  covariances[i][j] += add;
        //                  covariances[j][i] += add;
        //               }
        //            }
        ////            break;
        //         }
    }
    // Compute and store the determinant.
    covarianceMatrixDeterminant = covMatDec.getDeterminant();

    // Eigenvalues of the covariance matrix.
    final double[] covMatEigenvalues = covMatDec.getRealEigenvalues();

    for (int i = 0; i < covMatEigenvalues.length; i++) {
        if (covMatEigenvalues[i] < 0) {
            throw new NonPositiveDefiniteMatrixException(covMatEigenvalues[i], i, 0);
        }
    }

    // Matrix where each column is an eigenvector of the covariance matrix.
    final Array2DRowRealMatrix covMatEigenvectors = new Array2DRowRealMatrix(dim, dim);
    for (int v = 0; v < dim; v++) {
        final double[] evec = covMatDec.getEigenvector(v).toArray();
        covMatEigenvectors.setColumn(v, evec);
    }

    final RealMatrix tmpMatrix = covMatEigenvectors.transpose();

    // Scale each eigenvector by the square root of its eigenvalue.
    for (int row = 0; row < dim; row++) {
        final double factor = FastMath.sqrt(covMatEigenvalues[row]);
        for (int col = 0; col < dim; col++) {
            tmpMatrix.multiplyEntry(row, col, factor);
        }
    }

    samplingMatrix = covMatEigenvectors.multiply(tmpMatrix);
}

From source file:org.pmad.gmm.MyMND.java

/** {@inheritDoc} */
public double density(final double[] vals) throws DimensionMismatchException {
    final int dim = getDimension();
    if (vals.length != dim) {
        throw new DimensionMismatchException(vals.length, dim);
    }// w w  w.  j av  a  2 s  . com

    return FastMath.pow(2 * FastMath.PI, -0.5 * dim) * FastMath.pow(covarianceMatrixDeterminant, -0.5)
            * getExponentTerm(vals);
}

From source file:Pruning.Experiments.KendallsCorrelation.java

/**
 * Computes the Kendall's Tau rank correlation coefficient between the two arrays.
 *
 * @param xArray first data array/* www . j av a2 s .c o m*/
 * @param yArray second data array
 * @return Returns Kendall's Tau rank correlation coefficient for the two arrays
 * @throws DimensionMismatchException if the arrays lengths do not match
 */
public double correlation(final double[] xArray, final double[] yArray, final int n)
        throws DimensionMismatchException {

    if (xArray.length != yArray.length) {
        throw new DimensionMismatchException(xArray.length, yArray.length);
    }

    final long numPairs = sum(n - 1);

    @SuppressWarnings("unchecked")
    Pair<Double, Double>[] pairs = new Pair[n];
    for (int i = 0; i < n; i++) {
        pairs[i] = new Pair<Double, Double>(xArray[i], yArray[i]);
    }

    Arrays.sort(pairs, new Comparator<Pair<Double, Double>>() {
        public int compare(Pair<Double, Double> pair1, Pair<Double, Double> pair2) {
            int compareFirst = pair1.getFirst().compareTo(pair2.getFirst());
            return compareFirst != 0 ? compareFirst : pair1.getSecond().compareTo(pair2.getSecond());
        }
    });

    long tiedXPairs = 0;
    long tiedXYPairs = 0;
    long consecutiveXTies = 1;
    long consecutiveXYTies = 1;
    Pair<Double, Double> prev = pairs[0];
    for (int i = 1; i < n; i++) {
        final Pair<Double, Double> curr = pairs[i];
        if (curr.getFirst().equals(prev.getFirst())) {
            consecutiveXTies++;
            if (curr.getSecond().equals(prev.getSecond())) {
                consecutiveXYTies++;
            } else {
                tiedXYPairs += sum(consecutiveXYTies - 1);
                consecutiveXYTies = 1;
            }
        } else {
            tiedXPairs += sum(consecutiveXTies - 1);
            consecutiveXTies = 1;
            tiedXYPairs += sum(consecutiveXYTies - 1);
            consecutiveXYTies = 1;
        }
        prev = curr;
    }
    tiedXPairs += sum(consecutiveXTies - 1);
    tiedXYPairs += sum(consecutiveXYTies - 1);

    int swaps = 0;
    @SuppressWarnings("unchecked")
    Pair<Double, Double>[] pairsDestination = new Pair[n];
    for (int segmentSize = 1; segmentSize < n; segmentSize <<= 1) {
        for (int offset = 0; offset < n; offset += 2 * segmentSize) {
            int i = offset;
            final int iEnd = FastMath.min(i + segmentSize, n);
            int j = iEnd;
            final int jEnd = FastMath.min(j + segmentSize, n);

            int copyLocation = offset;
            while (i < iEnd || j < jEnd) {
                if (i < iEnd) {
                    if (j < jEnd) {
                        if (pairs[i].getSecond().compareTo(pairs[j].getSecond()) <= 0) {
                            pairsDestination[copyLocation] = pairs[i];
                            i++;
                        } else {
                            pairsDestination[copyLocation] = pairs[j];
                            j++;
                            swaps += iEnd - i;
                        }
                    } else {
                        pairsDestination[copyLocation] = pairs[i];
                        i++;
                    }
                } else {
                    pairsDestination[copyLocation] = pairs[j];
                    j++;
                }
                copyLocation++;
            }
        }
        final Pair<Double, Double>[] pairsTemp = pairs;
        pairs = pairsDestination;
        pairsDestination = pairsTemp;
    }

    long tiedYPairs = 0;
    long consecutiveYTies = 1;
    prev = pairs[0];
    for (int i = 1; i < n; i++) {
        final Pair<Double, Double> curr = pairs[i];
        if (curr.getSecond().equals(prev.getSecond())) {
            consecutiveYTies++;
        } else {
            tiedYPairs += sum(consecutiveYTies - 1);
            consecutiveYTies = 1;
        }
        prev = curr;
    }
    tiedYPairs += sum(consecutiveYTies - 1);

    final long concordantMinusDiscordant = numPairs - tiedXPairs - tiedYPairs + tiedXYPairs - 2 * swaps;
    final double nonTiedPairsMultiplied = (numPairs - tiedXPairs) * (double) (numPairs - tiedYPairs);
    return concordantMinusDiscordant / FastMath.sqrt(nonTiedPairsMultiplied);
}