/*  file: intrinsictree.c
 *  Author: Ulrich Sauvage (ulrich@kaa.crbm.cnrs-mop.fr)
 *  Copyright (C) J Thierry-Mieg and R Durbin, 1994
 * -------------------------------------------------------------------
 * Acedb 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.
 * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
 * -------------------------------------------------------------------
 * This file is part of the ACEDB genome database package, written by
 * 	Richard Durbin (MRC LMB, UK) rd@mrc-lmba.cam.ac.uk, and
 *	Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr
 *
 * Description:
 * Exported functions:
 * HISTORY:
 * Last edited: Nov 20 14:05 1998 (fw)
 * Created: Thu Jun 23 12:24:12 1994 (ulrich)
 *-------------------------------------------------------------------
 */

/* $Id: intrinsictree.c,v 1.16 1999/09/01 10:57:33 fw Exp $ */

/***************************************************************/

/*
#define CHRONO

#define ARRAY_CHECK 
#define MALLOC_CHECK
*/
#include "acedb.h"
#include "keyset.h"
#include "topology.h"
#include "interval.h"

#ifndef NON_GRAPHIC
#include "graph.h"
#include "chrono.h"
#endif /* !NON_GRAPHIC */

/***************************************************************/

static INTTREE look ;

typedef struct { KEYSET next, zero ; int n, g ;} TR;
typedef struct { unsigned char tdis ; int i, j ;} TREEDIST ;

/***************************************************************/
/********************  Display  functions  *********************/
/***************************************************************/
#ifdef NON_GRAPHIC
void intrinsicTreeDestroy(void)
{
}
#else  /* !NON_GRAPHIC */
static Graph
  intTreeGraph = 0 ;
static Array
  intTreeCurrGraph = 0 ;

void intrinsicTreeDestroy(void)
{ int i ;
  Graph *gp ;

  if (intTreeCurrGraph)
    { i = arrayMax(intTreeCurrGraph) ;
      gp = arrp(intTreeCurrGraph, 0, Graph) - 1 ;
      while(gp++, i--)
	if (graphExists(*gp))
	  { graphActivate(*gp) ;
	    graphDestroy() ;
	  }
      arrayDestroy(intTreeCurrGraph) ;
    }
  if (intTreeGraph)
    if (graphExists(intTreeGraph))
      { graphActivate(intTreeGraph) ;
	graphDestroy() ;
      }
  intTreeGraph = 0 ;
} /* intrinsicTreeDestroy */

/***************************************************************/

static void localPreserve(void)
{ 
  if (!intTreeCurrGraph)
    intTreeCurrGraph = arrayCreate(16, Graph) ;
  array(intTreeCurrGraph, arrayMax(intTreeCurrGraph), Graph) = intTreeGraph ;
  intTreeGraph = 0 ;
} /* localPreserve */

/***************************************************************/

static MENUOPT intMapGraphMenu[] = {
  { graphDestroy,	"Quit" },
  { help,		"Help" },
  { graphPrint,		"Print" },
  { localPreserve,	"Preserve" },
  { 0, 0 }
};
#endif /* !NON_GRAPHIC */

/***************************************************************/
/************************* Sort Order **************************/
/***************************************************************/

static int treeOrder(void *a, void *b)
{ int x ;

  if (a == b)
    messcrash("Sort is stupid") ;
  x =  ((TREEDIST *) b)->tdis > ((TREEDIST *) a)->tdis ? 1 : 
     (((TREEDIST *) b)->tdis == ((TREEDIST *) a)->tdis ? 0 : -1) ;
  if (x == 0)
    x = ((TREEDIST *) b)->i - ((TREEDIST *) a)->i ;
  if (x == 0)
    x = ((TREEDIST *) b)->j - ((TREEDIST *) a)->j ;
  return
    x < 0 ? -1 : 1 ;
} /* treeOrder */

/***************************************************************/

static int intTreeOrder(void *a, void *b)
{ return 
    arr(look->tree,*(KEY*)a,TR).n 
    - arr(look->tree,*(KEY*)b,TR).n  ;
} /* intTreeOrder */

/***************************************************************/
/************************  Tree  Plot  *************************/
/***************************************************************/

static int intTreePlotDef(int i, int x, int fath, int prev)
{ 
  int j, ksmax, delta = 100, epsilon = 200 ;
  char *v , *vp, *v0 = 0 ;

  if (fath != -1)
    { v = v0 + (i > fath ? i + fath * look->nd : fath + i * look->nd) ;
      if (look->distances && assFind(look->distances, v, &vp))
	delta = vp - v0 ;
    }
  if (prev != -1)
    { v = v0 + (i > prev ? i + prev * look->nd : prev + i * look->nd ) ;
      if (look->distances && assFind(look->distances, v, &vp))
	epsilon = vp - v0 ;
    }
  if (arr(look->tree, i, TR).zero)
    { int z = x + 1 ;
      int k = i ;
      j = keySetMax(arr(look->tree, i, TR).zero) ;
      while(j--)
	k = intTreePlotDef(keySet(arr(look->tree, i, TR).zero, j), z++, i, k) ;
    }

#ifndef NON_GRAPHIC
  if (look->display)
    graphText
      ( messprintf
       ("%s     %d     %d",     
	name(keySet(look->def, i)), delta, epsilon
	), 2 + 8 * x, 2 + look->yTree) ;
#endif /* !NON_GRAPHIC */

  array(look->cOrder, look->yTree++, int) = i ;
  ksmax = keySetMax((look->trd).ks) ;
  keySet((look->trd).ks, ksmax) = keySet(look->def, i) ;
  keySet((look->trd).x, ksmax)  = x ;
  return i ;
} /* intTreePlotDef */

/***************************************************************/

static void intTreePlotNext(int i, int x, int fath)
{ int n, j, k ;
  KEYSET s = arr(look->tree, i, TR).next ;

  arr(look->tree, i, TR).n = 1 ; /* to prevent looping */
  if (s)
    { n = 0 ;
      for(j = 0 ; j < keySetMax(s) ; j++)
	{ k = keySet(s, j) ;
	  if(!arr(look->tree, k, TR).n)
	    intTreePlotNext(k, x + n++, i) ;
	}
    }
  look->prev = intTreePlotDef(i, x, fath, look->prev) ;
} /* intTreePlotNext */

/***************************************************************/

static int intTreeLength(int k)
{ KEYSET s = arrp(look->tree, k, TR)->next ;
  int i, l = 0, n ;
  i = s ? keySetMax(s) : 0 ;
  arr(look->tree, k, TR).n = 1 ; /* To prevent looping */
  while(i--)
    { if(!arr(look->tree, keySet(s, i), TR).n) /* not the father */
	if ((n = intTreeLength(keySet(s, i))) > l)
	  l = n ;
    }
  arr(look->tree, k, TR).n = ++l ; /* ++ for self */
  return l ;
} /* intTreeLength */

/***************************************************************/

static int intTreeEnd(int k)
{ KEYSET s = arrp(look->tree, k, TR)->next ;
  int i, n ;
  int j = -1 , l = -1 , myself = arr(look->tree, k, TR).n ;

  i = s ? keySetMax(s) : 0 ;
  if (myself == 1)
    return k ;
  while(i--)
    { n = arr(look->tree, keySet(s,i), TR).n ;
      if (n < myself && n > l)
	{ l = n ;
	  j = keySet(s, i) ;
	}
    }
  if (j == -1)
    messcrash("Bad tree in intTreeEnd, sorry") ;
  return intTreeEnd(j) ;
}

/***************************************************************/

static void intTreeClearN(void)
{ int i ;
  TR *trp ;

  i = look->nd ;
  trp = arrp(look->tree, 0, TR) - 1 ;
  while(trp++, i--)
    trp->n = 0 ;
} /* intTreeClearN */

/***************************************************************/

static void intTreeSort(int k)
{ KEYSET s = arrp(look->tree,k,TR)->next ;
  int i = s ? keySetMax(s) : 0 ;

  arr(look->tree, k, TR).n *= -1 ; /* To prevent looping */
  while(i--)
    if(arr(look->tree, keySet(s, i), TR).n > 0) /* not the father */
      intTreeSort(keySet(s, i)) ;
	
  if (s) arraySort(s, intTreeOrder) ;
} /* intTreeSort */

/***************************************************************/

static int intTreeFindHead(int x)
{ int i ;

  intTreeLength(x) ;
  i = intTreeEnd(x) ;
  intTreeClearN() ;
  intTreeLength(i) ; /* Move backwards */
  intTreeSort(i) ;
  intTreeClearN() ;
  return i ;
} /* intTreeFindHead */

/***************************************************************/

static void intTreePlot(int nn)
{ int first = 0, j, k, segmax = 0 ;
  TR *destrp ;

#ifndef NON_GRAPHIC
  if (look->display)
    {
      if(graphActivate(intTreeGraph))
	{ graphPop() ;
	  graphClear() ;
	  if (look->map)
	    graphText (messprintf("Map : %s", name(look->map)),  1., .2) ;
	}
      else
	{ intTreeGraph = graphCreate(TEXT_FULL_SCROLL, "Intrinsic tree", 0.180,0.25,0.4,0.65) ;
	  graphMenu(intMapGraphMenu) ;
	  graphHelp("Deficiency_map") ;
	  if (look->map)
	    graphText (messprintf("Map : %s", name(look->map)),  1., .2) ;
	  graphTextBounds(80, look->nd + 5) ;
	}
    }
#endif /* !NON_GRAPHIC */
  look->segment = arrayCreate(nn, TREE_DEF) ;
  for (j = 1 ; j <= nn ; j++)
    { for (k = 0; k < look->nd && arr(look->tree, k, TR).g != j; k++) ;
      if (k == look->nd)
	continue ;
      first = intTreeFindHead(k) ;
      (look->trd).ks = keySetCreate() ;
      (look->trd).x  = keySetCreate() ;
      look->prev = - 1 ;
      intTreePlotNext(first, 0, -1) ;
      array(look->segment, segmax++, TREE_DEF) = look->trd ;
    }
  j = arrayMax(look->tree) ;
  destrp = arrp(look->tree, j - 1, TR) + 1 ;
  while(destrp--, j--)
    { keySetDestroy(destrp->zero) ;
      keySetDestroy(destrp->next) ;
    }
  arrayDestroy(look->tree) ;
  arrayDestroy(look->tabledis) ;
  assDestroy(look->distances) ;
#ifndef NON_GRAPHIC
  if (look->display)
    { graphTextBounds(80, look->yTree + 5) ;
      graphRedraw() ;
    }
#endif /* !NON_GRAPHIC */

  return;
} /* intTreePlot */

/***************************************************************/
/*********************** Make Distances ************************/
/***************************************************************/

static void makeShortDistances(int limit)
{ int i, j, jmax, ii, jj ;
  int u, v , dummy, n, ND ;
  char *assp, *assp0 = 0, *valcp ;
  KEY *ksp, *kspi, *kspj ;
  KEYSET ks = 0, *ksi, *ksj ;
  Associator dista ;
  Array table ;

  if (assExists(look->distances))
    messcrash ("Double allocation de look->distances") ;
  if (look->display)
    look->distances = assBigCreate(20 * look->nd) ;
  else
    look->distances = 0 ;
  look->tabledis = arrayCreate(10 * look->nd, TREEDIST) ;/* Tableau contenant les distances !infinie */
  dista = look->distances ;
  table = look->tabledis ;
  n = 0 ;/* indice dans le tableau tabledis */
  i = ND = look->nd ;
  ksi = arrp(look->mind, i - 1, KEYSET) + 1 ;
  while(ksi--, i--)
    { if (!(j = keySetMax (*ksi)) || keySet(*ksi, 0) & F_FLAG)
	continue ; /* Pour obtenir le keyset des voisins de i */
      ks = keySetReCreate(ks) ;
      jj = 0 ;
      kspi = arrp(*ksi, j - 1, KEY) + 1 ;
      while(kspi--, j--)
	{ ksj = arrp(look->dinm, *kspi, KEYSET) ;
	  if ((ii = keySetMax(*ksj)))
	    { kspj = arrp(*ksj, ii - 1, KEY) ;
	      while(ii--)
		{ if (*kspj < i) /* Pour ne pas avoir a faire d(i, i) et d(j, i) */
		    keySet(ks, jj++) = *kspj ;
		  kspj-- ;
		}
	    }
	  else messerror ("tableau vide dans makeShortDistances") ;
	}
      keySetSort(ks) ;
      keySetCompress(ks) ;
      jmax = keySetMax(ks) ;
      if (!jmax)
	continue ; /* i e pas de voisin en dessous => passe a la def suivante */
      ksp = arrp(ks, jmax - 1, KEY) + 1 ;
      while(ksp--, jmax-- && !(keySet(*ksi,0) & F_FLAG))
	{ ksj = arrp(look->mind, *ksp, KEYSET) ;
	  if (!keySetMax (*ksj) || keySet(*ksj, 0) & F_FLAG) /* test sur max normalement inutile */
	    continue ;
	  j = *ksp ;
	  valcp = assp0 + (i + ND * j) ;
	  ii = keySetMax(*ksi) ;
	  jj = keySetMax(*ksj) ;
	  kspi = arrp(*ksi, ii - 1, KEY) ;
	  kspj = arrp(*ksj, jj - 1, KEY) ;
	  u = ii + jj ;
	  v = 0 ;
	  while(ii && jj)
	    {
	      if (*kspi == *kspj)
		{ v++ ;
		  kspi-- ; ii-- ;
		  kspj-- ; jj-- ;
		}
	      else if (*kspi > *kspj)
		{ ii-- ;
		  kspi-- ;
		}
	      else
		{ jj-- ;
		  kspj-- ;
		}
	    }
	  u -= (2 * v) ;
	  if (!u && !v) u = 1 ;
	  dummy = 120 * u ;
	  dummy /= (u + v) ;
	  if (v == keySetMax(*ksj)) /* alors ksj inclus ou egale ksi */
	    dummy = 0 ;
	  else if (v == keySetMax(*ksi)) /* alors ksi inclus ksj */
	    { dummy = 0 ;
	      keySet(*ksi, 0) |= F_ZERO ;
	      if (dista)
		assInsert(dista, valcp, assp0) ; /* association avec dummy = 0 donc assp0 directement */
	      array(table, n, TREEDIST).tdis = (unsigned char)dummy ;
	      arr(table, n, TREEDIST).i = j ; /* car c'est i qu'on supprime au */
	      arr(table, n, TREEDIST).j = i ; /* retour dans le calcul de l'arbre */
	      dummy = 100 ; /* Pour etre au-dessus de la limite */
	      n++ ;
	    }
	  assp = assp0 + dummy ;
	  if (!dummy) /* ksj identique (ou inclus) a ksi */
	    keySet(*ksj, 0) |= F_ZERO ;
	  if (dummy <= limit)
	    { 
	      if (dista)
		assInsert(dista, valcp, assp) ;
	      array(table, n, TREEDIST).tdis = (unsigned char) dummy ;
	      arr(table, n, TREEDIST).i = i ;
	      arr(table, n, TREEDIST).j = j ;
	      n++ ;
	    }
	}
    }
  keySetDestroy(ks) ;
} /* makeShortDistances */

/***************************************************************/

static void intProporEdw(Array *intpro)
{ int i = look->nm, j, k = 0, l = 0, *ip ;
  KEYSET *ksp ;
  KEY *keyp ;

  *intpro = arrayCreate(i, int) ;
  ip = arrayp(*intpro, i - 1, int) ;
  ksp = arrp(look->dinm, i - 1, KEYSET) + 1 ;
  while (ksp--, i--)
    { j = keySetMax(*ksp) ;
      k = 0 ;
      keyp = arrp(*ksp, 0, KEY) - 1 ;
      while(keyp++, j--)
	(*keyp & F_PERE) ? k++ : l++ ;
      if (!k && !l)
	*ip-- = 50 ;
      else
	*ip-- = (100 * k) / (k + l) ;
    }
} /* intProporEdw */

/***************************************************************/

static void makeDistancesEdwards(int limit)
{ int i, j, jmax, ii, jj ;
  int u, v, w, dummy, n, ND ;
  char *assp, *assp0 = 0, *valcp ;
  KEY *ksp, *kspi, *kspj ;
  KEYSET ks = 0, *ksi, *ksj ;
  Associator dista ;
  Array table, propor = 0 ;

  look->distances = assBigCreate(20 * look->nd) ;
  look->tabledis = arrayCreate(10 * look->nd, TREEDIST) ;/* Tableau contenant les distances !infinie */
  dista = look->distances ;
  table = look->tabledis ;
  intProporEdw(&propor) ;
  n = 0 ;/* indice dans le tableau tabledis */
  i = ND = look->nd ;
  ksi = arrp(look->mind, i - 1, KEYSET) + 1 ;
  while(ksi--, i--)
    { if (keySet(*ksi, 0) & F_ZV_FLAG)
	continue ;
      j = keySetMax(*ksi) ; /* Pour obtenir le keyset des voisins de i */
      ks = keySetReCreate(ks) ;
      jj = 0 ;
      kspi = arrp(*ksi, j - 1, KEY) + 1 ;
      while(kspi--, j--)
	{ ksj = arrp(look->dinm, *kspi & F_WHO, KEYSET) ;
	  ii = keySetMax(*ksj) ;
	  kspj = arrp(*ksj, ii - 1, KEY) ;
	  while(ii--)
	    { if ((*kspi & *kspj & F_PEME) && ((*kspj & F_WHO) < i)) /* Pour ne pas avoir a faire d(i, i) et d(j, i) */
		keySet(ks, jj++) = *kspj & F_WHO ;
	      kspj-- ;
	    }
	}
      keySetSort(ks) ;
      keySetCompress(ks) ;
      jmax = keySetMax(ks) ;
      if (!jmax)
	continue ; /* i e pas de voisin en dessous => passe a la def suivante */
      ksp = arrp(ks, jmax - 1, KEY) + 1 ;
      while(ksp--, jmax-- && !(keySet(*ksi,0) & F_ZV_FLAG))
	{ ksj = arrp(look->mind, *ksp, KEYSET) ;
	  if (keySet(*ksj, 0) & F_ZV_FLAG)
	    continue ;
	  j = *ksp ;
	  valcp = assp0 + (i + ND * j) ;
	  ii = keySetMax(*ksi) ;
	  jj = keySetMax(*ksj) ;
	  kspi = arrp(*ksi, ii - 1, KEY) ;
	  kspj = arrp(*ksj, jj - 1, KEY) ;
	  u = v = 0 ;
	  while(ii && jj)
	    {
	      if ((*kspi & F_WHO) == (*kspj & F_WHO))
		{ 
		  if (*kspi & *kspj & F_PEME)
		    v += 200 ;
		  else
		    u += 200 ;
		  kspi-- ; ii-- ;
		  kspj-- ; jj-- ;
		}
	      else if ((*kspi & F_WHO) > (*kspj & F_WHO))
		{ ii-- ;
		  w = arr(propor, *kspi & F_WHO, int) ;
		  if (*kspi & F_PERE) /* inconue => 1 chance sur deux d'etre 1 ou autre */
		    { v += w ;
		      u += 100 - w ;
		    }
		  else
		    { v += 100 - w ;
		      u += w ;
		    }
		  kspi-- ;
		}
	      else
		{ jj-- ;
		  w = arr(propor, *kspj & F_WHO, int) ;
		  if (*kspj & F_PERE) /* inconue => 1 chance sur deux d'etre 1 ou autre */
		    { v += w ;
		      u += 100 - w ;
		    }
		  else
		    { v += 100 - w ;
		      u += w ;
		    }
		  kspj-- ;
		}
	    }
	  while(ii--)
	    { w = arr(propor, *kspi & F_WHO, int) ;
	      if (*kspi & F_PERE) /* inconue => 1 chance sur deux d'etre 1 ou autre */
		{ v += w ;
		  u += 100 - w ;
		}
	      else
		{ v += 100 - w ;
		  u += w ;
		}
	      kspi-- ;
	    }
	  while(jj--)
	    { w = arr(propor, *kspj & F_WHO, int) ;
	      if (*kspj & F_PERE) /* inconue => 1 chance sur deux d'etre 1 ou autre */
		{ v += w ;
		  u += 100 - w ;
		}
	      else
		{ v += 100 - w ;
		  u += w ;
		}
	      kspj-- ;
	    }
	  if (!u && !v) u = 1 ;
	  dummy = 120 * u ;
	  dummy /= (u + v) ;
	  if (!dummy) /* ksj identique (ou inclus) a ksi */
	    keySet(*ksj, 0) |= F_ZERO ;
	  if (dummy <= limit)
	    { assp = assp0 + dummy ;
	      assInsert(dista, valcp, assp) ;
	      array(table, n, TREEDIST).tdis = (unsigned char) dummy ;
	      arr(table, n, TREEDIST).i = i ;
	      arr(table, n, TREEDIST).j = j ;
	      n++ ;
	    }
	}
    }
  keySetDestroy(ks) ;
  arrayDestroy(propor) ;
}

/***************************************************************/

static void intCalPropor(Array *intpro)
{ int i = look->nd, j, k = 0, *ip ;
  KEYSET *ksp ;
  KEY *keyp ;

  *intpro = arrayCreate(i, int) ;
  ip = arrayp(*intpro, i - 1, int) ;
  ksp = arrp(look->mind, i - 1, KEYSET) + 1 ;
  while (ksp--, i--)
    { j = keySetMax(*ksp) ;
      k = 0 ;
      keyp = arrp(*ksp, 0, KEY) - 1 ;
      while(keyp++, j--)
	if (!(*keyp & F_NO)) 
	  k++ ;
      *ip-- = (100 * k) / look->nd ;
    }
} /* intCalPropor */

/***************************************************************/

static void makeDistances(int limit, int choix)
{ int i, j, k, jmax, ii, jj, dati, datj ;
  int u, v , dummy, n, ND ;
  char *assp, *assp0 = 0, *valcp ;
  KEY *ksp, *kspi, *kspj ;
  KEYSET ks = 0, *ksi, *ksj ;
  Associator dista ;
  Array table, propor = 0 ;

  look->distances = assBigCreate(20 * look->nd) ;
  look->tabledis = arrayCreate(10 * look->nd, TREEDIST) ;/* Tableau contenant les distances !infinie */
  dista = look->distances ;
  table = look->tabledis ;
  n = 0 ;/* indice dans le tableau tabledis */
  i = ND = look->nd ;
  if (choix == 4)
    intCalPropor(&propor) ;
  ksi = arrp(look->mind, i - 1, KEYSET) + 1 ;
  while(ksi--, i--)
    { if (keySet(*ksi, 0) & F_ZV_FLAG)
	continue ;
      j = keySetMax(*ksi) ; /* Pour obtenir le keyset des voisins de i */
      ks = keySetReCreate(ks) ;
      jj = 0 ;
      kspi = arrp(*ksi, j - 1, KEY) + 1 ;
      while(kspi--, j--)
	{ if (*kspi & F_NO) /* on ne prend que si il existe un vrai YES en commun */
	    continue ;
	  ksj = arrp(look->dinm, *kspi & F_WHO, KEYSET) ;
	  ii = keySetMax(*ksj) ;
	  kspj = arrp(*ksj, ii - 1, KEY) ;
	  while(ii--)
	    { if (!(*kspj & F_NO) && ((*kspj & F_WHO) < i)) /* Pour ne pas avoir a faire */
		keySet(ks, jj++) = *kspj & F_WHO ; /* d(i, i) et d(j, i) et ceux qui n'ont pas */
	      kspj-- ;                                      /* au moins un YES en commun */
	    }
	}
      keySetSort(ks) ;
      keySetCompress(ks) ;
      jmax = keySetMax(ks) ;
      if (!jmax)
	continue ; /* i e pas de voisin en dessous => passe a la def suivante */
      ksp = arrp(ks, jmax - 1, KEY) + 1 ;
      while(ksp--, jmax-- && !(keySet(*ksi,0) & F_ZERO))
	{ ksj = arrp(look->mind, *ksp, KEYSET) ;
	  if (keySet(*ksj, 0) & F_ZV_FLAG)
	    continue ;
	  j = *ksp ;
	  valcp = assp0 + (i + ND * j) ;
	  ii = keySetMax(*ksi) ;
	  jj = keySetMax(*ksj) ;
	  kspi = arrp(*ksi, ii - 1, KEY) ;
	  dati = (*kspi & F_NO) ? 0 : 1 ;
	  kspj = arrp(*ksj, jj - 1, KEY) ;
	  datj = (*kspj & F_NO) ? 0 : 1 ;
	  u = v = 0 ;
	  while(ii && jj)
	    {
	      if ((*kspi & F_NON_DIAG) || (*kspj & F_NON_DIAG))
		{
		  if (*kspi & F_NON_DIAG)
		    { kspi-- ; i-- ;
		      dati = (*kspi & F_NO) ? 0 : 1 ;
		    }
		  if (*kspj & F_NON_DIAG)
		    { kspj-- ; j-- ;
		      datj = (*kspj & F_NO) ? 0 : 1 ;
		    }
		  continue ;
		}
	      if ((*kspi & F_WHO) == (*kspj & F_WHO))
		{ switch(dati + datj)
		    { case 0: /*  no - no  */
			break ;
		      case 1: /* yes - no  */
			u += 200 ;
			break ;
		      case 2: /* yes - yes */
			v += 200 ;
			break ;
		      }
		  kspi-- ; ii-- ;
		  dati = (*kspi & F_NO) ? 0 : 1 ;
		  kspj-- ; jj-- ;
		  datj = (*kspj & F_NO) ? 0 : 1 ;
		}
	      else if ((*kspi & F_WHO) > (*kspj & F_WHO))
		{ switch(dati)
		    { case 0: /*  no - inc */
			if (choix == 3)
			  u += 100 ;
			if (choix == 4)
			  u += arr(propor, i, int) ;
			break ;
		      case 1: /* yes - inc */
			switch(choix)
			  { case 1:
			      u += 200 ;
			      break ;
			    case 2:
			      u += 100 ;
			      break ;
			    case 3:
			      u += 50 ;
			      v += 50 ;
			      break ;
			    case 4:
			      v += arr(propor, i, int) ;
			      u += 100 - arr(propor, i, int) ;
			      break ;
			    default:
			      break ;
			    }
			break ;
		      }
		  ii-- ;
		  kspi-- ;
		  dati = (*kspi & F_NO) ? 0 : 1 ;
		}
	      else
		{ switch(datj)
		    { case 0: /*  no - inc */
			if (choix == 3)
			  u += 100 ;
			if (choix == 4)
			  u += arr(propor, j, int) ;
			break ;
		      case 1: /* yes - inc */
			switch(choix)
			  { case 1:
			      u += 200 ;
			      break ;
			    case 2:
			      u += 100 ;
			      break ;
			    case 3:
			      u += 50 ;
			      v += 50 ;
			      break ;
			    case 4:
			      v += arr(propor, j, int) ;
			      u += 100 - arr(propor, j, int) ;
			      break ;
			    default:
			      break ;
			    }
			break ;
		      }
		  jj-- ;
		  kspj-- ;
		  datj = (*kspj & F_NO) ? 0 : 1 ;
		}
	    }
	  k = i ;
	  if (jj)
	    { if (ii)
		messcrash("Probleme dans makeDistances") ;
	      ii = jj ;
	      dati = datj ;
	      kspi = kspj ;
	      k = j ;
	    }
	  while(ii--)
	    { switch(dati)
		{ case 0: /*  no - inc */
		    if (choix == 3)
		      u += 100 ;
		    if (choix == 4)
		      u += arr(propor, k, int) ;
		    break ;
		  case 1: /* yes - inc */
		    switch(choix)
		      { case 1:
			  u += 200 ;
			  break ;
			case 2:
			  u += 100 ;
			  break ;
			case 3:
			  u += 50 ;
			  v += 50 ;
			  break ;
			case 4:
			  v += arr(propor, k, int) ;
			  u += 100 - arr(propor, k, int) ;
			  break ;
			default:
			  break ;
			}
		    break ;
		  }
	      kspi-- ;
	      dati = (*kspi & F_NO) ? 0 : 1 ;
	    }
	  if (!u && !v) u = 1 ;
	  dummy = 120 * u ;
	  dummy /= (u + v) ;
	  if (v == 200 * keySetMax(*ksj)) /* alors ksj inclus ou egale ksi */
	    dummy = 0 ;
	  else if (v == 200 * keySetMax(*ksi)) /* alors ksi inclus ksj */
	    { dummy = 0 ;
	      keySet(*ksi, 0) |= F_ZERO ;
	      assInsert(dista, valcp, assp0) ; /* association a 0 */
	      array(table, n, TREEDIST).tdis = (unsigned char)dummy ;
	      arr(table, n, TREEDIST).i = j ; /* car c'est i qu'on supprime au */
	      arr(table, n, TREEDIST).j = i ; /* retour dans le calcul de l'arbre */
	      dummy = 100 ; /* Pour etre au-dessus de la limite */
	      n++ ;
	    }
	  assp = assp0 + dummy ;
	  if (!dummy) /* ksj identique (ou inclus) a ksi */
	    keySet(*ksj, 0) |= F_ZERO ;
	  if (dummy <= limit)
	    { assInsert(dista, valcp, assp) ;
	      array(table, n, TREEDIST).tdis = (unsigned char) dummy ;
	      arr(table, n, TREEDIST).i = i ;
	      arr(table, n, TREEDIST).j = j ;
	      n++ ;
	    }
	}
    }
  keySetDestroy(ks) ;
  if (choix == 4)
    arrayDestroy(propor) ;
} /* makeDistances */

/***************************************************************/
/************************  Make  Tree  *************************/
/***************************************************************/

static int makeIntTree(void)
{ Array tree ;
  int nmax, narb, inarb, newi, newj, valg ;
  int valni, valnj , izer ;
  KEYSET newKs ;

  look->tree = arrayCreate(look->nd, TR) ;
  tree = look->tree ;
  array(tree, look->nd - 1, TR).g = 0 ;
  nmax = arrayMax(look->tabledis) ;
  arraySort(look->tabledis, treeOrder) ;
  narb = 0 ;  /* number of small tree */
  inarb = 0 ;   /* indice de l'arbre */
  while(nmax--)
    { newi = arr(look->tabledis, nmax, TREEDIST).i ;
      newj = arr(look->tabledis, nmax, TREEDIST).j ;
      if (!(arr(look->tabledis, nmax, TREEDIST).tdis)) /* Suppression des identiques */
	{ arr(tree, newj, TR).g = - 1 ;
	  newKs = arr(tree, newi, TR).zero ;
	  if (!arrayExists(newKs))
	    { newKs = keySetCreate() ;
	      arr(tree, newi, TR).zero = newKs ;
	    }
	  izer = keySetMax(newKs) ;
	  if (!(arr(tree, newi, TR).g))
	    { arr(tree, newi, TR).g = ++inarb ;
	      narb++ ;
	    }
	  keySet(newKs, izer) = newj ;
	  continue ;
	}
      if (keySet(arr(look->mind, newi, KEYSET), 0) & F_ZERO)
	continue ;                                 /* identiques dans un autre arbre */
      if (keySet(arr(look->mind, newj, KEYSET), 0) & F_ZERO)
	continue ;
      if (!(arr(tree, newi, TR).g))
	{ newKs = keySetCreate() ;
	  arr(tree, newi, TR).next = newKs ;
	  keySet(arr(tree, newi, TR).next, 0) = newj ;
	  if (!(arr(tree, newj, TR).g))
	    { newKs = keySetCreate() ;
	      arr(tree, newj, TR).next = newKs ;
	      keySet(arr(tree, newj, TR).next, 0) = newi ;
	      arr(tree, newi, TR).g = ++inarb ;
	      arr(tree, newj, TR).g = inarb ;
	      narb++ ;
	    }
	  else
	    { 
	      if (!(arr(tree, newj, TR).next)) /* il est possible d'etre dans un arbre */
		{ valnj = 0 ;                  /* sans avoir de next, si on a un zero */
		  newKs = keySetCreate() ;
		  arr(tree, newj, TR).next = newKs ;
		}
	      else
		valnj = keySetMax(arr(tree, newj, TR).next) ;
	      keySet(arr(tree, newj, TR).next, valnj) = newi ;
	      arr(tree, newi, TR).g = arr(tree, newj, TR).g ;
	    } 
	}
      else
	{
	  if (arr(tree, newj, TR).g)
	    {
	      if (arr(tree, newi, TR).g != arr(tree, newj, TR).g)
		{ narb-- ;
		  if (!(arr(tree, newi, TR).next))
		    { valni = 0 ;
		      newKs = keySetCreate() ;
		      arr(tree, newi, TR).next = newKs ;
		    }
		  else
		    valni = keySetMax(arr(tree, newi, TR).next) ;
		  if (!(arr(tree, newj, TR).next))
		    { valnj = 0 ;
		      newKs = keySetCreate() ;
		      arr(tree, newj, TR).next = newKs ;
		    }
		  else
		    valnj = keySetMax(arr(tree,newj,TR).next) ;
		  keySet(arr(tree, newi, TR).next, valni) = newj ;
		  keySet(arr(tree, newj, TR).next, valnj) = newi ;
		  if (arr( tree, newi, TR).g < arr(tree, newj, TR).g)
		    { valnj = arr(tree, newj, TR).g ;
		      valni = look->nd ;
		      valg = arr(tree, newi, TR).g ;
		      while (valni--)
			{ if ((arr(tree, valni, TR).g) == valnj)
			    arr(tree, valni, TR).g = valg ;
			}
		    }
		  else
		    { valni = arr(tree, newi, TR).g ;
		      valnj = look->nd ;
		      valg = arr(tree, newj, TR).g ;
		      while (valnj--)
			{ if ((arr(tree, valnj, TR).g) == valni)
			    arr(tree, valnj, TR).g = valg ;
			}
		    }
		}
	    }
	  else
	    {
	      if (!(arr(tree, newi, TR).next))
		{ valni = 0 ;
		  newKs = keySetCreate() ;
		  arr(tree, newi, TR).next = newKs ;
		}
	      else
		valni = keySetMax(arr(tree, newi, TR).next) ;
	      keySet(arr(tree, newi, TR).next, valni) = newj ;
	      newKs = keySetCreate() ;
	      arr(tree, newj, TR).next = newKs ;
	      keySet(arr(tree, newj, TR).next, 0) = newi ;
	      arr(tree, newj, TR).g = arr(tree, newi, TR).g ;
	    }
	}
    }
  newi = look->nd ;
  while (newi--)
    { if (!arr(tree, newi, TR).g)
	{ narb++ ;
	  arr(tree, newi, TR).g = ++inarb ;
	}
    }
  return inarb ;
} /* makeIntTree */

/***************************************************************/

static Array intRetourneSeg(void)
{ int i = arrayMax(look->segment), j, k ;
  TREE_DEF *ksmp, *ksmq ;
  KEY *keys, *keyx ;
  Array maille = arrayCreate(i, TREE_DEF) ;

  if (!i) return maille ;
  ksmp = arrp(look->segment, i - 1, TREE_DEF) + 1 ;
  ksmq = arrayp(maille, i - 1, TREE_DEF) + 1 ;
  while(ksmp--, ksmq--, i--)
    { j = keySetMax(ksmp->ks) ;
      keys = arrp(ksmp->ks, j - 1, KEY) ;
      keyx = arrp(ksmp->x, j - 1, KEY) ;
      ksmq->ks = keySetCreate() ;
      ksmq->x = keySetCreate() ;
      k = 0 ;
      while(j--)
	{ keySet(ksmq->ks, k) = *keys-- ;
	  keySet(ksmq->x, k++) = *keyx-- ;
	}
      keySetDestroy(ksmp->ks) ;
      keySetDestroy(ksmp->x) ;
    }
  return maille ;
} /* intRetourneSeg */

/***************************************************************/

static void intDestroySeg()
{ int i ;
  TREE_DEF *ksm ;

  i = arrayMax(look->segment) ;
  ksm = arrp(look->segment, 0, TREE_DEF) - 1 ;
  while(ksm++, i--)
    { keySetDestroy(ksm->ks) ;
      keySetDestroy(ksm->x) ;
    }
  arrayDestroy(look->segment) ;
} /* intDestroySeg */

/***************************************************************/

void intCptSupFlag(Array donnee, KEY flag)
{ int i, j ;
  KEYSET *ksp ;
  KEY *mkp ;

  i = arrayMax(donnee) ;
  ksp = arrp(donnee, i - 1, KEYSET) + 1 ;
  while(ksp--, i--)
    { if ((j = keySetMax(*ksp)))
	{ mkp = arrp(*ksp, j - 1, KEY) ;
	  while(j--)
	    *mkp-- &= ~flag ;
	}
    }
} /* intCptSupFlag */

/***************************************************************/

int intCptTree(DEFCPT defCptLook)
{ int nbTree, dlimit, mchoix , n ;

  dlimit = defCptLook->dlimit ;
  if (dlimit <= 0 || dlimit > 100)
    messcrash("Bad value for distance limit in intCptTree") ;
  dlimit *= 12 ; dlimit /= 10 ;
  if (dlimit == 120) dlimit = 119 ;
  if ((mchoix = defCptLook->method) > 4) /* attention en fait M_EDWARD */
    messcrash("Bad value for method in intCptTree") ;
  look = (INTTREE)messalloc(sizeof(struct INTTREESTUFF)) ;
  look->magic = INTTREEMAG ;
  look->yTree = 0 ;
  look->display = defCptLook->display ;
  look->map = defCptLook->selectedMap ;
  look->def = defCptLook->def ;
  look->genes = defCptLook->mar ;
  look->cOrder = defCptLook->colOrder ;
  look->mind = defCptLook->marInDef ;
  look->dinm = defCptLook->defInMar ;
  if ((look->nd = arrayMax(look->mind)) != keySetMax(look->def) || 
      (look->nm = arrayMax(look->dinm)) != keySetMax(look->genes))
    messcrash("uncompatible dimension of arrays data") ;
  if (arrayMax(look->cOrder) != look->nd)
    messcrash("uncompatible dimension of array Order") ;
  if (!look->nd || !look->nm)
    messcrash("ND or NM is null") ;
  if (!(defCptLook->whatDis & F_G_ZERO))
    { intCptSupFlag(look->mind, F_ZERO) ;
      intCptSupFlag(look->dinm, F_ZERO) ;
    }
  if (!(defCptLook->whatDis & F_E_YN) && mchoix != M_EDWARD)   /* attention choix des distances a revoir */
    makeShortDistances(dlimit) ;
  else if (mchoix == M_EDWARD)
    makeDistancesEdwards(dlimit) ;
  else
    makeDistances(dlimit, defCptLook->whatDis & 7) ;
  if (!arrayMax(look->tabledis))
    { arrayDestroy(look->tabledis) ;
      look->magic = 0 ;
      messfree(look) ;
      return 0 ;
    }
  nbTree = makeIntTree() ;
  intTreePlot(nbTree) ;  /* destroys look->tree and look->tabledis */
  n = arrayMax(look->segment) ;
  if (mchoix == M_EDWARD)
    { intDestroySeg() ;
      intCptSupFlag(look->mind, F_ZERO) ;
      intCptSupFlag(look->dinm, F_ZERO) ;
    }
  else if(n)
    defCptLook->maillon = intRetourneSeg() ;

  arrayDestroy(look->segment) ; 
  look->magic = 0 ;
  messfree(look) ;
  return n ;
} /* intCptTree */

/***************************************************************/
/************************* eof *********************************/
/***************************************************************/
