/**
 * 
 * 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.Atom;
import jgromacs.data.FrameIndexSet;
import jgromacs.data.IndexSet;
import jgromacs.data.Point3D;
import jgromacs.data.PointList;
import jgromacs.data.Residue;
import jgromacs.data.Structure;
import jgromacs.data.Trajectory;
import jama.Matrix;


/**
 * Collection of methods for analysing distances
 *
 */
public class Distances {

	public static final int ALPHACARBON = 1;
	public static final int CLOSEST = 2;
	public static final int CLOSESTHEAVY = 3;
	
	// Distance matrices
	
	/**
	 * Calculates the distance matrix from a point list
	 * @param points point list
	 * @return distance matrix
	 */
	public static Matrix getDistanceMatrix(PointList points){
		Matrix ret = new Matrix(points.getNumberOfPoints(),points.getNumberOfPoints(),0);
		for (int i = 0; i < points.getNumberOfPoints(); i++) {
			for (int j = i+1; j < points.getNumberOfPoints(); j++) {
				ret.set(i, j, points.getPoint(i).distance(points.getPoint(j)));
				ret.set(j, i, ret.get(i, j));
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the distance matrix of all atoms in a structure
	 * @param s structure
	 * @return distance matrix
	 */
	public static Matrix getAtomicDistanceMatrix(Structure s){
		PointList points = s.getAllAtomCoordinates();
		return getDistanceMatrix(points);
	}
	
	/**
	 * Calculates the distance matrix of atoms of given indices in a structure
	 * @param s structure
	 * @param indices index set
	 * @return distance matrix
	 */
	public static Matrix getAtomicDistanceMatrix(Structure s, IndexSet indices){
		int dim = indices.getNumberOfIndices();
		Matrix ret = new Matrix(dim,dim,0);
		ArrayList<Integer> indexList = indices.getAsArrayList();
		for (int i = 0; i < dim; i++) {
			Atom atomI = s.getAtomByIndex(indexList.get(i));
			for (int j = i+1; j < dim; j++) {
				Atom atomJ = s.getAtomByIndex(indexList.get(j));
				ret.set(i, j, atomI.distance(atomJ));
				ret.set(j, i, ret.get(i, j));
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the distance matrix between two sets of atoms
	 * @param s structure
	 * @param indices1 index set 1
	 * @param indices2 index set 2
	 * @return distance matrix
	 */
	public static Matrix getAtomicDistanceMatrix(Structure s, IndexSet indices1, IndexSet indices2){
		int dim1 = indices1.getNumberOfIndices();
		int dim2 = indices2.getNumberOfIndices();
		Matrix ret = new Matrix(dim1,dim2,0);
		ArrayList<Integer> indexList1 = indices1.getAsArrayList();
		ArrayList<Integer> indexList2 = indices2.getAsArrayList();
		for (int i = 0; i < dim1; i++) {
			Atom atomI = s.getAtomByIndex(indexList1.get(i));
			for (int j = 0; j < dim2; j++) {
				Atom atomJ = s.getAtomByIndex(indexList2.get(j));
				ret.set(i, j, atomI.distance(atomJ));
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the distance matrix of residues in a structure by the given method
	 * @param s structure
	 * @param distanceBetween which atoms are used for calculating the distances (ALPHACARBON: alpha carbon atoms,
	 * CLOSEST: closest atoms of two residues, CLOSESTHEAVY: closest heavy atoms of two residues)
	 * @return distance matrix
	 */
	public static Matrix getResidueDistanceMatrix(Structure s, int distanceBetween){
		int dim = s.getNumberOfResidues();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < dim; i++) {
			Residue resI = s.getResidue(i);
			for (int j = i+1; j < dim; j++) {
				Residue resJ = s.getResidue(j);
				double dist = 0;
				if (distanceBetween==ALPHACARBON) dist = resI.distanceAlphaCarbons(resJ);
				if (distanceBetween==CLOSEST) dist = resI.distanceClosest(resJ);
				if (distanceBetween==CLOSESTHEAVY) dist = resI.distanceClosestHeavy(resJ);
				ret.set(i, j, dist);
				ret.set(j, i, dist);
			}
		}
	  return ret;
	}

	/**
	 * Calculates the mean distance matrix of all atoms in a trajectory
	 * @param t trajecotry
	 * @return mean distance matrix
	 */
	public static Matrix getMeanAtomicDistanceMatrix(Trajectory t){
		int dim = t.getNumberOfAtoms();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			PointList frame = t.getFrameAsPointList(i);
			Matrix Di = getDistanceMatrix(frame);
			ret = ret.plus(Di);
		}
		return ret.times(1.0/t.getNumberOfFrames());
	}
	
	/**
	 * Calculates the mean distance matrix of atoms of given indices in a trajectory
	 * @param t trajecotry
	 * @param indices index set
	 * @return mean distance matrix
	 */
	public static Matrix getMeanAtomicDistanceMatrix(Trajectory t, IndexSet indices){
		ArrayList<Integer> indexList = t.getFrameAsStructure(0).convertIndicesToArrayListIndices(indices);
		int dim = indexList.size();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			PointList frame = t.getFrameAsPointList(i).getSubList(indexList);
			Matrix Di = getDistanceMatrix(frame);
			ret = ret.plus(Di);
		}
		return ret.times(1.0/t.getNumberOfFrames());
	}
	
	/**
	 * Calculates the mean distance matrix of residues in a trajectory by the given method
	 * @param t trajectory
	 * @param distanceBetween which atoms are used for calculating the distances (ALPHACARBON: alpha carbon atoms,
	 * CLOSEST: closest atoms of two residues, CLOSESTHEAVY: closest heavy atoms of two residues)
	 * @return mean distance matrix
	 */
	public static Matrix getMeanResidueDistanceMatrix(Trajectory t, int distanceBetween){
		int dim = t.getNumberOfResidues();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			Matrix Di = getResidueDistanceMatrix(frame, distanceBetween);
			ret = ret.plus(Di);
		}
		return ret.times(1.0/t.getNumberOfFrames());
	}
	
	// Contact matrices
	
	/**
	 * Calculates the contact matrix from a point list
	 * @param points point list
	 * @param cutoff distance cutoff
	 * @return contact matrix
	 */
	public static Matrix getContactMatrix(PointList points, double cutoff){
		Matrix distM = getDistanceMatrix(points);
		Matrix ret = new Matrix(distM.getRowDimension(),distM.getColumnDimension(),0);
		for (int i = 0; i < distM.getRowDimension(); i++) {
			for (int j = i; j < distM.getColumnDimension(); j++) {
				if (distM.get(i, j)<=cutoff) ret.set(i, j, 1);
				ret.set(j, i, ret.get(i, j));
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the contact matrix of residues in a structure by the given method
	 * @param s structure
	 * @param distanceBetween which atoms are used for calculating the distances (ALPHACARBON: alpha carbon atoms,
	 * CLOSEST: closest atoms of two residues, CLOSESTHEAVY: closest heavy atoms of two residues)
	 * @param cutoff distance cutoff
	 * @return contact matrix
	 */
	public static Matrix getContactMatrix(Structure s, int distanceBetween, double cutoff){
		Matrix distM = getResidueDistanceMatrix(s,distanceBetween);
		Matrix ret = new Matrix(distM.getRowDimension(),distM.getColumnDimension(),0);
		for (int i = 0; i < distM.getRowDimension(); i++) {
			for (int j = i; j < distM.getColumnDimension(); j++) {
				if (distM.get(i, j)<=cutoff) ret.set(i, j, 1);
				ret.set(j, i, ret.get(i, j));
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the contact matrix based on the mean residue distance matrix in a trajectory 
	 * @param t trajectory
	 * @param distanceBetween which atoms are used for calculating the distances (ALPHACARBON: alpha carbon atoms,
	 * CLOSEST: closest atoms of two residues, CLOSESTHEAVY: closest heavy atoms of two residues)
	 * @param cutoff distance cutoff
	 * @return contact matrix
	 */
	public static Matrix getContactOfMeanMatrix(Trajectory t, int distanceBetween, double cutoff){
		Matrix distM = getMeanResidueDistanceMatrix(t, distanceBetween);
		Matrix ret = new Matrix(distM.getRowDimension(),distM.getColumnDimension(),0);
		for (int i = 0; i < distM.getRowDimension(); i++) {
			for (int j = i; j < distM.getColumnDimension(); j++) {
				if (distM.get(i, j)<=cutoff) ret.set(i, j, 1);
				ret.set(j, i, ret.get(i, j));
			}
		}
		return ret;
	}
	
	/**
	 * Calculates the contact matrix of residues where two residues are said to be in contact if they are in contact
	 * in at least the given percentage of frames of the trajectory 
	 * @param t trajectory
	 * @param distanceBetween which atoms are used for calculating the distances (ALPHACARBON: alpha carbon atoms,
	 * CLOSEST: closest atoms of two residues, CLOSESTHEAVY: closest heavy atoms of two residues)
	 * @param cutoff distance cutoff
	 * @param frequency minimal frequency of frames in which two residues are in contact
	 * @return contact matrix
	 */
	public static Matrix getFrequencyContactMatrix(Trajectory t, int distanceBetween, double cutoff, double frequency){
		int dim = t.getNumberOfResidues();
		Matrix ret = new Matrix(dim,dim,0);
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			Matrix Ci = getContactMatrix(frame, distanceBetween, cutoff);
			ret = ret.plus(Ci);
		}
		ret = ret.times(1.0/t.getNumberOfFrames());
		for (int i = 0; i < dim; i++) {
			for (int j = i+1; j < dim; j++) {
				if (ret.get(i, j)>=frequency) ret.set(i, j, 1);
				else ret.set(i, j, 0);
				ret.set(j, i, ret.get(i, j));
			}
		}
		return ret;
	}
		
	// Distance distributions / time series
	/**
	 * Returns the time series of the distance of two atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return time series of distance
	 */
	public static ArrayList<Double> getDistanceTimeSeries(Trajectory t, int atomindex1, int atomindex2){
		ArrayList<Double> ret = new ArrayList<Double>();
		IndexSet indices = new IndexSet();
		indices.addIndex(atomindex1);
		indices.addIndex(atomindex2);
		ArrayList<Integer> listindices = t.getFrameAsStructure(0).convertIndicesToArrayListIndices(indices);
		int listIndex1 = listindices.get(0);
		int listIndex2;
		if (listindices.size()>1) listIndex2 = listindices.get(1);
		else listIndex2=listIndex1;
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Point3D point1 = t.getFrameAsPointList(i).getPoint(listIndex1);
			Point3D point2 = t.getFrameAsPointList(i).getPoint(listIndex2);
			double d = point1.distance(point2);
			ret.add(d);
		}
		return ret;
	}
	
	/**
	 * Returns the time series of the distance of a single atom and a set of atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex index of atom
	 * @param referenceset index set of reference atoms
	 * @return time series of distance
	 */
	public static ArrayList<Double> getDistanceTimeSeries(Trajectory t, int atomindex, IndexSet referenceset){
		ArrayList<Double> ret = new ArrayList<Double>();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			double d = Distances.getDistanceOfAtomToAtomSet(t.getFrameAsStructure(i), atomindex, referenceset);
			ret.add(d);
		}
		return ret;
	}
	
	/**
	 * Returns the time series of the distance of two sets of atoms in a trajectory
	 * @param t trajectory
	 * @param indices1 first index set
	 * @param indices2 second index set
	 * @return time series of distance
	 */
	public static ArrayList<Double> getDistanceTimeSeries(Trajectory t,  IndexSet indices1, IndexSet indices2){
		ArrayList<Double> ret = new ArrayList<Double>();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			double d = Distances.getDistanceOfTwoAtomSets(t.getFrameAsStructure(i), indices1, indices2);
			ret.add(d);
		}
		return ret;
	}
	
	/**
	 * Returns the mean of the distance of two atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return mean of distance
	 */
	public static double  getMeanDistance(Trajectory t, int atomindex1, int atomindex2){
		double ret = 0;
		ArrayList<Double> distr = getDistanceTimeSeries(t, atomindex1, atomindex2);
		for (int i = 0; i < distr.size(); i++) ret+=distr.get(i);
		return ret/distr.size();
	}
	
	/**
	 * Returns the variance of the distance of two atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return variance of distance
	 */
	public static double getVarianceOfDistance(Trajectory t, int atomindex1, int atomindex2){
		double mean = 0;
		ArrayList<Double> distr = getDistanceTimeSeries(t, atomindex1, atomindex2);
		for (int i = 0; i < distr.size(); i++) mean+=distr.get(i);
		mean = mean/distr.size();
		double ret = 0;
		for (int i = 0; i < distr.size(); i++) ret+=Math.pow(distr.get(i)-mean,2);
		return ret/distr.size();
	}
	
	/**
	 * Returns the minimal distance of two atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return minimal distance
	 */
	public static double  getMinimalDistance(Trajectory t, int atomindex1, int atomindex2){
		double ret = 999999;
		ArrayList<Double> distr = getDistanceTimeSeries(t, atomindex1, atomindex2);
		for (int i = 0; i < distr.size(); i++) {
			double value = distr.get(i);
			if (value<ret) ret=value;
		}
		return ret;
	}
	
	/**
	 * Returns the maximal distance of two atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return maximal distance
	 */
	public static double getMaximalDistance(Trajectory t, int atomindex1, int atomindex2){
		double ret = -999999;
		ArrayList<Double> distr = getDistanceTimeSeries(t, atomindex1, atomindex2);
		for (int i = 0; i < distr.size(); i++) {
			double value = distr.get(i);
			if (value>ret) ret=value;
		}
		return ret;
	}
	
	/**
	 * Returns the range (max-min) of the distance of two atoms in a trajectory
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return size of distance interval
	 */
	public static double getDistanceRange(Trajectory t, int atomindex1, int atomindex2){
		double min = 999999;
		double max = -999999;
		ArrayList<Double> distr = getDistanceTimeSeries(t, atomindex1, atomindex2);
		for (int i = 0; i < distr.size(); i++) {
			double value = distr.get(i);
			if (value>max) max=value;
			if (value<min) min=value;
		}
		return max-min;
	}
	
	// Index sets defined by distances
	
	/**
	 * Returns the index set of atoms in a structure that are closer to a reference point than a given radius
	 * @param s structure
	 * @param point reference point
	 * @param radius distance radius
	 * @return atom indices
	 */
	public static IndexSet getAtomIndicesInRadius(Structure s, Point3D point, double radius){
		IndexSet ret = new IndexSet();
		for (int i = 0; i < s.getNumberOfResidues(); i++) {
			Residue res = s.getResidue(i);
			for (int j = 0; j < res.getNumberOfAtoms(); j++) {
				Atom atom = res.getAtom(j);
				if (atom.getCoordinates().distance(point)<=radius) 
					ret.addIndex(atom.getIndex()); 
			}
		}
		return ret;
	}
	
	/**
	 * Returns the index set of atoms in a structure that are closer to a reference atom than a given radius
	 * @param s structure
	 * @param atom reference atom
	 * @param radius distance radius
	 * @return atom indices
	 */
	public static IndexSet getAtomIndicesInRadius(Structure s, Atom atom, double radius){
		return getAtomIndicesInRadius(s,atom.getCoordinates(),radius);
	}

	/**
	 * Returns the index set of atoms in a structure that are closer to a reference set of atoms than a given radius
	 * @param s structure
	 * @param referenceindices index set of reference atoms
	 * @param radius distance radius
	 * @return atom indices
	 */
	public static IndexSet getAtomIndicesInRadius(Structure s, IndexSet referenceindices, double radius){
		IndexSet ret = new IndexSet();
		for (int i = 0; i < s.getNumberOfResidues(); i++) {
			Residue res = s.getResidue(i);
			for (int j = 0; j < res.getNumberOfAtoms(); j++) {
				Atom atom = res.getAtom(j);		
				if (isInProximity(s, atom, referenceindices, radius)) ret.addIndex(atom.getIndex());
			}
		}
		return ret;
	}
	
	/**
	 * Returns the index set of atoms from a set of atoms that are closer to a reference set than a given radius
	 * @param s structure
	 * @param atomIndices index set of atoms tested
	 * @param referenceIndices index set of reference atoms
	 * @param radius distance radius
	 * @return atom indices
	 */
	public static IndexSet getAtomIndicesInRadius(Structure s, IndexSet atomIndices, IndexSet referenceIndices, double radius){
		IndexSet ret = new IndexSet();
		ArrayList<Integer> atoms = atomIndices.getAsArrayList();
			for (int j = 0; j < atoms.size(); j++) {
				Atom atom = s.getAtomByIndex(atoms.get(j));		
				if (isInProximity(s, atom, referenceIndices, radius)) ret.addIndex(atom.getIndex());
			}
		return ret;
	}
	
	private static boolean isInProximity(Structure s, Atom atom, IndexSet referenceindices, double radius){
		ArrayList<Integer> indexList = s.convertIndicesToArrayListIndices(referenceindices);
		for (int i = 0; i < indexList.size(); i++) {
			int index = indexList.get(i);
			if (s.getAtom(index).distance(atom)<=radius) return true;
		}
		return false;
	}
	
	// Misc
	
	/**
	 * Returns the frame in which two atoms are closest to each other in a simulation
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return frame of trajectory
	 */
	public static PointList findFrameWhereClosest(Trajectory t, int atomindex1, int atomindex2){
		int best = 0;
		double min = 99999;
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			double dist = frame.getAtomByIndex(atomindex1).distance(frame.getAtomByIndex(atomindex2));
			if (dist<min){
				min = dist;
				best = i;
			}
		}
		return t.getFrameAsPointList(best);
	}
	
	/**
	 * Returns the frame in which two atoms are most distant from each other in a simulation
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @return frame of trajectory
	 */
	public static PointList findFrameWhereMostDistant(Trajectory t, int atomindex1, int atomindex2){
		int best = 0;
		double max = -99999;
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			double dist = frame.getAtomByIndex(atomindex1).distance(frame.getAtomByIndex(atomindex2));
			if (dist>max){
				max = dist;
				best = i;
			}
		}
		return t.getFrameAsPointList(best);
	}
	
	/**
	 * Calculates the distance of an atom to a reference set of atoms (i.e. the minimum of all pairwise distances)
	 * @param s structure
	 * @param atomindex index of atom
	 * @param referenceset index set of reference atoms
	 * @return minimal distance
	 */
	public static double getDistanceOfAtomToAtomSet(Structure s, int atomindex, IndexSet referenceset){
		double ret = 999999;
		Atom atom = s.getAtomByIndex(atomindex);
		ArrayList<Integer> indexList = referenceset.getAsArrayList();
		for (int i = 0; i < indexList.size(); i++) {
			Atom refatom = s.getAtomByIndex(indexList.get(i));
			double dist = atom.distance(refatom);
			if (dist<ret) ret = dist;
			
		}
		return ret;
	}
	
	/**
	 * Calculates the distance between two sets of atoms (i.e. the minimum of all pairwise distances)
	 * @param s structure
	 * @param indices1 first index set
	 * @param indices2 second index set
	 * @return minimal distance
	 */
	public static double getDistanceOfTwoAtomSets(Structure s, IndexSet indices1, IndexSet indices2){
		double ret = 999999;
		ArrayList<Integer> indexList1 = indices1.getAsArrayList();
		ArrayList<Integer> indexList2 = indices2.getAsArrayList();
		for (int i = 0; i < indexList1.size(); i++) {
			Atom atom1 = s.getAtomByIndex(indexList1.get(i));
			for (int j = 0; j < indexList2.size(); j++) {
				Atom atom2 = s.getAtomByIndex(indexList2.get(j));
				double dist = atom1.distance(atom2);
				if (dist<ret) ret = dist;
			}
		}
		return ret;
	}
	
	/**
	 * Returns the atom from a given atom set that is closest to a reference set of atoms
	 * @param s structure
	 * @param atomset index set of atoms
	 * @param referenceset index set of reference atoms
	 * @return closest atom
	 */
	public static Atom findClosestAtom(Structure s, IndexSet atomset, IndexSet referenceset){
		int bestindex = 0;
		double min = 99999;
		ArrayList<Integer> indexList = atomset.getAsArrayList();
		for (int i = 0; i < indexList.size(); i++) {
			double dist = getDistanceOfAtomToAtomSet(s, indexList.get(i), referenceset);
			if (dist<min){
				min = dist;
				bestindex = indexList.get(i);
			}
		}
		return s.getAtomByIndex(bestindex);
	}
	
	// Frame lists defined by distances
	
	
	/**
	 * Returns the list of frames in a trajectory where two atoms are closer to each other than the given cutoff
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @param cutoff distance cutoff
	 * @return frame list
	 */
	public static FrameIndexSet getFramesWhereAtomsAreClose(Trajectory t, int atomindex1, int atomindex2, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			if (frame.getAtomByIndex(atomindex1).distance(frame.getAtomByIndex(atomindex2))<=cutoff) ret.addFrame(i);
		}
		String name1 = atomindex1+t.getFirstFrameAsStructure().getAtomByIndex(atomindex1).getName();
		String name2 = atomindex2+t.getFirstFrameAsStructure().getAtomByIndex(atomindex2).getName();
		ret.setName("Close["+cutoff+"]["+name1+","+name2+"]");
		return ret;
	}
	
	/**
	 * Returns the list of frames in a trajectory where two atoms are more distant from each other than the given cutoff
	 * @param t trajectory
	 * @param atomindex1 index of first atom
	 * @param atomindex2 index of second atom
	 * @param cutoff distance cutoff
	 * @return frame list
	 */
	public static FrameIndexSet getFramesWhereAtomsAreDistant(Trajectory t, int atomindex1, int atomindex2, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			if (frame.getAtomByIndex(atomindex1).distance(frame.getAtomByIndex(atomindex2))>=cutoff) ret.addFrame(i);
		}
		String name1 = atomindex1+t.getFirstFrameAsStructure().getAtomByIndex(atomindex1).getName();
		String name2 = atomindex2+t.getFirstFrameAsStructure().getAtomByIndex(atomindex2).getName();
		ret.setName("Distant["+cutoff+"]["+name1+","+name2+"]");
		return ret;
	}
	
	/**
	 * Returns the list of frames in the trajectory where an atoms is closer to a reference point than a given cutoff
	 * @param t trajectory
	 * @param atomindex index of atom
	 * @param point reference point
	 * @param cutoff distance cutoff
	 * @return frame list
	 */
	public static FrameIndexSet getFramesWhereAtomIsCloseToPoint(Trajectory t, int atomindex, Point3D point, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			if (frame.getAtomByIndex(atomindex).getCoordinates().distance(point)<=cutoff) ret.addFrame(i);
		}
		String name = atomindex+t.getFirstFrameAsStructure().getAtomByIndex(atomindex).getName();
		ret.setName("Close["+cutoff+"]["+name+","+point+"]");
		return ret;
	}
	
	/**
	 * Returns the list of frames in the trajectory where an atoms is more distant from a reference point than a given cutoff
	 * @param t trajectory
	 * @param atomindex index of atom
	 * @param point reference point
	 * @param cutoff distance cutoff
	 * @return frame list
	 */
	public static FrameIndexSet getFramesWhereAtomIsDistantFromPoint(Trajectory t, int atomindex, Point3D point, double cutoff){
		FrameIndexSet ret = new FrameIndexSet();
		for (int i = 0; i < t.getNumberOfFrames(); i++) {
			Structure frame = t.getFrameAsStructure(i);
			if (frame.getAtomByIndex(atomindex).getCoordinates().distance(point)>=cutoff) ret.addFrame(i);
		}
		String name = atomindex+t.getFirstFrameAsStructure().getAtomByIndex(atomindex).getName();
		ret.setName("Distant["+cutoff+"]["+name+","+point+"]");
		return ret;
	}
	
	
}
