content: /*
   * Copyright (C) 2010-2014 Mathias Unberath CONRAD is developed as an Open
   * Source project under the GNU General Public License (GPL).
   */
  package edu.stanford.rsl.conrad.io;
  
  import java.io.BufferedReader;
  import java.io.FileNotFoundException;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.UnsupportedEncodingException;
  import java.util.StringTokenizer;
  
  import edu.stanford.rsl.conrad.geometry.shapes.activeshapemodels.KPCA;
  import edu.stanford.rsl.conrad.geometry.shapes.activeshapemodels.kernels.GaussianKernel;
  import edu.stanford.rsl.conrad.geometry.shapes.activeshapemodels.kernels.PolynomialKernel;
  import edu.stanford.rsl.conrad.geometry.shapes.mesh.DataMatrix;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  
  /**
   * Class to read and write Eigenvectors, Eigenvalues and the training-sets as
   * used in kernel principal component analysis.
   * 
   * @author Mathias Unberath
   * 
   */
  public class KpcaIO {
  
  	/**
  	 * Dimension of vertices in a mesh object. Default is 1;
  	 */
  	private int pointDimension = 1;
  
  	/**
  	 * The filename for input or output operation.
  	 */
  	String filename;
  
  	/**
  	 * Array containing the eigenvalues of the covariance matrix after singular
  	 * value decomposition.
  	 */
  	private double[] eigenValues;
  
  	/**
  	 * Matrix containing the Eigenvectors of the covariance matrix after
  	 * singular value decomposition.
  	 */
  	private SimpleMatrix eigenVectors;
  
  	/**
  	 * Training-sets.
  	 */
  	private SimpleMatrix trainingSets;
  
  	/**
  	 * Feature Matrix K.
  	 */
  	private SimpleMatrix featureMatrix;
  
  	/**
  	 * Consensus object needed to project new shapes.
  	 */
  	private SimpleVector consensus;
  	
  	/**
  	 * The name of the kernel.
  	 */
  	private String kernel;
  	
  	/**
  	 * The variation threshold used to reduce dimensionality.
  	 */
  	private double variation;
  
  	// ==========================================================================================
  	// METHODS
  	// ==========================================================================================
  
  	/**
  	 * Constructor for KPCA writing operation. 
  	 * @param filename
  	 * @param kpca
  	 */
  	public KpcaIO(String filename, KPCA kpca) {
  		this.filename = filename;
  		this.pointDimension = kpca.dimension;
  		this.eigenValues = kpca.eigenValues;
  		this.eigenVectors = kpca.eigenVectors; // the eigenvectors are commonly referred to as alpha
  		this.trainingSets = kpca.data;
  		this.featureMatrix = kpca.getFeatureMatrix();
  		this.consensus = toSimpleVector(kpca.data.consensus);
  		this.kernel = kpca.kernel.getName();
  		this.variation =  kpca.variationThreshold;
  	}
  
  	/**
  	 * Constructor for reading operation. 
  	 * @param filename The filename containing the Kpca Object as produced by this class
  	 */
  	public KpcaIO(String filename){
  		this.filename = filename;
  	}
  	
  	/**
  	 * Writes the previously set KPCA object to the specified file.
  	 */
  	public void writeFile(){
  		assert( filename != null) : new Exception("Filename not set.");
  		assert( consensus != null && eigenVectors != null && eigenValues != null && trainingSets != null && featureMatrix != null && kernel != null) 
  			: new Exception("Data not set.");
  		
  		System.out.println("Writing to file: " + filename);
  		
  		try{
  			System.out.println("Writing Eigen-Values and Eigen-Vectors to file: " + filename);
  			PrintWriter writer = new PrintWriter(filename,"UTF-8");
  			
  			writer.println("Number of datasets: " + Integer.valueOf(trainingSets.getCols()));
  			writer.println("Number of points: " + Integer.valueOf(trainingSets.getRows()/pointDimension));
  			writer.println("Point dimension: " + Integer.valueOf(pointDimension));
  			writer.println("Number of P.C.s: " + Integer.valueOf(eigenValues.length));
  			writer.println("Variation threshold: " + Double.valueOf(variation));
  			writer.println("Kernel: " + kernel);
  			
  			writer.println("");
  			writer.println("EIGENVALUES of K_ij");
  			String eval = "";
  			for(int i = 0; i < eigenValues.length; i++){
  				eval += (Double.valueOf(eigenValues[i]) + " ");
  			}
  			writer.println(eval);
  			
  			writer.println("EIGENVECTORS of K_ij");
  			for(int i = 0; i < trainingSets.getCols(); i++){
  				writer.println(rowAsString(eigenVectors, i));
  			}
  			
  			writer.println("FEATUREMATRIX K_ij");
  			for(int i = 0; i < trainingSets.getCols(); i++){
  				writer.println(rowAsString(featureMatrix, i));
  			}
  			
  			writer.println("TRAININGSETS | CONSENSUS");
  			for(int i = 0; i < trainingSets.getRows(); i++){
  				writer.println(rowAsString(trainingSets, i) + Double.valueOf(consensus.getElement(i)));
  			}
  			
  			writer.close();
  		} catch (FileNotFoundException e) {
  			e.printStackTrace();
  		} catch (UnsupportedEncodingException e) {
  			e.printStackTrace();
  		}
  	}
  	
  	/**
  	 * Constructs a KPCA object from the data given in the file referred to by filename.
  	 * @return KPCA object
  	 */
  	public KPCA readFile(){
  		assert(filename != null) : new Exception("Filename not set.");
  		
  		KPCA kpca= new KPCA();
  				
  		FileReader fr;
  		try {
  			fr = new FileReader(filename);
  			BufferedReader br = new BufferedReader(fr);
  			
  			String line = br.readLine();
  			StringTokenizer tok = new StringTokenizer(line);
  			tok.nextToken(); // skip "Number of Datasets"
  			tok.nextToken();
  			tok.nextToken();
  			String t = tok.nextToken();
  			kpca.numSamples = Integer.parseInt(t);
  			
  			line = br.readLine();
  			tok = new StringTokenizer(line);
  			tok.nextToken(); // skip "Number of points"
  			tok.nextToken();
  			tok.nextToken();
  			t = tok.nextToken();
  			kpca.numVertices = Integer.parseInt(t);
  			
  			line = br.readLine();
  			tok = new StringTokenizer(line);
  			tok.nextToken(); // skip "Point dimension"
  			tok.nextToken();
  			t = tok.nextToken();
  			kpca.dimension = Integer.parseInt(t);
  			kpca.numPoints = kpca.numVertices * kpca.dimension;
  			
  			line = br.readLine();
  			tok = new StringTokenizer(line);
  			tok.nextToken(); // skip "Number of Pcs"
  			tok.nextToken();
  			tok.nextToken();
  			t = tok.nextToken();
  			int numPC = Integer.parseInt(t);
  			
  			line = br.readLine();
  			tok = new StringTokenizer(line);
  			tok.nextToken(); // skip "Variation threshold"
  			tok.nextToken();
  			t = tok.nextToken();
  			kpca.variationThreshold = Double.parseDouble(t);
  			
  			line = br.readLine();
  			tok = new StringTokenizer(line);
  			tok.nextToken(); // skip "Kernel"
  			t = tok.nextToken();
  			String kernelName = t;
  			t = tok.nextToken();
  			double val = Double.parseDouble(t);
  			if(kernelName.equals("Gaussian")){
  				kpca.kernel = new GaussianKernel(val);
  			}else if(kernelName.equals("Polynomial")){
  				double alpha = Double.valueOf(tok.nextToken());
  				double offs = Double.valueOf(tok.nextToken());
  				kpca.kernel = new PolynomialKernel((int) val, alpha, offs);
  			}else{
  				System.out.println("Kernel method unknown. Using default: parabolic kernel.");
  				kpca.kernel = new PolynomialKernel();
  			}
  			// read the EIGENVALUES
  			br.readLine();
  			br.readLine(); // skip empty line, then skip "EIGENVALUES ..."
  			line = br.readLine();
  			tok = new StringTokenizer(line);
  			double[] ev = new double[numPC];
  			for(int i = 0; i < numPC; i++){
  				t = tok.nextToken();
  				ev[i] = Double.parseDouble(t);
  			}
  			kpca.eigenValues = ev;
  			
  			// read the EIGENVECTORS
  			br.readLine(); // skip "EIGENVECTORS ..."
  			SimpleMatrix evec = new SimpleMatrix(kpca.numSamples, numPC);
  			for(int i = 0; i < kpca.numSamples; i++){
  				line = br.readLine();
  				tok = new StringTokenizer(line);
  				for(int j = 0; j < numPC; j++){
  					t = tok.nextToken();
  					evec.setElementValue(i, j, Double.parseDouble(t));
  				}
  			}
  			kpca.eigenVectors = evec;
  			
  			// read the Feature Matrix
  			br.readLine(); // skip "FEATUREMATRIX ..."
  			SimpleMatrix feat = new SimpleMatrix(kpca.numSamples, kpca.numSamples);
  			for(int i = 0; i < kpca.numSamples; i++){
  				line = br.readLine();
  				tok = new StringTokenizer(line);
  				for(int j = 0; j < kpca.numSamples; j++){
  					t = tok.nextToken();
  					feat.setElementValue(i, j, Double.parseDouble(t));
  				}
  			}
  			kpca.setFeatureMatrix(feat);
  			
  			// read the trainingssets and consensus
  			br.readLine(); // skip "FEATUREMATRIX ..."
  			DataMatrix m = new DataMatrix();
  			m.init(kpca.numPoints, kpca.numSamples);
  			m.dimension = kpca.dimension;
  			
  			SimpleMatrix consensus = new SimpleMatrix(kpca.numVertices, kpca.dimension);
  			for(int i = 0; i < kpca.numPoints; i++){
  				int idxx = (int)Math.floor((float)i/kpca.dimension);
  				int idxy = i - idxx * kpca.dimension;
  				SimpleVector vec = new SimpleVector(kpca.numSamples);
  				line = br.readLine();
  				tok = new StringTokenizer(line);
  				for(int j = 0; j < kpca.numSamples; j++){
  					t = tok.nextToken();
  					if(j == kpca.numSamples-1){
  						consensus.setElementValue(idxx, idxy, Double.parseDouble(t));
  					}else{
  						vec.setElementValue(j, Double.parseDouble(t));
  					}
  				}
  			}
  			m.consensus = consensus;
  			kpca.data = m;
  			
  			br.close();
  			
  		} catch (FileNotFoundException e) {
  			e.printStackTrace();
  		} catch (IOException e) {
  			e.printStackTrace();
  		}
  		
  		
  		return kpca;
  	}
  	
  	
  	
  	
  	/**
  	 * Condenses the i_th row of a simple matrix into a string. 
  	 * @param m
  	 * @param row
  	 * @return i_th row as string
  	 */
  	private String rowAsString(SimpleMatrix m, int row){
  		String r = "";
  		for(int i = 0; i < m.getCols(); i++){
  			r += (Double.valueOf(m.getElement(row, i)) + " ");
  		}
  		return r;
  	}
  	
  	
  	
  	/**
  	 * Transforms a SimpleMatrix into a SimpleVector by appending each consecutive row to the former.
  	 * @param m The SimpleMatrix.
  	 * @return The SimpleMatrix as SimpleVector.
  	 */
  	private SimpleVector toSimpleVector(SimpleMatrix m){
  		SimpleVector v = new SimpleVector(m.getRows() * m.getCols());
  		for(int i = 0; i < m.getRows(); i++){
  			for(int j = 0; j < m.getCols(); j++){
  				v.setElementValue(i * m.getCols() + j, m.getElement(i, j));
  			}
  		}
  		return v;
  	}
  }
  
  