/*  
 *  src/org/metaqtl/ChromCluster.java
 * 
 *  $Author: veyrieras $, $Date: 2006-10-17 15:41:54 $, $Version$
 *
 *  MetaQTL : a Java package for QTL Meta-Analysis
 * 
 *  Copyright (C) 2003-2006  Jean-Baptiste Veyrieras, INRA, France.
 *
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * 
 */
package org.metaqtl;

import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;

import org.metaqtl.algo.CMSAlgorithm;
import org.metaqtl.bio.IBioCross;
import org.metaqtl.bio.IBioLGroup;
import org.metaqtl.bio.IBioLocus;
import org.metaqtl.numrec.NumericalUtilities;
import org.metaqtl.util.LocusProperties;
import org.metaqtl.util.MappingFunction;
/**
 * A cluster of chromosomes which share the same name inside a 
 * {@link MapCluster} framwork.
 * 
 * @author   Jean-Baptiste Veyrieras
 * @version  $Version$
 * @see      MapCluster
 */
public class ChromCluster {
	
	private int mrkThresh = 1;
	/**
	 * Total number of common marker between clusters
	 */ 
	public int nmc;
	/**
	 * The total number of marker intervals.
	 */
	public int ni;
	/**
	 * The name of the chromosome cluster. 
	 */
	public String name;
	/**
	 * Ths locus name index table
	 */
	public Hashtable locusNames;
	/**
	 *  The list of the cluster members.
	 */  
	public ArrayList clusters;
	/**
	 * The marker name index build from
	 * the locus name table.
	 */
	private String[] _nameIdx;
	/**
	 * An array of dubious markers. 
	 */
	private IBioLocus[] dubious;
	/**
	 * The locusDictionnary : not use at this time.
	 */
	private MetaDico mrkDico;
	
	/**
	 * Creates a new instance if ChromCluster.
	 */
	public ChromCluster() {
		clusters   = new ArrayList();
		locusNames = new Hashtable();
		dubious    = null;
		mrkDico = null;
		nmc = 0;
		ni  = 0;
	}
	/**
	 * Add a chromosome to the cluster
	 * @param group the chromosome to add.
	 * @param crossDesign the cross design properties.
	 * @param mappingFunction the mapping function for this chromosome. 
	 * @param mappingUnit the mapping unit for this chromosome
	 * @param rescale true if the chromosome must be rescaled @see Chromosome#R2r()
	 * @param skeleton true if the chromosome if a skeleton chromosome.
	 */
	public void addChromosome(IBioLGroup group, IBioCross crossDesign, int mappingFunction, int mappingUnit, boolean rescale, boolean skeleton) {
		int i,nm;
		double d,d1,d2,start,end;
		boolean status;
		String locusName;
		int[] oidx;			  /* index of marker over loci[] */
		int[] idx;			  /* index for Heap sort */
		double[] positions;	  /* the marker positions on the chromosome */
		double[] mr;		  /* the marker interval recombination rates */
		String[] names;		  /* the marker names */
		Integer locusCount;	  
		IBioLocus[] loci;	  /* the list of loci (marker + qtl) on the chromosome */
		Chromosome member; 	  /* the cluster member which will represent the chromsome */
		ArrayList qtls=null;  /* the QTL on the chromosome */
		
		loci = group.loci();
		
		oidx      = new int[loci.length];
		idx       = new int[loci.length+1];
		positions = new double[loci.length+1];
		mr        = new double[loci.length-1];
		names     = new String[loci.length];
		
		start = Double.POSITIVE_INFINITY;
		end   = Double.NEGATIVE_INFINITY;
		
		for(nm=i=0;i<loci.length;i++) {
			if (loci[i].getLocusType()==IBioLocus.MARKER) {
				if (loci[i].getPosition().absolute() < start)
				{
					start = loci[i].getPosition().absolute();
				}
				else if (loci[i].getPosition().absolute() > end)
				{
					end = loci[i].getPosition().absolute();
				}
			}
		}
		
		for(nm=i=0;i<loci.length;i++) {
			
			/* Get the status of this locus */
			status = (!LocusProperties.isDubiousOrderLocus(loci[i]) && !isDubiousLocus(loci[i]));
			
			if (status) {
				
				if (loci[i].getLocusType()==IBioLocus.MARKER) {
					
						/* At the same time push the marker names into the
						 * locus name cluster index. We also count the number
						 * of times the locus has been seen.
						 */
						positions[nm+1] = loci[i].getPosition().absolute();
						oidx[nm] 		= i; /* Index */
						locusName       = getMarkerName(loci[i].getName());
						
						if (!locusNames.containsKey(locusName)) {
						
							locusNames.put(locusName, new Integer(1));
						
						} else {
							
							locusCount = (Integer) locusNames.get(locusName);
							locusCount = new Integer(locusCount.intValue()+1);
							locusNames.put(locusName, locusCount);
							/* Increment the total number of common markers */
							this.nmc++;
							
						}
						
						nm++;
					
				} else if (loci[i].getLocusType()==IBioLocus.QTL) {
					if (loci[i].getPosition().absolute() < start)
					{
						continue;
					}
					else if (loci[i].getPosition().absolute() > end)
					{
						continue;
					}
					/* Add it to the qtl array list */
					if (qtls==null) qtls = new ArrayList();
					qtls.add(loci[i]);
					
				}
			
			}
			
		}
		/* (Heap) Sort the marker positions in order to be sure that the order
		 * which we will use is the correct order
		 */
		if (nm > 1)
		   NumericalUtilities.indexx(nm, positions, idx);
		names[0] = getMarkerName(loci[oidx[idx[1]-1]].getName());
		
		for(d=0.0,i=1;i<nm;i++) {
			d1 		= positions[idx[i]];
			d2 		= positions[idx[i+1]];
			/* Convert the distance into recombination rate */
			mr[i-1] = MappingFunction.recombination(d2-d1, mappingFunction, mappingUnit);
			d += MappingFunction.distance(mr[i-1], mappingFunction, mappingUnit);
			names[i]= getMarkerName(loci[oidx[idx[i+1]-1]].getName());
			/* Increment the cumulated number of marker intervals */
			if (!skeleton)
				this.ni++;			
		}
		
		member = new Chromosome();
		if (group.getParent() != null)
			member.mapName     = group.getParent().getName();
		member.name       = group.getName();
		member.nm              = nm;
		member.mr			   = mr;
		member.totd            = d;
		member.mrkNames 		   = names;
		member.ct       = crossDesign.getCrossType();
		member.cs       = crossDesign.getSize();
		member.cg = crossDesign.getGeneration();
		member.skeleton        = skeleton;
		member.mfc = mappingFunction;
		member.mut 	   = mappingUnit;
		if (qtls != null) {
			loci = new IBioLocus[qtls.size()];
			qtls.toArray(loci);
			member.attachQTL(loci);
		}
		if (rescale) {
			member.R2r();
		}
		
		clusters.add(member);
		
	}
	/**
	 * If a list of dubious markers has been set before
	 * then this routine checks if the given locus is
	 * not included into this list. 
	 * @see #setDubiousMarker(IBioLocus[])
	 * 
	 * @param locus the locus to check.
	 * @return true if the locus is in the dubious marker list, false
	 * otherwise.
	 */
	public boolean isDubiousLocus(IBioLocus locus) {
		int i;
		if (dubious==null) return false;
		
		for(i=0;i<dubious.length;i++) {
			if (dubious[i].getName().equals(locus.getName()))
				return true;
		}
		
		return false;
	}
	/**
	 * This method returns the members of the cluster as an
	 * array of {@link Chromosomes}. If any chromosome has been
	 * added to the cluster the method returns null. 
	 * 
	 * @return an array of {@link Chromosomes}.
	 */
	public Chromosome[] getClusterMembers() {
		if (clusters==null) return null;
		if (clusters.size() == 0) return null;
		
		if (this._nameIdx != null) {
			ArrayList memberList = new ArrayList();
			for(int i=0;i<clusters.size();i++) {
				Chromosome chrom    = (Chromosome) clusters.get(i);
				Chromosome subChrom = chrom.getSubChromosome(this._nameIdx);
				if (subChrom != null)
					memberList.add(subChrom);
			}
			if (memberList.size() > 0) {
				Chromosome[] members = new Chromosome[memberList.size()];
				memberList.toArray(members);
				return members;
			} else
				return null;
		} else if (clusters != null ){
			Chromosome[] members = new Chromosome[clusters.size()];
			clusters.toArray(members);
			return members;
		} else
			return null;
	}
	/**
	 * This method returns the number of distinct markers implied
	 * in this cluster, i.e the size of the marker table.
	 * 
	 * @return the number of distinct markers.
	 */
	public int getMarkerNumber() {
		if (_nameIdx == null) return 0;
		return _nameIdx.length;
	}
	/**
	 * This method returns the total number of marker intervals,
	 * i.e the sum over chromosomes of the number of marker intervals.
	 * @return the total number of marker intervals.
	 */
	public int getMarkerIntervalNumber() {
		return this.ni;
	}
	/**
	 * This methods frozes the index of the distinct
	 * markers over all the members of the cluster.
	 * 
	 * @see #getMarkerNameByIndex(int)
	 * @see #getMarkerIndexByName(String)
	 */
	public void fixCluster()
	{
		int i,nmrk;
		Enumeration enu;
		if (locusNames == null) return;
		if (locusNames.size()==0) return;
		
		nmrk = 0;
		enu  = locusNames.keys();
		while (enu.hasMoreElements()) {
			Integer mrkCount = (Integer) locusNames.get(enu.nextElement());
			if (mrkCount.intValue() >= mrkThresh)
				nmrk++;
		}
		
		if (nmrk > 0) {
			enu      = locusNames.keys();
			_nameIdx = new String[nmrk];
			i=0;
			while (enu.hasMoreElements()) {
				_nameIdx[i] = (String) enu.nextElement();
				Integer mrkCount = (Integer) locusNames.get(_nameIdx[i]);
				if (mrkCount.intValue() >= mrkThresh)
					i++;
				if (i>=nmrk) break;
			}
		}
	}
	/**
	 * For a given marker name this method returns
	 * the marker index of the marker table if found.
	 * Otherwise it returns -1.
	 * 
	 * @see #fixCluster()
	 * 
	 * @param mrkName the name of the marker.
	 * @return the indice of the marker if found, -1 otherwise.
	 */
	public int getMarkerIndexByName(String mrkName) {
		int i;
		
		if (_nameIdx == null) return -1;
		
		for(i=0;i<_nameIdx.length;i++) {
			if (_nameIdx[i].equals(mrkName))
				return i;
		}
		
			return -1;
	}
	
	public void mvMarkerIndexToEnd(int i) {
		int j;
		String tmp = _nameIdx[i];
		
		for(j=i;j<_nameIdx.length-1;j++) {
			_nameIdx[j]=_nameIdx[j+1];
		}
		_nameIdx[j]=tmp;
	}
	/**
	 * This method returns the name of the marker 
	 * according to this indice. The given indice
	 * is assumed to be inside the correct range.
	 * 
	 * @see #fixCluster()
	 *  
	 * @param i the indice of the marker. 
	 * @return the name of the marker.
	 */
	public String getMarkerNameByIndex(int i) {
		return _nameIdx[i];
	}
	/**
	 * Returns the number of times the marker with
	 * the indice i has been seen over the
	 * chromosomes of the cluster.
	 * 
	 * @see #fixCluster()
	 * 
	 * @param i
	 * @return
	 */
	public int getMarkerOccurence(int i) {
		Integer ii = (Integer) this.locusNames.get(_nameIdx[i]);
		if (ii!=null)
			return ii.intValue();
		else
			return 0;
	}
	/**
	 * This method returns the name of the locus
	 * from the given name. If no locus name dictionnary for
	 * the clusters is defined then the method returns
	 * an exact copy of the given name.
	 * 
	 * @param name the raw name of the locus.
	 * @return the standard name of the locus.
	 */
	public String getMarkerName(String name) {
		
		if (mrkDico == null) {
			return new String(name);
		} else {
			String s = mrkDico.getTerm(name);
			return (s != null) ? s : name;
		}
		
	}
	/**
	 * This method sets the list of dubious marker. It must be used
	 * before adding chromosomes to the chromosome cluster. Then the
	 * the markers which names matches those of the list will be ignored.
	 * @param dubious
	 */
	public void setDubiousMarker(IBioLocus[] dubious) {
		this.dubious = dubious;
	}
	/**
	 * Returns the name of the cluster.
	 * @return the name of the cluster.
	 */
	public String getName() {
		return this.name;
	}	
	/**
	 * @return Returns the mrkDico.
	 */
	public MetaDico getMarkerDico() {
		return mrkDico;
	}
	/**
	 * @param mrkDico The mrkDico to set.
	 */
	public void setMarkerDico(MetaDico mrkDico) {
		this.mrkDico = mrkDico;
	}
	/**
	 * 
	 */
	public double[] getCMrkTotalNumbers() {
		double[] ratios = new double[this.clusters.size()];
		for(int i=0;i<this.clusters.size();i++) {
			Chromosome cur1 = (Chromosome) clusters.get(i);
			for(int j=i+1;j<this.clusters.size();j++) {
				Chromosome cur2 = (Chromosome) clusters.get(j);
				CMarkerSequence cms = CMSAlgorithm.run(cur1,cur2);
				if (cms != null) {
					ratios[i]+=cms.nmc;
					ratios[j]+=cms.nmc;
				}
			}
		}
		return ratios;
	}
	/**
	 * Tests if the cluster of chromosome is connected using a graph
	 * approach. Each chromosome is related to another by a set of 
	 * common markers. The use can supply a minimal number of common
	 * marker to declare that a chromosome is connected to the other ones.
	 * The cluster is said to be connected if there is a path
	 * connecting every pair of chromosomes.
	 * 
	 * @return
	 */
	public boolean isConnected(int nMrk) {
		ClusterGraph graph = new ClusterGraph(this);
		
		for(int i=0;i<this.clusters.size();i++) {
			Chromosome cur1 = (Chromosome) clusters.get(i);
			for(int j=i+1;j<this.clusters.size();j++) {
				Chromosome cur2 = (Chromosome) clusters.get(j);
				CMarkerSequence cms = CMSAlgorithm.run(cur1,cur2);
				if (cms != null && cms.nmc >= nMrk) {
					graph.addEdge(cur1, cur2, cms);
				}
			}
		}
		
		return graph.isConnected(nMrk);
	}
	
	public class ClusterGraph {
		
		public Hashtable adjTable;
		
		public ChromCluster cluster;
		
		/**
		 * @param cluster
		 */
		public ClusterGraph(ChromCluster cluster) {
			this.cluster=cluster;
			this.adjTable=new Hashtable();
		}
		/**
		 * Test if the graph is connected
		 * @return
		 */
		public boolean isConnected(int nMrk) {
			
			ArrayList L,K;
			Chromosome x;
			
			if (!adjTable.keySet().isEmpty())
			{	
				
				x = (Chromosome) adjTable.keySet().iterator().next();
				L = new ArrayList();
				K = new ArrayList();
				
				L.add(x);K.add(x);
				
				do {
					Chromosome y    = (Chromosome) K.get(0);
					K.remove(0);
					ArrayList edges = (ArrayList) adjTable.get(y);
					for(int i=0;i<edges.size();i++) {
						ClusterEdge edge = (ClusterEdge) edges.get(i);
						if (edge.cms.nmc >= nMrk) {
							Chromosome z = edge.chrom;
							if (!L.contains(z)) {
								L.add(z);
								K.add(z);
							}
						}
					}
				} while(!K.isEmpty());
				
				
				return (L.size() == adjTable.size());
			}
			
			return false;
		}
		/**
		 * @param cur1
		 * @param cur2
		 * @param nmc
		 */
		public void addEdge(Chromosome cur1, Chromosome cur2, CMarkerSequence cms) {
			ArrayList adj;
			if (!adjTable.containsKey(cur1)) {
				adj = new ArrayList();
				adjTable.put(cur1, adj);
			} else {
				adj = (ArrayList) adjTable.get(cur1);
			}
			adj.add(new ClusterEdge(cms, cur2));
			if (!adjTable.containsKey(cur2)) {
				adj = new ArrayList();
				adjTable.put(cur2, adj);
			} else {
				adj = (ArrayList) adjTable.get(cur2);
			}
			adj.add(new ClusterEdge(cms, cur1));
		}
		/**
		 * @return
		 */
		public Hashtable getAdjacentTable() {
			return adjTable;
		}
		
		public void summarize(OutputStream stream) {
			int i,j;
			int index[];
			double tot,prop,ntot;
			PrintWriter writer = new PrintWriter(stream);
			Hashtable buffer = new Hashtable();
			ArrayList keys;
			
			
			// Get the labels of the vertices.
			String[]     labels   = getVLabels();
			writer.println(">CR " + cluster.name + " Connected="+isConnected(1));
			writer.println("#");
			writer.println("# Table of the chromosomes");
			writer.println("#");
			buffer = new Hashtable(labels.length);
			keys   = new ArrayList(labels.length);
			for(ntot=tot=i=0;i<labels.length;i++) {
				Chromosome c = getChrom(i);
				keys.add(labels[i]);
				//writer.println((i+1)+"\t"+labels[i]+"\t"+c.nm+"\t"+c.totd/c.nm);
				buffer.put(labels[i], new String(labels[i]+"\t"+c.nm+"\t"+c.totd/c.nm));
            	tot+=c.nm;
            	ntot+=c.totd/c.nm;
			}
			Collections.sort(keys);
			index = new int[keys.size()];
			for(i=0;i<labels.length;i++) {
				writer.println((i+1)+"\t"+buffer.get(keys.get(i)));
				for(j=0;j<labels.length;j++) {
					if (labels[j].equals(keys.get(i))) { 
						index[i]=j;
						break;
					}
				}
			}
			tot/=(double)labels.length;
			ntot/=(double)labels.length;
			writer.println("#");
			writer.println("# Average number of marker per chrom m="+tot);
			writer.println("# Average interval marker distance="+ntot);
			writer.println("#");
			// Get edge values
			double[][] values = getEValues(1);
			for(tot=i=0;i<values.length;i++) {
				for(j=i+1;j<values.length;j++) {
					if (!Double.isNaN(values[i][j])) {
						prop=2.0*values[i][j];
						ntot=getChrom(i).nm;
						ntot+=getChrom(j).nm;
						prop /= ntot;
						tot  +=prop;
					}
				}
			}
			tot /= 0.5*values.length*(values.length-1);
			writer.println("#");
			writer.println("# Table of the number of common markers");
			writer.println("#");
			writer.println("# Total number of marker M="+cluster.getMarkerNumber());
			writer.println("# Prop of common markers p="+tot);
			writer.println("#");
			writer.print("Chrom\tIndex");
			for(j=0;j<labels.length;j++)
				writer.print("\t"+(j+1));
			writer.println("\tTotal");
			for(i=0;i<labels.length;i++) {
				writer.print(labels[index[i]]+"\t"+(i+1));
            	for(tot=0,j=0;j<=i;j++) {
            		writer.print("\t");
            		if (!Double.isNaN(values[index[i]][index[j]]))
            			tot+=values[index[i]][index[j]];
            	}
            	for(j=i+1;j<labels.length;j++) {
            		if (!Double.isNaN(values[index[i]][index[j]])) {
            			writer.print("\t"+values[index[i]][index[j]]);
            			tot+=values[index[i]][index[j]];
            		}
            		else writer.print("\t");        		
            	}
            	writer.println("\t"+tot);
            	//line += "\t"+tot;
            	//buffer.put(labels[i], line);
            }
			//for(i=0;i<labels.length;i++) {
			//	writer.println(keys.get(i)+"\t"+(i+1)+buffer.get(keys.get(i)));
			//}
			writer.println();
			writer.println("#");
			writer.println("# Table of the number of common sequences");
			writer.println("#");
			values = getEValues(2);
			writer.print("Chrom\tIndex");
			for(j=0;j<labels.length;j++)
				writer.print("\t"+(j+1));
			writer.println();
			for(i=0;i<labels.length;i++) {
				//String line = "";
            	writer.print(labels[index[i]]+"\t"+(i+1));
				for(j=0;j<=i;j++) writer.print("\t");
            	for(j=i+1;j<labels.length;j++) {
            		if (!Double.isNaN(values[index[i]][index[j]]))
            			writer.print("\t"+values[index[i]][index[j]]);
            		else
            			writer.print("\t");
            	}
            	writer.println();
            	//buffer.put(labels[i], line);
            }
			//for(i=0;i<labels.length;i++) {
			//	writer.println(keys.get(i)+"\t"+(i+1)+buffer.get(keys.get(i)));
			//}
			writer.println();
			writer.println("#");
			writer.println("# Table of proportion of marker implied in common sequences");
			writer.println("# The value can be negative depending on the frame of the  ");
			writer.println("# common sequences");
			writer.println("#");
			values = getEValues(3);
			writer.print("Chrom\tIndex");
			for(j=0;j<labels.length;j++)
				writer.print("\t"+(j+1));
			writer.println();
			for(i=0;i<labels.length;i++) {
				//String line = "";
            	writer.print(labels[index[i]]+"\t"+(i+1));
				for(j=0;j<=i;j++) writer.print("\t");
            	for(j=i+1;j<labels.length;j++) {
            		if (!Double.isNaN(values[index[i]][index[j]])) writer.print("\t"+values[index[i]][index[j]]);
            		else writer.print("\t");
            	}
            	writer.println();
            	//buffer.put(labels[i], line);
            }
			//for(i=0;i<labels.length;i++) {
			//	writer.println(keys.get(i)+"\t"+(i+1)+buffer.get(keys.get(i)));
			//}
			writer.println();
			writer.flush();
		}
		
		public String[] getVLabels() {
			String[] labels = new String[adjTable.size()];
			Enumeration iter      = adjTable.keys();
        	int i=0;
			while(iter.hasMoreElements()) {
        		Chromosome c = (Chromosome)iter.nextElement();
        		labels[i++]  = c.getMapName();
        	}
			return labels;
		}
		
		private double[][] getEValues(int mode) {
			int i,j;
			double[][] values     = new double[adjTable.size()][adjTable.size()];
			for(i=0;i<values.length;i++)
				for(j=0;j<values.length;j++)
					values[i][j]=Double.NaN;
			i=j=0;
			Enumeration iter      = adjTable.keys();
			while(iter.hasMoreElements()) {	
        		Chromosome c = (Chromosome)iter.nextElement();
        		ArrayList  edges = (ArrayList) adjTable.get(c);
        		if (edges == null) continue;
        		for(int k=0;k<edges.size();k++) {
        			ClusterEdge edge = (ClusterEdge)edges.get(k);
        			i = getVIndex(edge);
        			switch(mode) {
        				case 1 :
        					values[j][i] = edge.cms.nmc;
        					break;
        				case 2 :
        					if (edge.cms.nmc>1)
        						values[j][i] = edge.cms.ncs;
        					break;
        				case 3 :
        					if (edge.cms.nmc>1) {
	        					values[j][i]=0.0;
	        					for(int l=0;l<edge.cms.ncs;l++) {
	        						if (edge.cms.frames[l])	values[j][i]+=edge.cms.css[l];
	        						else values[j][i]-=edge.cms.css[l];
	        					}
	        					values[j][i] /= (double)edge.cms.nmc;
        					}
    						break;
    						
        			}
        		}
        		j++;
        	}
			return values;
		}
		
		private int getVIndex(ClusterEdge e) {
			int idx=0;
			Chromosome c = e.chrom;
			Enumeration iter      = adjTable.keys();
			while(iter.hasMoreElements()) {
				Chromosome c1 = (Chromosome)iter.nextElement();
				if (c1.getMapName().equals(c.getMapName()))
					break;
				idx++;
			}
			return idx;
		}
		
		private Chromosome getChrom(int idx) {
			int i=0;
			Enumeration iter      = adjTable.keys();
			while(iter.hasMoreElements()) {
				Chromosome c = (Chromosome)iter.nextElement();
				if (i==idx) return c;
				i++;
			}
			return null;
		}
		
	}
	
	public class ClusterEdge {
		/**
		 * @param nmc
		 * @param cur2
		 */
		public ClusterEdge(CMarkerSequence cms, Chromosome chrom) {
			this.cms   = cms;
			this.chrom = chrom;
		}

		public CMarkerSequence cms;
		
		public Chromosome chrom;
		
	}

	/**
	 * @param mrkThresh
	 */
	public void setMrkThresh(int mrkThresh) {
		this.mrkThresh = mrkThresh;
	}
	/**
	 * @return
	 */
	public ClusterGraph getGraph() {
		Chromosome[] chroms = getClusterMembers();
		if (chroms != null) {
			ClusterGraph graph  = new ClusterGraph(this);
			for(int i=0;i<chroms.length;i++) {
				for(int j=i+1;j<chroms.length;j++) {
					CMarkerSequence cms = CMSAlgorithm.run(chroms[i],chroms[j]);
					if (cms != null) {
						graph.addEdge(chroms[i], chroms[j], cms);
					}
				}
			}
			return graph;
		} else
			return null;
	}
}
