/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.util; import java.text.FieldPosition; import java.text.NumberFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.math.stat.StatUtils; import cern.colt.function.DoubleFunction; import cern.colt.matrix.DoubleMatrix1D; import cern.colt.matrix.DoubleMatrix2D; import cern.colt.matrix.impl.DenseDoubleMatrix1D; import cern.jet.math.Functions; import at.tuwien.ifs.somtoolbox.data.InputDatum; import at.tuwien.ifs.somtoolbox.layers.metrics.MetricException; /** * This class provides several utility methods to operate on Vectors. * * @author Angela Roiger * @author Rudolf Mayer * @version $Id: VectorTools.java 3883 2010-11-02 17:13:23Z frank $ */ public class VectorTools { private static final String SPACING = " "; public static final NumberFormat myFormat = NumberFormat.getNumberInstance(); private static final FieldPosition fp = new FieldPosition(NumberFormat.INTEGER_FIELD); public static double[] subtract(double[] a, double[] b) { if (a.length != b.length) { try { throw new MetricException("Oops ... tried to subtract vectors with different dimensionalities."); } catch (MetricException e) { e.printStackTrace(); } } double[] diff = new double[a.length]; for (int i = 0; i < a.length; i++) { diff[i] = a[i] - b[i]; } return diff; } public static int[] subtract(int[] a, int[] b) { if (a.length != b.length) { try { throw new MetricException("Oops ... tried to subtract vectors with different dimensionalities."); } catch (MetricException e) { e.printStackTrace(); } } int[] diff = new int[a.length]; for (int i = 0; i < a.length; i++) { diff[i] = a[i] - b[i]; } return diff; } public static double[] add(double[] a, double[] b) { if (a.length != b.length) { try { throw new MetricException("Oops ... tried to add vectors with different dimensionalities."); } catch (MetricException e) { e.printStackTrace(); } } double[] add = new double[a.length]; for (int i = 0; i < a.length; i++) { add[i] = a[i] + b[i]; } return add; } public static int[] add(int[] a, int[] b) { if (a.length != b.length) { try { throw new MetricException("Oops ... tried to add vectors with different dimensionalities."); } catch (MetricException e) { e.printStackTrace(); } } int[] add = new int[a.length]; for (int i = 0; i < a.length; i++) { add[i] = a[i] + b[i]; } return add; } public static double[] multiply(double[] a, double[] b) throws MetricException { if (a.length != b.length) { throw new MetricException("Oops ... tried to add vectors with different dimensionalities."); } double[] mukt = new double[a.length]; for (int i = 0; i < a.length; i++) { mukt[i] = a[i] * b[i]; } return mukt; } public static double[] multiply(double[] a, double b) { double[] diff = new double[a.length]; for (int i = 0; i < a.length; i++) { diff[i] = a[i] * b; } return diff; } public static double[] divide(double[] a, double b) { double[] diff = new double[a.length]; for (int i = 0; i < a.length; i++) { diff[i] = a[i] / b; } return diff; } /** * Generates a random double[] with the given dimensionality, using a new random number generator. * * @param dim the dimensionality of the double[] * @return a random double[] */ public static double[] generateRandomVector(int dim) { return generateRandomVector(new Random(), dim); } /** * Generates a random double[] with the given dimensionality, using the given random number generator. * * @param rand a random number generator * @param dim the dimensionality of the double[] * @return a random double[] */ public static double[] generateRandomVector(Random rand, int dim) { return generateRandomVector(rand, dim, false); } /** * Generates a random double[] with the given dimensionality, using a new random number generator. * * @param dim the dimensionality of the double[] * @param generateNegativeNumbers whether to generate negative numbers or not. * @return a random double[] */ public static double[] generateRandomVector(int dim, boolean generateNegativeNumbers) { return generateRandomVector(new Random(), dim, generateNegativeNumbers); } /** * Generates a random double[] with the given dimensionality, using the given random number generator. * * @param rand a random number generator * @param dim the dimensionality of the double[] * @param generateNegativeNumbers whether to generate negative numbers or not. * @return a random double[] */ public static double[] generateRandomVector(Random rand, int dim, boolean generateNegativeNumbers) { Random sign = new Random(); double[] randomVector = new double[dim]; for (int i = 0; i < randomVector.length; i++) { randomVector[i] = rand.nextDouble(); if (generateNegativeNumbers && sign.nextBoolean()) { randomVector[i] = randomVector[i] * -1; } } return randomVector; } public static DoubleMatrix1D generateRandomDoubleMatrix1D(Random rand, int dim, boolean generateNegativeNumbers) { return new DenseDoubleMatrix1D(generateRandomVector(rand, dim, generateNegativeNumbers)); } public static DoubleMatrix1D generateRandomDoubleMatrix1D(int dim, boolean generateNegativeNumbers) { return new DenseDoubleMatrix1D(generateRandomVector(new Random(), dim, generateNegativeNumbers)); } public static DoubleMatrix1D generateRandomDoubleMatrix1D(int dim) { return new DenseDoubleMatrix1D(generateRandomVector(dim, false)); } /** * Normalises a matrix. * * @param matrix the matrix to normalise. */ public static void normalise(DoubleMatrix2D matrix) { final double min = matrix.aggregate(Functions.min, Functions.identity); final double max = matrix.aggregate(Functions.max, Functions.identity); matrix.assign(new DoubleFunction() { @Override public double apply(double argument) { return (argument - min) / (max - min); } }); } public static void divByMax(DoubleMatrix2D matrix) { final double max = matrix.aggregate(Functions.max, Functions.identity); matrix.assign(new DoubleFunction() { @Override public double apply(double argument) { return argument / max; } }); } /** Normalises vector elements by the length of the vector, i.e. the length of the vector will become 1. */ public static double[] normaliseByLength(double... vector) { return normaliseByLength(vector, 1); } public static double vectorLength(double... vector) { double length = 0; for (double element : vector) { length += element * element; } length = Math.sqrt(length); return length; } public static double[] normaliseByLength(double[] vector, double length) { double vectorLength = vectorLength(vector); double[] result = new double[vector.length]; // normalising - dividing each component by the vector length if (vectorLength > 0.0D) { // optimisation - only for vectors which do not have not only 0 values for (int i = 0; i < vector.length; i++) { result[i] = vector[i] / vectorLength * length; } } return result; } public static InputDatum normaliseByLength(InputDatum datum) { return normaliseByLength(datum, 1); } public static InputDatum normaliseByLength(InputDatum datum, double length) { // vectorLength = sqrt(SUM(x[i] * x[i]) double vectorLength = Math.sqrt(datum.getVector().aggregate(Functions.plus, Functions.square)); /* * normx[i] = (x[i] / vectorLength) length normx[i] = x[i] (length / vectorLength) */ double x = length / vectorLength; DenseDoubleMatrix1D xs = new DenseDoubleMatrix1D(datum.getVector().size()); xs.assign(x); datum.getVector().assign(xs, Functions.mult); return datum; } /** * Calculates the median of a vector. * * @param values the vector elements * @return the median value */ public static double median(double... values) { double res = 0; Arrays.sort(values); if (values.length % 2 == 0) { int idx = values.length / 2; res = (values[idx - 1] + values[idx]) / 2; } else { int idx = values.length / 2; res = values[idx]; } return res; } /** * @param a an array * @return the standard deviation */ public static double standard_deviation(double... a) { return Math.sqrt(StatUtils.variance(a)); } public static boolean[] createBooleanArray(int dim, boolean initialValue) { boolean[] presentTerms = new boolean[dim]; for (int l = 0; l < presentTerms.length; l++) { presentTerms[l] = initialValue; } return presentTerms; } /** Creates a {@link Map} with an inversed mapping of the original map, i.e. a valu e-> key mapping. */ public static HashMap<Object, Integer> reverseHashMap(Map<Integer, Object> map) { HashMap<Object, Integer> reversed = new HashMap<Object, Integer>(); for (Integer i : map.keySet()) { reversed.put(map.get(i), i); } return reversed; } /** Calculates the mean vector from the given array of vectors. */ public static double[] meanVector(double[][] a) { double[] meanVector = new double[a[0].length]; for (int i = 0; i < a[0].length; i++) { double sum = 0; for (double[] element : a) { sum += element[i]; } meanVector[i] = sum / a.length; } return meanVector; } public static double[] medianVector(double[][] a) { double[] medianVector = new double[a[0].length]; for (int i = 0; i < a[0].length; i++) { medianVector[i] = median(slice(a, i)); } return medianVector; } public static String printMatrix(double[][] matrix) { StringBuffer b = new StringBuffer(); for (double[] element : matrix) { b.append(printVector(element)); b.append('\n'); } return b.toString(); } public static String printMatrix(Object[][] matrix) { StringBuffer b = new StringBuffer(); for (Object[] element : matrix) { b.append(printVector(element)); b.append('\n'); } return b.toString(); } public static String printMatrix(int[][] matrix) { StringBuffer b = new StringBuffer(); for (int j = 0; j < matrix[0].length; j++) { for (int[] element : matrix) { b.append(element[j]).append(SPACING); } b.append('\n'); } return b.toString(); } /** * Method for printing a double float matrix <br> * Based on ER Harold, "Java I/O", O'Reilly, around p. 473. * * @param m input matrix values, double * @param d display precision, number of decimal places * @param w display precision, total width of floating value */ public static String printMatrix(double[][] m, int d, int w) { StringBuffer b = new StringBuffer(); // Some definitions for handling output formating myFormat.setMaximumIntegerDigits(d); myFormat.setMaximumFractionDigits(d); myFormat.setMinimumFractionDigits(d); for (int i = 0; i < m[0].length; i++) { // Print each row, elements separated by spaces for (int j = 0; j < m.length; j++) { String valString = myFormat.format(m[i][j], new StringBuffer(), fp).toString(); valString = getSpaces(w - fp.getEndIndex()) + valString; b.append(valString); } // Start a new line at the end of a row b.append("\n"); } return b.toString(); } /** * Method printVect for printing a double float vector <br> * Based on ER Harold, "Java I/O", O'Reilly, around p. 473. * * @param m input vector of length m.length * @param d display precision, number of decimal places * @param w display precision, total width of floating value */ public static String printVect(double[] m, int d, int w) { StringBuffer b = new StringBuffer(); // Some definitions for handling output formating myFormat.setMaximumIntegerDigits(d); myFormat.setMaximumFractionDigits(d); myFormat.setMinimumFractionDigits(d); int len = m.length; for (int i = 0; i < len; i++) { String valString = myFormat.format(m[i], new StringBuffer(), fp).toString(); valString = getSpaces(w - fp.getEndIndex()) + valString; b.append(valString); } return b.toString(); } // Little method for helping in output formating public static String getSpaces(int n) { StringBuffer sb = new StringBuffer(Math.max(0, n)); for (int i = 0; i < n; i++) { sb.append(' '); } return sb.toString(); } // FIXME replace calls with ArrayUtils (and a couple of more I suppose) public static StringBuffer printVector(double... vector) { StringBuffer b = new StringBuffer(); b.append('['); for (int i = 0; i < vector.length; i++) { b.append(vector[i]); if (i < vector.length - 1) { b.append(", "); } } b.append(']'); return b; } public static StringBuffer printVector(int... vector) { return printVector(vector, ", "); } public static StringBuffer printVector(int[] vector, String spacing) { StringBuffer b = new StringBuffer(); b.append('['); for (int i = 0; i < vector.length; i++) { b.append(vector[i]); if (i < vector.length - 1) { b.append(spacing); } } b.append(']'); return b; } public static StringBuffer printVector(Object... vector) { return printVector(vector, ", "); } public static StringBuffer printVector(Object[] vector, String spacing) { StringBuffer b = new StringBuffer(); b.append('['); for (int i = 0; i < vector.length; i++) { b.append(vector[i]); if (i < vector.length - 1) { b.append(spacing); } } b.append(']'); return b; } public static String printMatrixComparison(double[][] matrix, double[][] otherMatrix) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < otherMatrix.length; i++) { for (int j = 0; j < otherMatrix.length; j++) { if (otherMatrix[j][i] == matrix[j][i]) { buffer.append("[ok] "); } else { buffer.append("[no] "); } } buffer.append("\n"); } return buffer.toString(); } public static boolean equals(double[][] distanceMatrix, double[][] distanceMatrixFromFile) { for (int i = 0; i < distanceMatrix.length; i++) { if (!Arrays.equals(distanceMatrix[i], distanceMatrixFromFile[i])) { return false; } } return true; } public static double[] generateOneVector(int dim) { return generateVectorWithValue(dim, 1); } public static double[] generateVectorWithValue(int dim, int value) { double[] res = new double[dim]; for (int i = 0; i < res.length; i++) { res[i] = value; } return res; } public static double[][] transpose(double[][] matrix) { double[][] res = new double[matrix[0].length][matrix.length]; for (int i = 0; i < matrix[0].length; i++) { for (int j = 0; j < matrix.length; j++) { res[i][j] = matrix[j][i]; } } return res; } /** main method for testing purposes only. */ public static void main(String[] args) { double[] a1 = new double[] { 1, 2, 3, 4 }; double[] a2 = new double[] { 2, 2, 3, 5 }; double[] a3 = new double[] { 5, 8, 9, 2 }; double[][] aa = new double[][] { a1, a2, a3 }; System.out.println(printMatrix(aa)); System.out.println(printMatrix(transpose(aa))); System.out.println("===================="); for (int i = 0; i < aa[0].length; i++) { System.out.println(printVector(slice(aa, i))); } System.out.println("===================="); System.out.println(printVector(meanVector(aa))); System.out.println(printVector(medianVector(aa))); System.out.println("===================="); double[][] m = { { 1, 2 }, { 3, 4 } }; double[] vec = { 1, 2 }; System.out.println(printVector(multiply(m, vec))); System.out.println(printVector(multiply(vec, m))); double[] vec2 = { 99, 1, 2, 3, 4, 5, 6, .5d }; System.out.println(ArrayUtils.toString(vec2)); int toRemove = 4; int numOfMaxIndices = 3; double[] shrinked = removeIndex(vec2, toRemove); System.out.println("Removed index: " + toRemove + ":\n" + ArrayUtils.toString(shrinked)); System.out.println("Max index is: " + getIndexOfMaxValue(vec2)); System.out.println("Max indices for: " + numOfMaxIndices + " are:\n" + ArrayUtils.toString(getIndicesOfMaxValues(vec2, numOfMaxIndices))); Point3d a = new Point3d(1, 2, -3); Point3d b = new Point3d(5, 6, 0); System.out.println(ArrayUtils.toString(a)); System.out.println(ArrayUtils.toString(b)); System.out.println(ArrayUtils.toString(VectorTools.crossProduct(a, b))); double[] x = new double[] { 12, 12, 5, 2 }; System.out.println(ArrayUtils.toString(x)); System.out.println(ArrayUtils.toString(getIndicesOfMaxValues(x, 3))); } public static double[] multiply(double[][] matrix, double[] vec) { double[] res = new double[vec.length]; for (int i = 0; i < res.length; i++) { for (int j = 0; j < res.length; j++) { res[i] += matrix[i][j] * vec[j]; } } return res; } public static double[] multiply(double[] vec, double[][] matrix) { double[] res = new double[vec.length]; for (int i = 0; i < res.length; i++) { for (int j = 0; j < res.length; j++) { res[i] += matrix[j][i] * vec[j]; } } return res; } /** * Gets the index of the max value in an array. * * @param array the input array * @return index of the maximum value in the array */ public static int getIndexOfMaxValue(int... array) { int max = Integer.MIN_VALUE; int index = -1; for (int i = 0; i < array.length; i++) { if (array[i] >= max) { max = array[i]; index = i; } } return index; } /** * Gets the index of the max value in an array. * * @param array the input array * @return index of the maximum value in the array */ public static int getMaxValue(int... array) { int max = 0; for (int i : array) { if (i > max) { max = i; } } return max; } /** * Gets the index of the max value in an array (for a double array his time). * * @param array the input array * @return index of the maximum value in the array * @deprecated not used anymore, marked for removal */ @Deprecated public static int getIndexOfMaxValue(double... array) { double max = Double.MIN_VALUE; int index = -1; for (int i = 0; i < array.length; i++) { if (array[i] >= max) { max = array[i]; index = i; } } return index; } /** * get the max indices of the numberOfIndices largest values in an array * * @param array the input array * @param numberOfIndices the desired number of indices to get in return * @return indices of largest values in the array */ public static int[] getIndicesOfMaxValues(double[] array, int numberOfIndices) { int[] indices = new int[numberOfIndices]; // sort first, then get indices from original array we need to // clone here, otherwise nothing is going to work anymore double[] sortedArray = array.clone(); Arrays.sort(sortedArray); // get in descending order, i.e. reverse ArrayUtils.reverse(sortedArray); // we get the indices of all max values // we count double occurrences of values and take this // into account when getting the indices, we hence avoid // double indices in the result double oldValue = 0d; int skip = 0; for (int i = 0; i < numberOfIndices; i++) { if (sortedArray[i] == oldValue) { skip++; } else { skip = 0; oldValue = sortedArray[i]; } int maxIndex = getIndexOfValue(array, sortedArray[i], skip); // we avoid everything being set to -1 if (maxIndex == -1) { return indices; } indices[i] = maxIndex; } return indices; } /** * get the max indices of the numberOfIndices largest values in an array * * @param array the input array * @param numberOfIndices how many * @return the indices */ public static int[] getIndicesOfMaxValues(int[] array, int numberOfIndices) { int[] indices = new int[numberOfIndices]; // sort first, then get indices from original array we need to // clone here, otherwise nothing is going to work anymore int[] sortedArray = array.clone(); Arrays.sort(sortedArray); // get in descending order, i.e. reverse ArrayUtils.reverse(sortedArray); // we get the indices of all max values for (int i = 0; i < numberOfIndices; i++) { int maxIndex = getIndexOfValue(array, sortedArray[i]); // we avoid everything being set to -1 if (maxIndex == -1) { return indices; } indices[i] = maxIndex; } return indices; } /** * get the indices of the numberOfIndices largest values in an array * * @param array input * @param threshold value to compare to * @return indices of all values larger than the given threshold */ public static int[] getIndicesOfMaxValues(double[] array, double threshold) { int num = getNumIndicesLargerThanThreshold(array, threshold); int[] indices = new int[num]; int indexInFoundOnes = 0; for (int i = 0; i < array.length; i++) { if (array[i] > threshold) { indices[indexInFoundOnes] = i; indexInFoundOnes++; } } return indices; } /** * return the indices of all values larger than the given threshold * * @param array input * @param threshold to compare to * @return number of indices containing a value larger than the threshold */ public static int getNumIndicesLargerThanThreshold(double[] array, double threshold) { int num = 0; for (double element : array) { if (element > threshold) { num++; } } return num; } /** * get the index of a given value in an array and skip the given number of indices to avoid duplicate indices in the * results, i.e. if the array is [12, 12, 5, 2] we will return 0 if skip = 0 and 1 if skip = 1 * * @param array the input array * @param d the value to search for * @return the index */ private static int getIndexOfValue(double[] array, double d, int skip) { for (int i = 0; i < array.length; i++) { if (array[i] == d && skip-- == 0) { return i; } } return -1; } /** * get the index of a given value in an array * * @param array the input array * @param d the value to search for * @return the index */ private static int getIndexOfValue(int[] array, int d) { for (int i = 0; i < array.length; i++) { if (array[i] == d) { return i; } } return -1; } /** * Ever needed a certain number of indices with maximum values sorted in descending order? Well, here it comes. * * @param array an array of doubles * @param numberOfIndices the number of max values you want the indices of * @return the indices of these max values in the given array * @deprecated FIXME this is not working marked for removal */ @Deprecated public static int[] getIndicesOfMaxValuesOLD(int[] array, int numberOfIndices) { int[] indices = new int[numberOfIndices]; for (int i = 0; i < numberOfIndices; i++) { int maxIndex = getIndexOfMaxValue(array); indices[i] = maxIndex; array = removeIndex(array, maxIndex); } return indices; } /** * Remove an index from an array, it's as simple as that. * * @param array the input array * @param indexToRemove the index to remove * @return an array of size array.length - 1 FIXME how about templates for this one? * @deprecated use {@link org.apache.commons.lang.ArrayUtils#remove(double[], int)} instead */ @Deprecated public static double[] removeIndex(double[] array, int indexToRemove) { double[] shrinkedArray = new double[array.length - 1]; for (int i = 0, j = 0; i < array.length; i++) { if (i != indexToRemove) { shrinkedArray[j++] = array[i]; } } return shrinkedArray; } /** * Remove an index from an array, it's as simple as that. * * @param array the input array * @param indexToRemove index to remove * @return an array of size array.length - 1 FIXME how about templates for this one? */ public static int[] removeIndex(int[] array, int indexToRemove) { int[] shrinkedArray = new int[array.length - 1]; for (int i = 0, j = 0; i < array.length; i++) { if (i != indexToRemove) { shrinkedArray[j++] = array[i]; } } return shrinkedArray; } public static int sum(int... array) { int sum = 0; for (int element : array) { sum += element; } return sum; } public static int calculateArrayOverlaps(int[] array1, int[] array2) { int overlaps = 0; for (int i = 0; i < array1.length; i++) { if (array1[i] == array2[i]) { overlaps++; } } return overlaps; } /** * merges two direction arrays, i.e. {1, 0, 0} and {0, 0, 1} become {1, 0, 1 * * @param directions1 direction one * @param directions2 direction two * @return the merged direction */ public static int[] mergeArrays(int[] directions1, int[] directions2) { int[] directions = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; for (int i = 0; i < directions.length; i++) { if (directions1[i] > 0) { directions[i] = directions1[i]; } if (directions2[i] > 0) { directions[i] = directions2[i]; } } return directions; } public static double[][][] multiply(double[][][] data, double factor) { double[][][] newData = new double[data.length][data[0].length][data[0][0].length]; for (int i = 0; i < newData.length; i++) { for (int j = 0; j < newData[0].length; j++) { for (int k = 0; k < newData[0][0].length; k++) { newData[i][j][k] = data[i][j][k] * factor; } } } return newData; } public static double[][][] divide(double[][][] data, double factor) { double[][][] newData = new double[data.length][data[0].length][data[0][0].length]; for (int i = 0; i < newData.length; i++) { for (int j = 0; j < newData[0].length; j++) { for (int k = 0; k < newData[0][0].length; k++) { newData[i][j][k] = data[i][j][k] / factor; } } } return newData; } public static boolean isNullVector(double... vector) { for (double element : vector) { if (element != 0d) { return false; } } return true; } public static boolean isNullVector(int... vector) { for (int element : vector) { if (element != 0) { return false; } } return true; } /** * Calculates vector normalised to unit length. * * @param vector Vector to be normalised * @return The normalised vector. */ public static double[] normaliseVectorToUnitLength(double... vector) { int dim = vector.length; double len = 0; // double y = 10.0; // Ly for (int i = 0; i < dim; i++) { len += vector[i] * vector[i]; // L2 // len += vector[i]; // L1 // if (vector[i] > len) { // Linf // len = vector[i]; // Linf // } // Linf // len += Math.pow(vector[i],y); // Ly } len = Math.sqrt(len); // L2 // len = len; // L1 Linf /** Ly ** */ // if ( len < 0 && Math.round(y) == y && Math.round(y) % 2 == 1 ) { // len = Math.pow( -len, 1/y ); // len = -len; // } else { // len = Math.pow( len, 1/y ); // } /** Ly ** */ if (len > 0) { for (int i = 0; i < dim; i++) { vector[i] /= len; } } return vector; } /** * Calculate the cross product of two 3-dimensional direction vectors. This is needed to check whether two lines are * parallel or not. At first you gotta get two direction vectors by subtracting x, y, and z values. Then you can * calculate the cross product. If the cross product = [0 0 0] you found your parallel lines * * @param a first direction vector * @param b second direction vector * @return cross product of a and b */ public static Point3d crossProduct(Point3d a, Point3d b) { Point3d crossProduct = new Point3d(); crossProduct.x = a.y * b.z - a.z * b.y; crossProduct.y = a.z * b.x - a.x * b.z; crossProduct.z = a.x * b.y - a.y * b.x; return crossProduct; } public static int[] computeDefaultSize(int numInstances) { return computeDefaultSize(numInstances, 10); } public static int[] computeDefaultSize(int numInstances, int elementsPerUnit) { int numUnits = numInstances / elementsPerUnit; // area = x * y = x * (x / 4 * 3) = x^2 * 0.75 // x^2 = area / 0.75 // x = sqrt(area/0.75) int xSize = (int) Math.sqrt(numUnits / 0.75); int ySize = (int) (Math.sqrt(numUnits / 0.75) / 4 * 3); return new int[] { xSize, ySize }; } public static double[] slice(double[][] array, int i) { double[] res = new double[array.length]; for (int j = 0; j < res.length; j++) { res[j] = array[j][i]; } return res; } public static double[] findMiddle(double[] a, double[] b) { if (a.length != b.length) { throw new IndexOutOfBoundsException(); } double[] res = new double[a.length]; for (int i = 0; i < res.length; i++) { res[i] = a[i] + 0.5 * (b[i] - a[i]); } return res; } }