/**
 * 
 * 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 jama.Matrix;
import jama.SingularValueDecomposition;

import java.util.ArrayList;

import jgromacs.data.Structure;


/**
 * Objects of this class represent a Gaussian Network Model (GNM) of a protein
 *
 */
public class GNM {

	private Matrix cM = new Matrix(1,1,0);
	private Matrix KM = new Matrix(1,1,0);
	private Matrix U = new Matrix(1,1,0);
	private Matrix Lambda = new Matrix(1,1,0);
	
	/**
	 * Constructs a new Gaussian Network Model
	 * @param s structure to be modelled
	 * @param cutoff distance cutoff
	 * @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)
	 */
	public GNM(Structure s, double cutoff, int distanceBetween){
		cM = Distances.getContactMatrix(s, distanceBetween, cutoff);
		KM = calculateKirchhoffMatrix(cM);
		SingularValueDecomposition svd = new SingularValueDecomposition(KM);
		U = svd.getU();
		Lambda = svd.getS();
	}
	
	/**
	 * Returns the contact matrix
	 * @return contact matrix
	 */
	public Matrix getContactMatrix(){
		return cM;
	}
	
	/**
	 * Returns the Kirchhoff matrix
	 * @return Kirchhoff matrix
	 */
	public Matrix getKirchhoffMatrix(){
		return KM;
	}
	
	/**
	 * Returns the diagonal matrix of eigenvalues (Lambda)
	 * @return Lambda matrix
	 */
	public Matrix getLambdaMatrix(){
		return Lambda;
	}
	
	/**
	 * Returns the orthogonal matrix of eigenvectors (U)
	 * @return U matrix
	 */
	public Matrix getEigenvectorMatrix(){
		return U;
	}
	
	/**
	 * Calculates the mean square fluctuation (MSF) profile 
	 * @return MSF profile 
	 */
	public ArrayList<Double> getMSFProfile(){
		ArrayList<Double> ret = new ArrayList<Double>();
		int resnum = Lambda.getRowDimension();
		for (int i = 0; i < resnum; i++) {
			double msf = 0;
			for (int q = 0; q < resnum-1; q++)
			msf+=Math.pow(U.get(i, q),2)/Lambda.get(q, q);
			ret.add(msf);
		}
		return ret;
	}
	
	private static Matrix calculateKirchhoffMatrix(Matrix cM){
		Matrix ret = cM.times(-1);
		int resnum = ret.getRowDimension();
		for (int i = 0; i < resnum; i++) {
			double sum = 0;
			for (int j = 0; j < resnum; j++) 
				if (j!=i) sum+=ret.get(i, j);
			ret.set(i, i, -sum);
		}
		return ret;
	}
	
}
