/**
 * 
 * Written by Márton Münz and Philip C Biggin
 * Copyright (c) University of Oxford, United Kingdom
 * Visit http://sbcb.bioch.ox.ac.uk/jgromacs/
 * 
 * This source code file is part of JGromacs v1.0.
 * 
 * JGromacs v1.0 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JGromacs v1.0. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JGromacs v1.0. If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package jgromacs.analysis;

import java.util.ArrayList;
import jgromacs.data.FrameIndexSet;
import jgromacs.data.IndexSet;
import jgromacs.data.PointList;
import jgromacs.data.Structure;
import jgromacs.data.Trajectory;
import jama.Matrix;

/**
 * Collection of methods for measuring structural similarity
 *
 */
public class Similarity {
	
	// Coordinate-based measures
	
	/**
	 * Calculates RMSD similarity between two point lists
	 * @param A first point list
	 * @param B second point list
	 * @return RMSD
	 */
	public static double getRMSD(PointList A, PointList B){
		PointList superposedA = Superposition.superposeTo(A, B);
		return getRMSDnoSuperposition(superposedA, B);
	}
	
	/**
	 * Calculates RMSD similarity between two structures
	 * @param A first structure
	 * @param B second structure
	 * @return RMSD
	 */
	public static double getRMSD(Structure A, Structure B){
		return getRMSD(A.getAllAtomCoordinates(),B.getAllAtomCoordinates());
	}
	
	/**
	 * Calculates RMSD similarity between two groups of atoms
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @return RMSD
	 */
	public static double getRMSD(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getRMSD(subA, subB);
	}
	
	/**
	 * Calculates the RMSDi profile between two point lists
	 * @param A first point list
	 * @param B second point list
	 * @return RMSDi profile
	 */
	public static ArrayList<Double> getRMSDiProfile(PointList A, PointList B){
		PointList superposedA = Superposition.superposeTo(A, B);
		return getRMSDiProfileNoSuperposition(superposedA, B);
	}
	
	/**
	 * Calculates the RMSDi profile between two structures
	 * @param A first structure
	 * @param B second structure
	 * @return RMSDi profile
	 */
	public static ArrayList<Double> getRMSDiProfile(Structure A, Structure B){
		return getRMSDiProfile(A.getAllAtomCoordinates(),B.getAllAtomCoordinates());
	}
	
	/**
	 * Calculates the RMSDi profile between two groups of atoms
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @return RMSDi profile
	 */
	public static ArrayList<Double> getRMSDiProfile(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getRMSDiProfile(subA,subB);
	}
	
	/**
	 * Calculates RMSD similarity between two point lists without doing superposition
	 * @param A first point list
	 * @param B second point list
	 * @return RMSD without superposition
	 */
	public static double getRMSDnoSuperposition(PointList A, PointList B){
		double ret = 0;
		for (int i = 0; i < A.getNumberOfPoints(); i++) 
		ret+= Math.pow(A.getPoint(i).distance(B.getPoint(i)),2);
		double n = (double)A.getNumberOfPoints();
		return Math.sqrt(ret/n);
	}
	
	/**
	 * Calculates RMSD similarity between two structures without doing superposition
	 * @param A first structure
	 * @param B second structure
	 * @return RMSD without superposition
	 */
	public static double getRMSDnoSuperposition(Structure A, Structure B){
		return getRMSDnoSuperposition(A.getAllAtomCoordinates(),B.getAllAtomCoordinates());
	}
	
	/**
	 * Calculates RMSD similarity between two groups of atoms without superposition
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @return RMSD without superposition
	 */
	public static double getRMSDnoSuperposition(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getRMSDnoSuperposition(subA, subB);
	}

	/**
	 * Calculates the RMSDi profile between two point lists without superposition
	 * @param A first point list
	 * @param B second point list
	 * @return RMSDi profile without superposition
	 */
	public static ArrayList<Double> getRMSDiProfileNoSuperposition(PointList A, PointList B){
		ArrayList<Double> ret = new ArrayList<Double>();
		for (int i = 0; i < A.getNumberOfPoints(); i++) 
		ret.add(A.getPoint(i).distance(B.getPoint(i)));
		return ret;
	}
	
	/**
	 * Calculates the RMSDi profile between two structures without superposition
	 * @param A first structure
	 * @param B second structure
	 * @return RMSDi profile without superposition
	 */
	public static ArrayList<Double> getRMSDiProfileNoSuperposition(Structure A, Structure B){
		return getRMSDiProfileNoSuperposition(A.getAllAtomCoordinates(),B.getAllAtomCoordinates());
	}
	
	/**
	 * Calculates the RMSDi profile between two groups of atoms without superposition
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @return RMSDi profile without superposition
	 */
	public static ArrayList<Double> getRMSDiProfileNoSuperposition(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getRMSDiProfileNoSuperposition(subA, subB);
	}
	
	// Distance-based measures
	
	/**
	 * Calculates dRMSD similarity between two distance matrices
	 * @param A first distance matrix
	 * @param B second distance matrix
	 * @return dRMSD
	 */
	public static double getDRMSD(Matrix A, Matrix B){
		double ret = 0;
		int n = A.getRowDimension();
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				double dif = A.get(i, j)-B.get(i, j);
				ret+=Math.pow(dif, 2);
			}
		}
		return Math.sqrt(ret/(n*n));
	}
	
	/**
	 * Calculates dRMSD similarity between two point lists
	 * @param A first point list
	 * @param B second point list
	 * @return dRMSD
	 */
	public static double getDRMSD(PointList A, PointList B){
		double ret = 0;
		Matrix D1 = Distances.getDistanceMatrix(A);
		Matrix D2 = Distances.getDistanceMatrix(B);
		int n = D1.getRowDimension();
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				double dif = D1.get(i, j)-D2.get(i, j);
				ret+=Math.pow(dif, 2);
			}
		}
		return Math.sqrt(ret/(n*n));
	}
	
	/**
	 * Calculates dRMSD similarity between two structures
	 * @param A first structure
	 * @param B second structure
	 * @return dRMSD
	 */
	public static double getDRMSD(Structure A, Structure B){
		return getDRMSD(A.getAllAtomCoordinates(),B.getAllAtomCoordinates());
	}
	
	/**
	 * Calculates dRMSD similarity between two groups of atoms
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @return dRMSD
	 */
	public static double getDRMSD(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getDRMSD(subA, subB);
	}
	
	/**
	 * Calculates the dRMSDi profile between two distance matrices
	 * @param A first distance matrix
	 * @param B second distance matrix
	 * @return dRMSDi profile
	 */
	public static ArrayList<Double> getDRMSDiProfile(Matrix A, Matrix B){
		ArrayList<Double> ret = new ArrayList<Double>();
		int n = A.getRowDimension();
		for (int i = 0; i < n; i++) {
			double drmsdi = 0;
			for (int j = 0; j < n; j++) 
			drmsdi+=Math.pow(A.get(i, j)-B.get(i, j), 2);
			drmsdi = Math.sqrt(drmsdi/n);
			ret.add(drmsdi);
		}
		return ret;
	}
	
	/**
	 * Calculates the dRMSDi profile between two point lists
	 * @param A first point list
	 * @param B second point list
	 * @return dRMSDi profile
	 */
	public static ArrayList<Double> getDRMSDiProfile(PointList A, PointList B){
		ArrayList<Double> ret = new ArrayList<Double>();
		Matrix D1 = Distances.getDistanceMatrix(A);
		Matrix D2 = Distances.getDistanceMatrix(B);
		int n = D1.getRowDimension();
		for (int i = 0; i < n; i++) {
			double drmsdi = 0;
			for (int j = 0; j < n; j++) 
			drmsdi+=Math.pow(D1.get(i, j)-D2.get(i, j), 2);
			drmsdi = Math.sqrt(drmsdi/n);
			ret.add(drmsdi);
		}
		return ret;
	}
	
	/**
	 * Calculates the dRMSDi profile between two structures
	 * @param A first structure
	 * @param B second structure
	 * @return dRMSDi profile
	 */
	public static ArrayList<Double> getDRMSDiProfile(Structure A, Structure B){
		return getDRMSDiProfile(A.getAllAtomCoordinates(),B.getAllAtomCoordinates());
	}
	
	/**
	 * Calculates the dRMSDi profile between two groups of atoms
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @return dRMSDi profile
	 */
	public static ArrayList<Double> getDRMSDiProfile(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getDRMSDiProfile(subA, subB);
	}
	
	/**
	 * Calculates the wdRMSD (weighted dRMSD) similarity of two point lists
	 * @param A first point list
	 * @param B second point list
	 * @param W weight matrix
	 * @return wdRMSD
	 */
	public static double getWDRMSD(PointList A, PointList B, Matrix W){
		double ret = 0;
		double N = 0;
		Matrix D1 = Distances.getDistanceMatrix(A);
		Matrix D2 = Distances.getDistanceMatrix(B);
		int n = D1.getRowDimension();
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				double dif = D1.get(i, j)-D2.get(i, j);
				ret+=W.get(i, j)*Math.pow(dif, 2);
				N+=W.get(i, j);
			}
		}
		return Math.sqrt(ret/N);
	}
	
	/**
	 * Calculates the wdRMSD (weighted dRMSD) similarity of two structures
	 * @param A first structure
	 * @param B second structure
	 * @param W weight matrix
	 * @return wdRMSD
	 */
	public static double getWDRMSD(Structure A, Structure B, Matrix W){
		return getWDRMSD(A.getAllAtomCoordinates(),B.getAllAtomCoordinates(),W);
	}
	
	/**
	 * Calculates wdRMSD (weighted dRMSD) similarity between two groups of atoms
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @param W weight matrix
	 * @return wdRMSD
	 */
	public static double getWDRMSD(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB, Matrix W){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getWDRMSD(subA, subB, W);
	}

	/**
	 * Calculates the wdRMSDi (weighted dRMSDi) profile between two point lists
	 * @param A first point list
	 * @param B second point list
	 * @param W weight matrix
	 * @return wdRMSDi profile
	 */
	public static ArrayList<Double> getWDRMSDiProfile(PointList A, PointList B, Matrix W){
		ArrayList<Double> ret = new ArrayList<Double>();
		Matrix D1 = Distances.getDistanceMatrix(A);
		Matrix D2 = Distances.getDistanceMatrix(B);
		int n = D1.getRowDimension();
		for (int i = 0; i < n; i++) {	
			double wdrmsdi = 0;
			double N = 0;
			for (int j = 0; j < n; j++) {
				double dif = D1.get(i, j)-D2.get(i, j);
				wdrmsdi+=W.get(i, j)*Math.pow(dif, 2);
				N+=W.get(i, j);
			}
			wdrmsdi = Math.sqrt(wdrmsdi/N);
			ret.add(wdrmsdi);
		}
		return ret;
	}
	
	/**
	 * Calculates the wdRMSDi (weighted dRMSDi) profile between two structures
	 * @param A first structure
	 * @param B second structure
	 * @param W weight matrix
	 * @return wdRMSDi profile
	 */
	public static ArrayList<Double> getWDRMSDiProfile(Structure A, Structure B, Matrix W){
		return getWDRMSDiProfile(A.getAllAtomCoordinates(),B.getAllAtomCoordinates(),W);
	}
	
	/**
	 * Calculates the wdRMSDi (weighted dRMSDi) profile between two groups of atoms
	 * @param A first structure
	 * @param indicesA index set of atoms in the first structure
	 * @param B second structure
	 * @param indicesB index set of atoms in the second structure
	 * @param W weight matrix
	 * @return wdRMSDi profile
	 */
	public static ArrayList<Double> getWDRMSDiProfile(Structure A, IndexSet indicesA, Structure B, IndexSet indicesB, Matrix W){
		Structure subA = A.getSubStructure(indicesA);
		Structure subB = B.getSubStructure(indicesB);
		return getWDRMSDiProfile(subA, subB, W);
	}
	
	// Similarity matrices
	
	/**
	 * Calculates the similarity matrix of all frames in a trajectory using the RMSD measure
	 * @param t trajectory
	 * @return similarity matrix
	 */
	public static Matrix getSimilarityMatrixRMSD(Trajectory t){
		int dim = t.getNumberOfFrames();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			PointList frameI = t.getFrameAsPointList(i);
			for (int j = i+1; j < dim; j++) {
				PointList frameJ = t.getFrameAsPointList(j);
				double sim = getRMSD(frameI, frameJ);
				ret.set(i, j, sim);
				ret.set(j, i, sim);
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the similarity matrix of all frames in a trajectory using the RMSD measure
	 * taking into account only a subset of atoms 
	 * @param t trajectory
	 * @param indices index set of atoms
	 * @return similarity matrix
	 */
	public static Matrix getSimilarityMatrixRMSD(Trajectory t, IndexSet indices){
		int dim = t.getNumberOfFrames();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			Structure frameI = t.getFrameAsStructure(i);
			for (int j = i+1; j < dim; j++) {
				Structure frameJ = t.getFrameAsStructure(j);
				double sim = getRMSD(frameI, indices, frameJ, indices);
				ret.set(i, j, sim);
				ret.set(j, i, sim);
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the similarity matrix of all frames in a trajectory using the dRMSD measure
	 * @param t trajectory
	 * @return similarity matrix
	 */
	public static Matrix getSimilarityMatrixDRMSD(Trajectory t){
		int dim = t.getNumberOfFrames();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			PointList frameI = t.getFrameAsPointList(i);
			for (int j = i+1; j < dim; j++) {
				PointList frameJ = t.getFrameAsPointList(j);
				double sim = getDRMSD(frameI, frameJ);
				ret.set(i, j, sim);
				ret.set(j, i, sim);
			}
		}
		return ret;
	}
	

	/**
	 * Calculates the similarity matrix of all frames in a trajectory using the dRMSD measure
	 * taking into account only a subset of atoms 
	 * @param t trajectory
	 * @param indices index set of atoms
	 * @return similarity matrix
	 */
	public static Matrix getSimilarityMatrixDRMSD(Trajectory t, IndexSet indices){
		int dim = t.getNumberOfFrames();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			Structure frameI = t.getFrameAsStructure(i);
			for (int j = i+1; j < dim; j++) {
				Structure frameJ = t.getFrameAsStructure(j);
				double sim = getDRMSD(frameI, indices, frameJ, indices);
				ret.set(i, j, sim);
				ret.set(j, i, sim);
			}
		}
		return ret;
	}

	/**
	 * Calculates the similarity matrix of all frames in a trajectory using the wdRMSD (weighted dRMSD) measure
	 * @param t trajectory
	 * @param W weight matrix
	 * @return similarity matrix
	 */
	public static Matrix getSimilarityMatrixWDRMSD(Trajectory t, Matrix W){
		int dim = t.getNumberOfFrames();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			PointList frameI = t.getFrameAsPointList(i);
			for (int j = i+1; j < dim; j++) {
				PointList frameJ = t.getFrameAsPointList(j);
				double sim = getWDRMSD(frameI, frameJ, W);
				ret.set(i, j, sim);
				ret.set(j, i, sim);
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the similarity matrix of all frames in a trajectory using the wdRMSD (weighted dRMSD) measure
	 * taking into account only a subset of atoms 
	 * @param t trajectory
	 * @param indices index set of atoms
	 * @param W weight matrix
	 * @return similarity matrix
	 */
	public static Matrix getSimilarityMatrixWDRMSD(Trajectory t, IndexSet indices, Matrix W){
		int dim = t.getNumberOfFrames();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			Structure frameI = t.getFrameAsStructure(i);
			for (int j = i+1; j < dim; j++) {
				Structure frameJ = t.getFrameAsStructure(j);
				double sim = getWDRMSD(frameI, indices, frameJ, indices, W);
				ret.set(i, j, sim);
				ret.set(j, i, sim);
			}
		}
		return ret;
	}
	
	// Similarity time series
	
	/**
	 * Returns the time series of RMSD in a trajectory with regards to a reference point list
	 * @param t trajectory
	 * @param R reference point list
	 * @return time series of RMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesRMSD(Trajectory t, PointList R){
		ArrayList<Double> ret = new ArrayList<Double>();
		int dim = t.getNumberOfFrames();
		for (int i = 0; i < dim; i++) {
			PointList frame = t.getFrameAsPointList(i);
			ret.add(getRMSD(frame, R));
		}
		return ret;
	}
	
	/**
	 * Returns the time series of RMSD in a trajectory with regards to a reference structure
	 * @param t trajectory
	 * @param R reference structure
	 * @return time series of RMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesRMSD(Trajectory t, Structure R){
		return getSimilarityTimeSeriesRMSD(t,R.getAllAtomCoordinates());
	}	

	/**
	 * Returns the time series of RMSD in a trajectory with regards to a reference structure
	 * taking into account only a subset of atoms
	 * @param t trajectory
	 * @param indicesT index set of atoms in the trajectory
	 * @param R reference structure
	 * @param indicesR index set of atoms in the reference structure
	 * @return time series of RMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesRMSD(Trajectory t, IndexSet indicesT, Structure R, IndexSet indicesR){
		ArrayList<Double> ret = new ArrayList<Double>();
		int dim = t.getNumberOfFrames();
		for (int i = 0; i < dim; i++) {
			Structure frame = t.getFrameAsStructure(i);
			ret.add(getRMSD(frame, indicesT, R, indicesR));
		}
		return ret;
	}
	
	/**
	 * Returns the time series of dRMSD in a trajectory with regards to a reference point list
	 * @param t trajectory
	 * @param R reference point list
	 * @return time series of dRMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesDRMSD(Trajectory t, PointList R){
		ArrayList<Double> ret = new ArrayList<Double>();
		int dim = t.getNumberOfFrames();
		for (int i = 0; i < dim; i++) {
			PointList frame = t.getFrameAsPointList(i);
			ret.add(getDRMSD(frame, R));
		}
		return ret;
	}
	
	/**
	 * Returns the time series of dRMSD in a trajectory with regards to a reference structure
	 * @param t trajectory
	 * @param R reference structure
	 * @return time series of dRMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesDRMSD(Trajectory t, Structure R){
		return getSimilarityTimeSeriesDRMSD(t,R.getAllAtomCoordinates());
	}
	
	/**
	 * Returns the time series of dRMSD in a trajectory with regards to a reference structure
	 * taking into account only a subset of atoms
	 * @param t trajectory
	 * @param indicesT index set of atoms in the trajectory
	 * @param R reference structure
	 * @param indicesR index set of atoms in the reference structure
	 * @return time series of dRMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesDRMSD(Trajectory t, IndexSet indicesT, Structure R, IndexSet indicesR){
		ArrayList<Double> ret = new ArrayList<Double>();
		int dim = t.getNumberOfFrames();
		for (int i = 0; i < dim; i++) {
			Structure frame = t.getFrameAsStructure(i);
			ret.add(getDRMSD(frame, indicesT, R, indicesR));
		}
		return ret;
	}
	
	
	/**
	 * Returns the time series of wdRMSD (weighted dRMSD) in a trajectory with regards to a reference point list
	 * @param t trajectory
	 * @param R reference point list
	 * @param W weight matrix
	 * @return time series of wdRMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesWDRMSD(Trajectory t, PointList R, Matrix W){
		ArrayList<Double> ret = new ArrayList<Double>();
		int dim = t.getNumberOfFrames();
		for (int i = 0; i < dim; i++) {
			PointList frame = t.getFrameAsPointList(i);
			ret.add(getWDRMSD(frame, R, W));
		}
		return ret;
	}
	
	/**
	 * Returns the time series of wdRMSD (weighted dRMSD) in a trajectory with regards to a reference structure
	 * @param t trajectory
	 * @param R reference structure
	 * @param W weight matrix
	 * @return time series of wdRMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesWDRMSD(Trajectory t, Structure R, Matrix W){
		return getSimilarityTimeSeriesWDRMSD(t,R.getAllAtomCoordinates(),W);
	}
	
	/**
	 * Returns the time series of wdRMSD (weighted dRMSD) in a trajectory with regards to a reference structure
	 * taking into account only a subset of atoms
	 * @param t trajectory
	 * @param indicesT index set of atoms in the trajectory
	 * @param R reference structure
	 * @param indicesR index set of atoms in the reference structure
	 * @param W weight matrix
	 * @return time series of wdRMSD
	 */
	public static ArrayList<Double> getSimilarityTimeSeriesWDRMSD(Trajectory t, IndexSet indicesT, Structure R, IndexSet indicesR, Matrix W){
		ArrayList<Double> ret = new ArrayList<Double>();
		int dim = t.getNumberOfFrames();
		for (int i = 0; i < dim; i++) {
			Structure frame = t.getFrameAsStructure(i);
			ret.add(getWDRMSD(frame, indicesT,  R, indicesR, W));
		}
		return ret;
	}
	
	// Difference distance matrix
	
	/**
	 * Calculates the difference distance matrix between two point lists
	 * @param points1 first point list
	 * @param points2 second point list
	 * @return difference distance matrix
	 * 
	 */
	public static Matrix getDifferenceDistanceMatrix(PointList points1, PointList points2){
		Matrix d1 = Distances.getDistanceMatrix(points1);
		Matrix d2 = Distances.getDistanceMatrix(points2);
		return d1.minus(d2);
	}
	
	/**
	 * Calculates the atomic difference distance matrix between two structures
	 * @param s1 first structure
	 * @param s2 second structure
	 * @return difference distance matrix
	 * 
	 */
	public static Matrix getDifferenceDistanceMatrix(Structure s1, Structure s2){
		Matrix d1 = Distances.getAtomicDistanceMatrix(s1);
		Matrix d2 = Distances.getAtomicDistanceMatrix(s2);
		return d1.minus(d2);
	}
	
	/**
	 * Calculates the atomic difference distance matrix between two sets of atoms defined by two index sets in two structures
	 * @param s1 first structure
	 * @param indices1 first index set
	 * @param s2 second structure
	 * @param indices2 second index set
	 * @return difference distance matrix
	 * 
	 */
	public static Matrix getDifferenceDistanceMatrix(Structure s1, IndexSet indices1, Structure s2, IndexSet indices2){
		Matrix d1 = Distances.getAtomicDistanceMatrix(s1, indices1);
		Matrix d2 = Distances.getAtomicDistanceMatrix(s2, indices2);
		return d1.minus(d2);
	}
	
	// Find similar frames
	
	/**
	 * Returns the set of frames in a trajectory that are more similar to a reference frame (based on the RMSD similarity measure) than a cutoff value
	 * @param t trajectory
	 * @param reference reference frame
	 * @param cutoff similarity cutoff
	 * @return frame index set
	 */
	public static FrameIndexSet findSimilarFramesRMSD(Trajectory t, PointList reference, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			PointList frame = t.getFrameAsPointList(i);
			if (Similarity.getRMSD(frame, reference)<=cutoff) ret.addFrame(i);
		}
		ret.setName("Similar[RMSD:"+cutoff+"]"+"(to:Noname)");
		return ret;
	}
	
	/**
	 * Returns the list of frames in a trajectory that are more similar to a reference frame (based on the dRMSD similarity measure) than a cutoff value
	 * @param t trajectory
	 * @param reference reference frame
	 * @param cutoff similarity cutoff
	 * @return frame list
	 */
	public static FrameIndexSet findSimilarFramesDRMSD(Trajectory t, PointList reference, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			PointList frame = t.getFrameAsPointList(i);
			if (Similarity.getDRMSD(frame, reference)<=cutoff) ret.addFrame(i);
		}
		ret.setName("Similar[dRMSD:"+cutoff+"]"+"(to:Noname)");
		return ret;
	}
	
	/**
	 * Returns the list of frames in a trajectory that are more similar to a reference distance matrix (based on the dRMSD similarity measure) than a cutoff value
	 * @param t trajectory
	 * @param reference reference distance matrix
	 * @param cutoff similarity cutoff
	 * @return frame list
	 */
	public static FrameIndexSet findSimilarFramesDRMSD(Trajectory t, Matrix reference, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			PointList frame = t.getFrameAsPointList(i);
			if (Similarity.getDRMSD(Distances.getDistanceMatrix(frame), reference)<=cutoff) ret.addFrame(i);
		}
		ret.setName("Similar[dRMSD:"+cutoff+"]"+"(to:Noname)");
		return ret;
	}
	
	// Medoids
	
	/**
	 * Calculates the medoid frame of a trajectory using the RMSD measure
	 * @param t trajectory
	 * @return medoid frame
	 */
	public static PointList getMedoidRMSD(Trajectory t){
		Matrix S = getSimilarityMatrixRMSD(t);
		int dim = S.getRowDimension();
		int best = 0;
		double min = 99999;
		for (int i = 0; i < dim; i++) {
			double sum = 0;
			for (int j = 0; j < dim; j++) sum+=S.get(i, j);
			if (sum<min) {
				min = sum;
				best = i;
			}
		}
		return t.getFrameAsPointList(best);
	}
	
	/**
	 * Calculates the medoid frame of a trajectory using the dRMSD measure
	 * @param t trajectory
	 * @return medoid frame
	 */
	public static PointList getMedoidDRMSD(Trajectory t){
		Matrix S = getSimilarityMatrixDRMSD(t);
		int dim = S.getRowDimension();
		int best = 0;
		double min = 99999;
		for (int i = 0; i < dim; i++) {
			double sum = 0;
			for (int j = 0; j < dim; j++) sum+=S.get(i, j);
			if (sum<min) {
				min = sum;
				best = i;
			}
		}
		return t.getFrameAsPointList(best);
	}
			
	
}
