com.opengamma.financial.analytics.LabelledMatrix3D.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.financial.analytics.LabelledMatrix3D.java

Source

/**
 * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.financial.analytics;

import it.unimi.dsi.fastutil.objects.ObjectArrayList;

import java.util.Arrays;
import java.util.Comparator;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.Validate;

import com.opengamma.financial.analytics.QuickSorter.ArrayQuickSorter;
import com.opengamma.util.ArgumentChecker;

/**
 * Represents a 3D labeled matrix. The dimensions are named X, Y, Z - values[Z][Y][X].
 * 
 * @param <KX> Key type for the X dimension
 * @param <KY> Key type for the Y dimension
 * @param <KZ> Key type for the Z dimension
 * @param <TX> Tolerance type for X key comparisons
 * @param <TY> Tolerance type for Y key comparisons
 * @param <TZ> Tolerance type for Z key comparisons
 * @param <SUBCLASS> Instantiating sub-class
 */
public abstract class LabelledMatrix3D<KX, KY, KZ, TX, TY, TZ, SUBCLASS> {

    private final KX[] _xKeys;
    private final Object[] _xLabels;
    private final KY[] _yKeys;
    private final Object[] _yLabels;
    private final KZ[] _zKeys;
    private final Object[] _zLabels;
    private final double[][][] _values;

    /**
     * Creates a new 3D labeled matrix. The labels are the {@link Object#toString} forms of the keys.
     * 
     * @param xKeys keys for the X dimension
     * @param yKeys keys for the Y dimension
     * @param zKeys keys for the Z dimension
     * @param values values of the matrix in the shape [Z][Y][X]
     */
    public LabelledMatrix3D(final KX[] xKeys, final KY[] yKeys, final KZ[] zKeys, final double[][][] values) {
        this(xKeys, LabelledMatrixUtils.toString(xKeys), yKeys, LabelledMatrixUtils.toString(yKeys), zKeys,
                LabelledMatrixUtils.toString(zKeys), values);
    }

    /**
     * Creates a new 3D labeled matrix.
     * 
     * @param xKeys keys for the X dimension
     * @param xLabels labels for the X dimension
     * @param yKeys keys for the Y dimension
     * @param yLabels labels for the Y dimension
     * @param zKeys keys for the Z dimension
     * @param zLabels labels for the Z dimension
     * @param values values of the matrix in the shape [Z][Y][X]
     */
    public LabelledMatrix3D(final KX[] xKeys, final Object[] xLabels, final KY[] yKeys, final Object[] yLabels,
            final KZ[] zKeys, final Object[] zLabels, final double[][][] values) {
        ArgumentChecker.notNull(xKeys, "xKeys");
        ArgumentChecker.notNull(xLabels, "xLabels");
        ArgumentChecker.notNull(yKeys, "yKeys");
        ArgumentChecker.notNull(yLabels, "yLabels");
        ArgumentChecker.notNull(zKeys, "zKeys");
        ArgumentChecker.notNull(zLabels, "zLabels");
        ArgumentChecker.notNull(values, "values");
        final int x = xKeys.length;
        Validate.isTrue(xLabels.length == x, "invalid xLabels length");
        final int y = yKeys.length;
        Validate.isTrue(yLabels.length == y, "invalid yLabels length");
        final int z = zKeys.length;
        Validate.isTrue(zLabels.length == z, "invalid zLabels length");
        Validate.isTrue(values.length == z, "invalid zKeys length");
        _xKeys = Arrays.copyOf(xKeys, x);
        _xLabels = Arrays.copyOf(xLabels, x);
        _yKeys = Arrays.copyOf(yKeys, y);
        _yLabels = Arrays.copyOf(yLabels, y);
        _zKeys = Arrays.copyOf(zKeys, z);
        _zLabels = Arrays.copyOf(zLabels, z);
        _values = new double[z][y][x];
        for (int iz = 0; iz < z; iz++) {
            Validate.isTrue(values[iz].length == y, "invalid yKeys length");
            for (int iy = 0; iy < y; iy++) {
                Validate.isTrue(values[iz][iy].length == x, "invalid xKeys length");
                System.arraycopy(values[iz][iy], 0, _values[iz][iy], 0, x);
            }
        }
        quickSortX();
        quickSortY();
        quickSortZ();
    }

    /**
     * Creates a new labeled matrix instance. This is called at the end of operations so that the sub-class can return an object
     * of its own type.
     * 
     * @param xKeys keys for the X dimension of the new matrix
     * @param xLabels labels for the X dimension of the new matrix
     * @param yKeys keys for the Y dimension of the new matrix
     * @param yLabels labels for the Y dimension of the new matrix
     * @param zKeys keys for the Z dimension of the new matrix
     * @param zLabels labels for the Z dimension of the new matrix
     * @param values values of the new matrix in the shape [Z][Y][X]
     * @return the new matrix
     */
    protected abstract SUBCLASS create(final KX[] xKeys, final Object[] xLabels, final KY[] yKeys,
            final Object[] yLabels, final KZ[] zKeys, final Object[] zLabels, final double[][][] values);

    private void quickSortX() {
        (new ArrayQuickSorter<KX>(_xKeys) {

            private final TX _tolerance = getDefaultToleranceX();

            @Override
            protected int compare(final KX first, final KX second) {
                return compareKeysX(first, second, _tolerance);
            }

            @Override
            protected void swap(final int first, final int second) {
                super.swap(first, second);
                swap(_xLabels, first, second);
                for (int iz = 0; iz < _zKeys.length; iz++) {
                    for (int iy = 0; iy < _yKeys.length; iy++) {
                        swap(_values[iz][iy], first, second);
                    }
                }
            }

        }).sort();
    }

    private void quickSortY() {
        (new ArrayQuickSorter<KY>(_yKeys) {

            private final TY _tolerance = getDefaultToleranceY();

            @Override
            protected int compare(final KY first, final KY second) {
                return compareKeysY(first, second, _tolerance);
            }

            @Override
            protected void swap(final int first, final int second) {
                super.swap(first, second);
                swap(_yLabels, first, second);
                for (int iz = 0; iz < _zKeys.length; iz++) {
                    swap(_values[iz], first, second);
                }
            }

        }).sort();
    }

    private void quickSortZ() {
        (new ArrayQuickSorter<KZ>(_zKeys) {

            private final TZ _tolerance = getDefaultToleranceZ();

            @Override
            protected int compare(final KZ first, final KZ second) {
                return compareKeysZ(first, second, _tolerance);
            }

            @Override
            protected void swap(final int first, final int second) {
                super.swap(first, second);
                swap(_zLabels, first, second);
                swap(_values, first, second);
            }

        }).sort();
    }

    /**
     * Returns the default tolerance value for comparison of X dimension keys. This will be used for the initial sort
     * of a matrix, or if a tolerance is not specified for the other operations. It may be null if the
     * {@link #compareKeysX} method accepts it (i.e. ignores it).
     * 
     * @return the default tolerance for X key comparisons
     */
    public TX getDefaultToleranceX() {
        return null;
    }

    /**
     * Returns the default tolerance value for comparison of Y dimension keys. This will be used for the initial sort
     * of a matrix, or if a tolerance is not specified for the other operations. It may be null if the
     * {@link #compareKeysY} method accepts it (i.e. ignores it).
     * 
     * @return the default tolerance for Y key comparisons
     */
    public TY getDefaultToleranceY() {
        return null;
    }

    /**
     * Returns the default tolerance value for comparison of Z dimension keys. This will be used for the initial sort
     * of a matrix, or if a tolerance is not specified for the other operations. It may be null if the
     * {@link #compareKeysZ} method accepts it (i.e. ignores it).
     * 
     * @return the default tolerance for Z key comparisons
     */
    public TZ getDefaultToleranceZ() {
        return null;
    }

    /**
     * Compares two X dimension keys.
     * 
     * @param key1 first key to compare
     * @param key2 second key to compare
     * @param tolerance comparison tolerance
     * @return negative if the first key is before the second, positive if after, zero if equal (given the tolerance)  
     */
    protected abstract int compareKeysX(final KX key1, final KX key2, final TX tolerance);

    /**
     * Returns a {@link Comparator} wrapping the {@link #compareKeysX} method for a given tolerance.
     * 
     * @param tolerance comparison tolerance
     * @return a comparator
     */
    protected Comparator<KX> compareKeysX(final TX tolerance) {
        return new Comparator<KX>() {
            @Override
            public int compare(final KX key1, final KX key2) {
                return compareKeysX(key1, key2, tolerance);
            }
        };
    }

    /**
     * Compares two Y dimension keys.
     * 
     * @param key1 first key to compare
     * @param key2 second key to compare
     * @param tolerance comparison tolerance
     * @return negative if the first key is before the second, positive if after, zero if equal (given the tolerance)  
     */
    protected abstract int compareKeysY(final KY key1, final KY key2, final TY tolerance);

    /**
     * Returns a {@link Comparator} wrapping the {@link #compareKeysY} method for a given tolerance.
     * 
     * @param tolerance comparison tolerance
     * @return a comparator
     */
    protected Comparator<KY> compareKeysY(final TY tolerance) {
        return new Comparator<KY>() {
            @Override
            public int compare(final KY key1, final KY key2) {
                return compareKeysY(key1, key2, tolerance);
            }
        };
    }

    /**
     * Compares two Z dimension keys.
     * 
     * @param key1 first key to compare
     * @param key2 second key to compare
     * @param tolerance comparison tolerance
     * @return negative if the first key is before the second, positive if after, zero if equal (given the tolerance)  
     */
    protected abstract int compareKeysZ(final KZ key1, final KZ key2, final TZ tolerance);

    /**
     * Returns a {@link Comparator} wrapping the {@link #compareKeysZ} method for a given tolerance.
     * 
     * @param tolerance comparison tolerance
     * @return a comparator
     */
    protected Comparator<KZ> compareKeysZ(final TZ tolerance) {
        return new Comparator<KZ>() {
            @Override
            public int compare(final KZ key1, final KZ key2) {
                return compareKeysZ(key1, key2, tolerance);
            }
        };
    }

    /**
     * Adds a labeled matrix to this one to create a new matrix.
     * <p>
     * Each key triple in the other matrix is checked to see if it is in the current; if so, the value for that triple is added. If the key triple is
     * not present, the new key triple, labels and value are attached to the matrix. This method ignores the label - if there is a key already present
     * but the labels do not match, then the new label is the original.
     * 
     * @param other Another labeled matrix
     * @param xTolerance tolerance for detecting a match on the X keys
     * @param yTolerance tolerance for detecting a match on the Y keys
     * @param zTolerance tolerance for detecting a match on the Z keys
     * @return The sum of the matrices
     */
    public SUBCLASS addIgnoringLabel(final LabelledMatrix3D<KX, KY, KZ, TX, TY, TZ, ?> other, final TX xTolerance,
            final TY yTolerance, final TZ zTolerance) {
        return addImpl(other, xTolerance, yTolerance, zTolerance, true);
    }

    /**
     * Adds a labeled matrix to this one to create a new matrix.
     * <p>
     * Each key triple in the other matrix is checked to see if it is in the current; if so, the value for that triple is added. If the key triple is
     * not present, the new key triple, labels and value are attached to the matrix. This method ignores the label - if there is a key already present
     * but the labels do not match, then the new label is the original.
     * 
     * @param other Another labeled matrix
     * @return The sum of the matrices
     */
    public SUBCLASS addIgnoringLabel(final LabelledMatrix3D<KX, KY, KZ, TX, TY, TZ, ?> other) {
        return addIgnoringLabel(other, getDefaultToleranceX(), getDefaultToleranceY(), getDefaultToleranceZ());
    }

    /**
     * Adds a labeled matrix to this one to create a new matrix.
     * <p>
     * Each key triple in the other matrix is checked to see if it is in the current; if so, the value for that triple is added. If the key triple is
     * not present, the new key triple, labels and value are attached to the matrix. If the key labels on the two matrices differ, an exception is thrown.
     * 
     * @param other Another labeled matrix
     * @param xTolerance tolerance for detecting a match on the X keys
     * @param yTolerance tolerance for detecting a match on the Y keys
     * @param zTolerance tolerance for detecting a match on the Z keys
     * @return The sum of the matrices
     */
    public SUBCLASS add(final LabelledMatrix3D<KX, KY, KZ, TX, TY, TZ, ?> other, final TX xTolerance,
            final TY yTolerance, final TZ zTolerance) {
        return addImpl(other, xTolerance, yTolerance, zTolerance, false);
    }

    /**
     * Adds a labeled matrix to this one to create a new matrix.
     * <p>
     * Each key triple in the other matrix is checked to see if it is in the current; if so, the value for that triple is added. If the key triple is
     * not present, the new key triple, labels and value are attached to the matrix. If the key labels on the two matrices differ, an exception is thrown.
     * 
     * @param other Another labeled matrix
     * @return The sum of the matrices
     */
    public SUBCLASS add(final LabelledMatrix3D<KX, KY, KZ, TX, TY, TZ, ?> other) {
        return add(other, getDefaultToleranceX(), getDefaultToleranceY(), getDefaultToleranceZ());
    }

    protected SUBCLASS addImpl(final LabelledMatrix3D<KX, KY, KZ, TX, TY, TZ, ?> other, final TX xTolerance,
            final TY yTolerance, final TZ zTolerance, final boolean ignoreLabels) {
        Validate.notNull(other, "other");
        // Check the new X keys and labels
        int iThis = 0, iOther = 0;
        ObjectArrayList<KX> newXKeys = null;
        ObjectArrayList<Object> newXLabels = null;
        final KX[] otherXKeys = other.getXKeys();
        final Object[] otherXLabels = other.getXLabels();
        final int[] otherXIndex = new int[otherXKeys.length];
        while ((iThis < _xKeys.length) && (iOther < otherXKeys.length)) {
            final int cmp = compareKeysX(_xKeys[iThis], otherXKeys[iOther], xTolerance);
            if (cmp < 0) {
                iThis++;
            } else if (cmp > 0) {
                if (newXKeys == null) {
                    newXKeys = ObjectArrayList.wrap(_xKeys);
                    newXLabels = ObjectArrayList.wrap(_xLabels);
                }
                otherXIndex[iOther] = newXKeys.size();
                newXKeys.add(otherXKeys[iOther]);
                newXLabels.add(otherXLabels[iOther]);
                iOther++;
            } else {
                if (!ignoreLabels && !ObjectUtils.equals(_xLabels[iThis], otherXLabels[iOther])) {
                    throw new IllegalArgumentException("Label mismatch for X key " + _xKeys[iThis] + " - "
                            + _xLabels[iThis] + " vs " + otherXLabels[iOther]);
                }
                otherXIndex[iOther] = iThis;
                iThis++;
                iOther++;
            }
        }
        if (iOther < otherXKeys.length) {
            if (newXKeys == null) {
                newXKeys = ObjectArrayList.wrap(_xKeys);
                newXLabels = ObjectArrayList.wrap(_xLabels);
            }
            do {
                otherXIndex[iOther] = newXKeys.size();
                newXKeys.add(otherXKeys[iOther]);
                newXLabels.add(otherXLabels[iOther]);
                iOther++;
            } while (iOther < otherXKeys.length);
        }
        // Check the new Y keys and labels
        iThis = 0;
        iOther = 0;
        ObjectArrayList<KY> newYKeys = null;
        ObjectArrayList<Object> newYLabels = null;
        final KY[] otherYKeys = other.getYKeys();
        final Object[] otherYLabels = other.getYLabels();
        final int[] otherYIndex = new int[otherYKeys.length];
        while ((iThis < _yKeys.length) && (iOther < otherYKeys.length)) {
            final int cmp = compareKeysY(_yKeys[iThis], otherYKeys[iOther], yTolerance);
            if (cmp < 0) {
                iThis++;
            } else if (cmp > 0) {
                if (newYKeys == null) {
                    newYKeys = ObjectArrayList.wrap(_yKeys);
                    newYLabels = ObjectArrayList.wrap(_yLabels);
                }
                otherYIndex[iOther] = newYKeys.size();
                newYKeys.add(otherYKeys[iOther]);
                newYLabels.add(otherYLabels[iOther]);
                iOther++;
            } else {
                if (!ignoreLabels && !ObjectUtils.equals(_yLabels[iThis], otherYLabels[iOther])) {
                    throw new IllegalArgumentException("Label mismatch for Y key " + _yKeys[iThis] + " - "
                            + _yLabels[iThis] + " vs " + otherYLabels[iOther]);
                }
                otherYIndex[iOther] = iThis;
                iThis++;
                iOther++;
            }
        }
        if (iOther < otherYKeys.length) {
            if (newYKeys == null) {
                newYKeys = ObjectArrayList.wrap(_yKeys);
                newYLabels = ObjectArrayList.wrap(_yLabels);
            }
            do {
                otherYIndex[iOther] = newYKeys.size();
                newYKeys.add(otherYKeys[iOther]);
                newYLabels.add(otherYLabels[iOther]);
                iOther++;
            } while (iOther < otherYKeys.length);
        }
        // Check the new Z keys and labels
        iThis = 0;
        iOther = 0;
        ObjectArrayList<KZ> newZKeys = null;
        ObjectArrayList<Object> newZLabels = null;
        final KZ[] otherZKeys = other.getZKeys();
        final Object[] otherZLabels = other.getZLabels();
        final int[] otherZIndex = new int[otherZKeys.length];
        while ((iThis < _zKeys.length) && (iOther < otherZKeys.length)) {
            final int cmp = compareKeysZ(_zKeys[iThis], otherZKeys[iOther], zTolerance);
            if (cmp < 0) {
                iThis++;
            } else if (cmp > 0) {
                if (newZKeys == null) {
                    newZKeys = ObjectArrayList.wrap(_zKeys);
                    newZLabels = ObjectArrayList.wrap(_zLabels);
                }
                otherZIndex[iOther] = newZKeys.size();
                newZKeys.add(otherZKeys[iOther]);
                newZLabels.add(otherZLabels[iOther]);
                iOther++;
            } else {
                if (!ignoreLabels && !ObjectUtils.equals(_zLabels[iThis], otherZLabels[iOther])) {
                    throw new IllegalArgumentException("Label mismatch for Z key " + _zKeys[iThis] + " - "
                            + _zLabels[iThis] + " vs " + otherZLabels[iOther]);
                }
                otherZIndex[iOther] = iThis;
                iThis++;
                iOther++;
            }
        }
        if (iOther < otherZKeys.length) {
            if (newZKeys == null) {
                newZKeys = ObjectArrayList.wrap(_zKeys);
                newZLabels = ObjectArrayList.wrap(_zLabels);
            }
            do {
                otherZIndex[iOther] = newZKeys.size();
                newZKeys.add(otherZKeys[iOther]);
                newZLabels.add(otherZLabels[iOther]);
                iOther++;
            } while (iOther < otherZKeys.length);
        }
        // Build the new matrix
        final KX[] xKeys = (newXKeys != null) ? newXKeys.toArray(_xKeys) : _xKeys;
        final Object[] xLabels = (newXLabels != null) ? newXLabels.toArray(_xLabels) : _xLabels;
        final KY[] yKeys = (newYKeys != null) ? newYKeys.toArray(_yKeys) : _yKeys;
        final Object[] yLabels = (newYLabels != null) ? newYLabels.toArray(_yLabels) : _yLabels;
        final KZ[] zKeys = (newZKeys != null) ? newZKeys.toArray(_zKeys) : _zKeys;
        final Object[] zLabels = (newZLabels != null) ? newZLabels.toArray(_zLabels) : _zLabels;
        final double[][][] values = new double[zKeys.length][yKeys.length][xKeys.length];
        for (int z = 0; z < _values.length; z++) {
            for (int y = 0; y < _values[z].length; y++) {
                System.arraycopy(_values[z][y], 0, values[z][y], 0, _values[z][y].length);
            }
        }
        final double[][][] otherValues = other.getValues();
        for (int z = 0; z < otherValues.length; z++) {
            final int iz = otherZIndex[z];
            for (int y = 0; y < otherValues[z].length; y++) {
                final int iy = otherYIndex[y];
                for (int x = 0; x < otherValues[z][y].length; x++) {
                    final int ix = otherXIndex[x];
                    values[iz][iy][ix] += otherValues[z][y][x];
                }
            }
        }
        return create(xKeys, xLabels, yKeys, yLabels, zKeys, zLabels, values);
    }

    /**
     * Returns the keys for the X dimension.
     * 
     * @return X keys
     */
    public KX[] getXKeys() {
        return _xKeys;
    }

    /**
     * Returns the labels for the X dimension.
     * 
     * @return X labels
     */
    public Object[] getXLabels() {
        return _xLabels;
    }

    /**
     * Returns the keys for the Y dimension.
     * 
     * @return Y keys
     */
    public KY[] getYKeys() {
        return _yKeys;
    }

    /**
     * Returns the labels for the Y dimension.
     * 
     * @return Y labels
     */
    public Object[] getYLabels() {
        return _yLabels;
    }

    /**
     * Returns the keys for the Z dimension.
     * 
     * @return Z keys
     */
    public KZ[] getZKeys() {
        return _zKeys;
    }

    /**
     * Returns the labels for the Z dimension.
     * 
     * @return Z labels
     */
    public Object[] getZLabels() {
        return _zLabels;
    }

    /**
     * Returns the values of the matrix.
     * 
     * @return the matrix values
     */
    public double[][][] getValues() {
        return _values;
    }

    @Override
    public int hashCode() {
        int hc = 1;
        hc += (hc << 4) + Arrays.hashCode(_values);
        hc += (hc << 4) + Arrays.hashCode(_xKeys);
        hc += (hc << 4) + Arrays.hashCode(_xLabels);
        hc += (hc << 4) + Arrays.hashCode(_yKeys);
        hc += (hc << 4) + Arrays.hashCode(_yLabels);
        hc += (hc << 4) + Arrays.hashCode(_zKeys);
        hc += (hc << 4) + Arrays.hashCode(_zLabels);
        return hc;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof LabelledMatrix3D)) {
            return false;
        }
        final LabelledMatrix3D other = (LabelledMatrix3D) obj;
        return Arrays.deepEquals(_values, other._values) && Arrays.equals(_xKeys, other._xKeys)
                && Arrays.equals(_xLabels, other._xLabels) && Arrays.equals(_yKeys, other._yKeys)
                && Arrays.equals(_yLabels, other._yLabels) && Arrays.equals(_zKeys, other._zKeys)
                && Arrays.equals(_zLabels, other._zLabels);
    }
}