/*
 *   pstep.h
 *
 *
 * Part of TREE-PUZZLE 5.2 (February 2005)
 *
 * (c) 2003-2005 by Heiko A. Schmidt, Korbinian Strimmer, and Arndt von Haeseler
 * (c) 1999-2003 by Heiko A. Schmidt, Korbinian Strimmer,
 *                  M. Vingron, and Arndt von Haeseler
 * (c) 1995-1999 by Korbinian Strimmer and Arndt von Haeseler
 *
 * All parts of the source except where indicated are distributed under
 * the GNU public licence.  See http://www.opensource.org for details.
 *
 * ($Id$)
 *
 */


#ifndef PSTEP_H
#define PSTEP_H

#include<stdio.h>
#include<stdlib.h>
#include"puzzle.h"
#include"util.h"
/* #include"newpstep.h" */

/******************************************************************/
/*  switch puzzling step algorithm                                */
/******************************************************************/

#define PSTEP_TRIPL

#ifndef PSTEP_RECUR
#	define PSTEP_RECUR
#endif /* PSTEP_RECUR */

#ifdef PSTEP_SPLIT
#	define PSTEPALGNAME "split-based puzzling step algorithm"

#	ifdef PSTEP_ORIG
#		undef PSTEP_ORIG
#	endif /* PSTEP_ORIG */

#	ifdef PSTEP_MRCA
#		undef PSTEP_MRCA
#	endif /* PSTEP_MRCA */

#	ifdef PSTEP_TRIPL
#		undef PSTEP_TRIPL
#	endif /* PSTEP_TRIPL */

#	ifdef PSTEP_RECUR
#		undef PSTEP_RECUR
#	endif /* PSTEP_RECUR */
#endif /* PSTEP_SPLIT */

#ifdef PSTEP_MRCA
#	define PSTEPALGNAME "mrca-based puzzling step algorithm"

#	ifdef PSTEP_ORIG
#		undef PSTEP_ORIG
#	endif /* PSTEP_ORIG */

#	ifdef PSTEP_SPLIT
#		undef PSTEP_SPLIT
#	endif /* PSTEP_SPLIT */

#	ifdef PSTEP_TRIPL
#		undef PSTEP_TRIPL
#	endif /* PSTEP_TRIPL */

#	ifdef PSTEP_RECUR
#		undef PSTEP_RECUR
#	endif /* PSTEP_RECUR */
#endif /* PSTEP_MRCA */

#ifdef PSTEP_TRIPL
#	define PSTEPALGNAME "triplet-based puzzling step algorithm"

#	ifdef PSTEP_ORIG
#		undef PSTEP_ORIG
#	endif /* PSTEP_ORIG */

#	ifdef PSTEP_SPLIT
#		undef PSTEP_SPLIT
#	endif /* PSTEP_SPLIT */

#	ifdef PSTEP_MRCA
#		undef PSTEP_MRCA
#	endif /* PSTEP_MRCA */

#	ifdef PSTEP_RECUR
#		undef PSTEP_RECUR
#	endif /* PSTEP_RECUR */
#endif /* PSTEP_TRIPL */

#ifdef PSTEP_ORIG
#	define PSTEPALGNAME "original puzzling step algorithm"

#	ifdef PSTEP_MRCA
#		undef PSTEP_MRCA
#	endif /* PSTEP_MRCA */

#	ifdef PSTEP_SPLIT
#		undef PSTEP_SPLIT
#	endif /* PSTEP_SPLIT */

#	ifdef PSTEP_TRIPL
#		undef PSTEP_TRIPL
#	endif /* PSTEP_TRIPL */

#	ifdef PSTEP_RECUR
#		undef PSTEP_RECUR
#	endif /* PSTEP_RECUR */
#endif /* PSTEP_ORIG */

#ifdef PSTEP_RECUR
#	define PSTEPALGNAME "recursive puzzling step algorithm"

#	ifdef PSTEP_ORIG
#		undef PSTEP_ORIG
#	endif /* PSTEP_ORIG */

#	ifdef PSTEP_MRCA
#		undef PSTEP_MRCA
#	endif /* PSTEP_MRCA */

#	ifdef PSTEP_TRIPL
#		undef PSTEP_TRIPL
#	endif /* PSTEP_TRIPL */

#	ifdef PSTEP_SPLIT
#		undef PSTEP_SPLIT
#	endif /* PSTEP_SPLIT */
#endif /* PSTEP_RECUR */

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

#define NOWHERE     0
#define UP          1
#define UPDOWNLEFT  2
#define UPDOWNRIGHT 3
#define DOWNLEFT    4
#define DOWNRIGHT   5
#define STARTING    6

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

#ifdef PSTEP_TRIPL
#   define VOTING_PATH_ABS_PENALTY           1	/* QP */
#   define VOTING_PATH_ABS_BONUS             2
#   define VOTING_PATH_ABS_BONUS_PENALTY     3
#   define VOTING_PATH_REL_PENALTY           4
#   define VOTING_PATH_REL_BONUS             5
#   define VOTING_PATH_REL_BONUS_PENALTY     6	/* SQP */
#   define VOTING_SUBTREE_ABS_PENALTY        7
#   define VOTING_SUBTREE_ABS_BONUS          8
#   define VOTING_SUBTREE_ABS_BONUS_PENALTY  9
#   define VOTING_SUBTREE_REL_PENALTY       10
#   define VOTING_SUBTREE_REL_BONUS         11
#   define VOTING_SUBTREE_REL_BONUS_PENALTY 12
#   define VOTING_MAX                       12

#   define VOTING_DEFAULT VOTING_PATH_ABS_PENALTY		/* QP */
#   if 0
#      define VOTING_DEFAULT VOTING_PATH_REL_BONUS_PENALTY	/* SQP */
#   endif

#   define QUARTET_MISSING_RANDOM_DEFAULT FALSE
#   if 0
#      define QUARTET_MISSING_RANDOM TRUE
#      define QUARTET_MISSING_RANDOM FALSE
#   endif

#   define QUARTET_WEIGHTED_VOTE_DEFAULT TRUE
#   if 0
#      define QUARTET_WEIGHTED_VOTE TRUE
#      define QUARTET_WEIGHTED_VOTE FALSE
#   endif



extern int pstep_voting_optn;
extern int pstep_missing_random_optn;
extern int pstep_unresolved_weighting_optn;

#endif /* PSTEP_TRIPL */

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

/* tree structure */
typedef struct oneedge {
       	  /* pointer to other three edges */
       	  struct oneedge *up;
       	  struct oneedge *downleft;
       	  struct oneedge *downright;
       	  int             numedge;    /* number of edge (index) */
       	  int             taxon;      /* taxon number */
       	  uli             edgeinfo;   /* value of this edge (penalty) */
#	ifdef PSTEP_ORIG
	  int    *edgemap;            /* pointer to the local edgemap array */
#	endif /* PSTEP_ORIG */
#	ifdef PSTEP_SPLIT
	  uli     penalty;            /* penalty of this edge */
	  int    *split;              /* cluster array (up: 0..upsize-1; */
	                              /*   down: maxspc-1..maxspc-downsize */
	  int     upsize;             /* size of root-ward cluster */
	  int     downsize;           /* size of non-root-ward cluster */
#	endif /* PSTEP_SPLIT */
#	ifdef PSTEP_RECUR
	  uli     penalty;         /* penalty of this edge */
	  int    *cluster;         /* leaf cluster of edge (0..clsize-1; */
	  int     clsize;          /* size of non-root-ward cluster */
	  uli     submatrsum;      /* sum of all neibor-matr cluster entries */
	  uli     allsum;          /* sum of all cluster ext. edge penalty */
#	endif /* PSTEP_RECUR */
#	ifdef PSTEP_MRCA
	  uli     penalty;         /* penalty of this edge */
	  uli    *penaltyprop;     /* penalties to propagated to the leaves */
	  int    *cluster;         /* leaf cluster of edge (0..clsize-1; */
	  int     clsize;          /* size of non-root-ward cluster */
#	endif /* PSTEP_MRCA */
#	ifdef PSTEP_TRIPL
	  /* final subtree/path penalty/bonus through this edge */
	  uli     penalty;          /* path penalty of this edge */
	  uli     bonus;            /* path bonus of this edge */ /* not used */
	  uli     stpenalty;        /* subtree penalty of this edge */
	  uli     stbonus;          /* subtree bonus of this edge */
	  uli     stmissing;        /* subtree bonus of this edge */
	  uli     stmax;            /* subtree bonus of this edge */
	  uli     ppenalty;         /* path penalty of this edge */
	  uli     pbonus;           /* path bonus of this edge */
	  uli     pmissing;         /* path bonus of this edge */
	  uli     pmax;             /* path bonus of this edge */
	  /* max possible subtree bonus/penalty = st_penalty+st_bonus) */
	  /* max possible path penalty = #right*#left*(#leaves-2)/2     */
	  /* max possible path bonus = #right * (#left^2 - #left) +    */
	  /*                           #left  * (#right^2- #right)     */
	  /* penalty/bonus to be propagated trough the subtrees */
	  uli     up_stpenalty;     /* bonus to subtree above this edge */
	  uli     down_stpenalty;   /* bonus to subtree below this edge */
	  uli     up_stbonus;       /* bonus to subtree above this edge */
	  uli     down_stbonus;     /* bonus to subtree below this edge */
	  uli     up_stmissing;     /* bonus to subtree above this edge */
	  uli     down_stmissing;   /* bonus to subtree below this edge */
	  /* penalty/bonus to be propagated trough the paths */
	  uli    *ppenaltyarr;      /* bonus to paths */
	  /* uli    *up_ppenalty;   */   /* bonus to paths above this edge */
	  /* uli    *down_ppenalty; */   /* bonus to paths below this edge */
	  uli    *pbonusarr;        /* bonus to paths */
	  /* uli    *up_pbonus;     */   /* bonus to paths above this edge */
	  /* uli    *down_pbonus;   */   /* bonus to paths below this edge */
	  uli    *pmissingarr;      /* bonus to paths */
	  /* uli    *up_pmissing;   */   /* bonus to paths above this edge */
	  /* uli    *down_pmissing; */   /* bonus to paths below this edge */
	  /* house-keeping of splits */
	  int    *split;                /* cluster array (up: 0..upsize-1; */
	                                /*   down: maxspc-1..maxspc-downsize */
	  int     upsize;               /* size of root-ward cluster */
	  int     downsize;             /* size of non-root-ward cluster */
	  /* house-keeping for recursive penalty paths */
	  uli     submatrsum;      /* sum of all neibor-matr cluster entries */
	  uli     allsum;          /* sum of all cluster ext. edge penalty */
#	endif /* PSTEP_TRIPL */
#	ifdef PSTEP_TRIPLBONUS
	  uli     penalty;         /* penalty of this edge */
	  uli     bonus;           /* bonus of this edge */
	  uli     upbonus;         /* bonus to subtree above this edge */
	  uli     downbonus;       /* bonus to subtree below this edge */
	  int    *split;           /* cluster array (up: 0..upsize-1; */
	                           /*   down: maxspc-1..maxspc-downsize */
	  int     upsize;          /* size of root-ward cluster */
	  int     downsize;        /* size of non-root-ward cluster */
	  uli     submatrsum;      /* sum of all neibor-matr cluster entries */
	  uli     allsum;          /* sum of all cluster ext. edge penalty */
#	endif /* PSTEP_TRIPLBONUS */
} ONEEDGE;


/*****************************************************************************/
/* A bit of documentation: (HAS)                                             */
/*****************************************************************************/

/* Tree initialization:
 * --------------------
 *
 * A puzzling step tree is initialized like this:
 *
 *                2
 *         0  +------- C(=2)
 * A(=0) -----+
 *            +------- B(=1)
 *                1
 *
 *
 * more detailed:
 *
 *                 A(=0)
 *                 [up       =NULL]
 *                 [downleft =ptr(1)]
 *                 [downright=ptr(2)]
 *  PSTEP_SPLIT, PSTEP_TRIPL:
 *                 [upcluster=t0]
 *                 [downcluster=t1,t2]
 *                 [upsize=1]
 *                 [downsize=2]
 *  PSTEP_RECUR, PSTEP_MRCA:
 *                 [cluster=t1,t2]
 *                 [clsize=2]
 *
 *                    o
 *                    |
 *                    |
 *      +-------------+--------------+
 *      |                            |
 *      |                            |
 *      o                            o
 *
 *     C(=1)                        B(=2)
 *     [up       =ptr(0)]           [up       =ptr(0)]
 *     [downleft =NULL]             [downleft =NULL]
 *     [downright=NULL]             [downright=NULL]
 * PSTEP_SPLIT, PSTEP_TRIPL:
 *     [upcluster=t0,t2]            [upcluster=t0,t1]
 *     [downcluster=t1]             [downcluster=t2]
 *     [upsize=2]                   [upsize=2]
 *     [downsize=1]                 [downsize=1]
 *  PSTEP_RECUR, PSTEP_MRCA:
 *     [cluster=t1]                 [cluster=t2]
 *     [clsize=1]                   [clsize=1]
 *
 *  PSTEP_MRCA:
 *     MRCA [t0,*]  = 0
 *     MRCA [t1,t2] = 0
 *
 *   nextedge = 3
 *   nextleaf = 3
 *   and set according edge maps     (_orig,  PSTEP_ORIG), 
 *                     split lists   (_split, PSTEP_SPLIT),
 *                     cluster lists (_recur, PSTEP_MRCA),
 *                     MRCA matrix   (_mrca,  PSTEP_MRCA), or
 *                     split lists   (_tripl, PSTEP_TRIPL)
 *
 *
 *
 * Adding leaves to <dockedge> works like this:
 * --------------------------------------------
 *
 *      
 *                                    |
 *                                    | nextedge
 *  |                                 |
 *  | dockedge   insert              /^\
 *  |           ========>           *   *
 * /^\                             /     \
 *                       dockedge /       \ nextedge+1
 *                               /         \ 
 *                              /|         |\ permut[nextleaf]
 *
 *
 * Maximal penalty/bonus values per edge:
 * --------------------------------------
 *
 * #up (#down) = no. of leaves above (below) of current edge
 * #leaves = no. of leaves in current tree (= #up + #down)
 *
 * - max possible subtree bonus or penalty:
 * 	max_subtree_penalty = max_subtree_bonus = st_penalty+st_bonus
 *	(for each triplet each subtree gets either bonus or penalty)
 * - max possible path penalty: 
 *	max_path_penalty = #up*#down * (#leaves-2))     
 *	(number of penalty paths though that edge (= #up*#down) times all 
 *	possible third triplet leaves (= #leaves - 2 penalty path leaves))     
 * - max possible path bonus:
 * 	max_path_bonus = #up * (#down^2 - #down) + #down * (#up^2- #up)     
 * 	(number of bonus comming from above times from below:
 *	 number of end leaves #up or #down times the number of ordered
 *	 pairs on the other side (#down^2 - #down) or (#up^2- #up) )     
 *
 */

/*****************************************************************************/
/* internal functions for representing and building puzzling step trees      */
/*****************************************************************************/

/* writes OTU sitting on edge ed (usage: debugging) */
void writeOTU(FILE    *outfp, 		/* output file          */
              int      ed, 		/* edge to subtree      */
              ONEEDGE *edge, 		/* edge array           */
              int     *edgeofleaf, 	/* ext. edge idx array  */
              int      nextedge, 	/* next free edge idx   */
              int      nextleaf,	/* next free leaf idx   */
              int     *column);		/* current screen depth */

/* write tree (usage: debugging) */
void writetree(FILE   *outfp,              /* output file          */
               ONEEDGE *edge,              /* edge array           */
               int     *edgeofleaf,        /* ext. edge idx array  */
               int      nextedge,          /* next free edge idx   */
               int      nextleaf,          /* next free leaf idx   */
               int      rootleaf);         /* root leaf of tree    */

/* clear all edgeinfos */
void resetedgeinfo(ONEEDGE *edge,      	/* edge array           */
                   int      nextedge);	/* next free edge idx   */

/* checks which edge has the lowest edgeinfo
   if there are several edges with the same lowest edgeinfo,
   one of them will be selected randomly */
void minimumedgeinfo(ONEEDGE *edge,        /* edge array           */
                     int     *edgeofleaf,  /* ext. edge idx array  */
                     int      nextedge,    /* next free edge idx   */
                     int      nextleaf,    /* next free leaf idx   */
	             int     *minedges,
	             int     *out_howmany,
                     int     *out_minedge,     /* minimum edge set     */
                     uli     *out_mininfo);    /* minumum penalty      */

/*******************************************************/
/* routines specific for the original O(n^5) algorithm */
/*******************************************************/

/* initialize tree with the following starting configuration (see above) */
void inittree_orig(ONEEDGE **edge,      /* out: new array of edges          */
                   int     **edgeofleaf,/* out: array of external edge ptrs */
                   int      *rootedge,  /* out: rooting edge (=0)           */
                   int       Maxspc,    /* in:  Number of species (n)       */
                   int       Maxbrnch,  /* in:  Number of branches (2n-3)   */
                   int      *nextedge,  /* out: next free edge index (=3)   */
                   int      *nextleaf,  /* out: next free leaf index (=3)   */
                   int      *trueID);

/* update edgemap */
void updateedgemap_orig(int      dockedge,        /* insert here         */
                        ONEEDGE *edge,            /* edge array          */
                        int      nextedge);       /* next free edge idx  */

/* add next leaf on the specified edge */
void addnextleaf_orig(int      dockedge,        /* insert here         */
                      ONEEDGE *edge,            /* edge array          */
                      int     *edgeofleaf,      /* ext. edge idx array */
                      int      rootedge,	/* uppest edge */
                      int      Maxspc,          /* No. of species      */
                      int      Maxbrnch,        /* No. of branches     */
                      int     *in_nextedge,        /* next free edge idx  */
                      int     *in_nextleaf,        /* next free leaf idx  */
                      ivector  permut);

/* free memory (to be called after inittree) */
void freetree_orig(ONEEDGE *edge,		/* edge array          */
                   int     *edgeofleaf, 	/* ext. edge idx array */
                   int      Maxspc);		/* No. of species      */

/* increment all edgeinfo between leaf A and B */
void incrementedgeinfo_orig(int      A, 	/* start leaf of penalty path */
                            int      B,		/* start leaf of penalty path */
                            ONEEDGE *edge,	  /* edge array           */
                            int     *edgeofleaf); /* ext. edge idx array  */

/*********/

/* perform one single puzzling step to produce one intermediate tree */
void onepstep_orig(                    /* PStep (intermediate) tree topol:   */
         ONEEDGE      **edge,          /*   out: new array of edges          */
         int          **edgeofleaf,    /*   out: array of extern edge ptrs   */
         int           *rootleaf,      /*   out: root leaf in topol, starting perm[0]  */
         unsigned char *quartettopols, /* in: quartetblock with all topols   */
         int            Maxspc,        /* in: Number of species (n)          */
         ivector        permut,        /* in: species permutation (trueID)   */
         int       rootquartsonly_optn); /* in: use root quartets only  */



/**********************************************************/
/* routines specific for the split-based O(n^4) algorithm */
/**********************************************************/

/* initialize tree with the following starting configuration (see above) */
void inittree_split(ONEEDGE **edge,      /* out: new array of edges          */
                    int     **edgeofleaf,/* out: array of external edge ptrs */
                    int      *rootedge,  /* out: rooting edge (=0)           */
                    int       Maxspc,    /* in:  Number of species (n)       */
                    int       Maxbrnch,  /* in:  Number of branches (2n-3)   */
                    int      *nextedge,  /* out: next free edge index (=3)   */
                    int      *nextleaf,  /* out: next free leaf index (=3)   */
                    int      *trueID);

#if 0
/* update edgemap */
void updateedgemap_split(int      dockedge,        /* insert here         */
                        ONEEDGE *edge,            /* edge array          */
                        int      nextedge);       /* next free edge idx  */
#endif

/* add next leaf on the specified edge */
void addnextleaf_split(int      dockedge,        /* insert here         */
                       ONEEDGE *edge,            /* edge array          */
                       int     *edgeofleaf,      /* ext. edge idx array */
                       int      rootedge,	/* uppest edge */
                       int      Maxspc,          /* No. of species      */
                       int      Maxbrnch,        /* No. of branches     */
                       int     *in_nextedge,        /* next free edge idx  */
                       int     *in_nextleaf,        /* next free leaf idx  */
                       ivector  permut);

/* free memory (to be called after inittree) */
void freetree_split(ONEEDGE *edge,		/* edge array          */
                    int     *edgeofleaf, 	/* ext. edge idx array */
                    int      Maxspc);		/* No. of species      */

#if 0
/* increment all edgeinfo between leaf A and B */
void incrementedgeinfo_split(int      A, 	/* start leaf of penalty path */
                            int      B,		/* start leaf of penalty path */
                            ONEEDGE *edge,	  /* edge array           */
                            int     *edgeofleaf); /* ext. edge idx array  */
#endif

/*********/

/* perform one single puzzling step to produce one intermediate tree */
void onepstep_split(                    /* PStep (intermediate) tree topol:   */
         ONEEDGE      **edge,          /*   out: new array of edges          */
         int          **edgeofleaf,    /*   out: array of extern edge ptrs   */
         int           *rootleaf,      /*   out: root leaf in topol, starting perm[0]  */
         unsigned char *quartettopols, /* in: quartetblock with all topols   */
         int            Maxspc,        /* in: Number of species (n)          */
         ivector        permut,        /* in: species permutation (trueID)   */
         int       rootquartsonly_optn); /* in: use root quartets only  */


/*****************************************************************************/
/* global functions for representing and building puzzling step trees        */
/*****************************************************************************/

/* perform Numtrial single puzzling steps constructing Numtrial intermediate */
/* trees, sort each of them and extract splits for consensus step            */
void allpstep(uli       Numtrial,         /* in: no. psteps to perform       */
              unsigned char *quartetinfo, /* in: quartetblock with all topols*/
              int       Maxspc,           /* in: Number of species (n)       */
              int       fixedorder_optn,  /* in: 'fixed' anchored RNG (debug)*/
              int       rootstart_optn,   /* in: start with root quartets    */
              int       rootquartsonly_optn); /* in: use root quartets only  */

#endif /* PSTEP_H */

#ifdef PSTEP_TRIPL
char *voting_string(int voting);
#endif /* PSTEP_TRIPL */
