#include "phylip.h"
#include "disc.h"

/* Written by Joseph Felsenstein, Jerry Shurman, Hisashi Horino,
   Akiko Fuseki, Sean Lamont, and Andrew Keeffe.

   Copyright (c) 1993-2014, Joseph Felsenstein
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
   POSSIBILITY OF SUCH DAMAGE.
*/

#define FormWide        80   /* width of outfile page */

typedef boolean *aPtr;
typedef long *SpPtr, *ChPtr;

typedef struct vecrec {
  aPtr vec;
  struct vecrec *next;
} vecrec;

typedef vecrec **aDataPtr;
typedef vecrec **Matrix;

#ifndef OLDC
/* function prototypes */
void clique_gnu(vecrec **);
void clique_chuck(vecrec *);
void nunode(node **);
void getoptions(void);
void clique_setuptree(void);
void allocrest(void);
void doinit(void);
void clique_inputancestors(void);
void clique_printancestors(void);
void clique_inputfactors(void);

void inputoptions(void);
void clique_inputdata(void);
boolean Compatible(long, long);
void SetUp(vecrec **);
void Intersect(boolean *, boolean *, boolean *);
long CountStates(boolean *);
void Gen1(long , long, boolean *, boolean *, boolean *);
boolean Ingroupstate(long );
void makeset(void);
void Init(long *, long *, long *, aPtr);

void ChSort(long *, long *, long);
void PrintClique(boolean *);
void bigsubset(long *, long);
void recontraverse(node **, long *, long, long);
void reconstruct(long, long);
void reroot(node *);
void clique_coordinates(node *, long *, long);
void clique_drawline(long);
void clique_printree(void);
void DoAll(boolean *, boolean *, boolean *, long);

void Gen2(long, long, boolean *, boolean *, boolean *);
void GetMaxCliques(vecrec **);
void reallocchars(void);
/* function prototypes */
#endif



Char infilename[FNMLNGTH], outfilename[FNMLNGTH], outtreename[FNMLNGTH], ancfilename[FNMLNGTH], factfilename[FNMLNGTH], weightfilename[FNMLNGTH];
long ActualChars, Cliqmin, outgrno,
       col, ith, msets, setsz;
boolean ancvar, Clmin, Factors, outgropt, trout, weights, noroot, justwts,
               printcomp, progress, treeprint, mulsets, firstset;
long nodes;

aPtr ancone;
Char *Factor;
long *ActChar, *oldweight;
aDataPtr Data;
Matrix Comp;            /* the character compatibility matrix      */
node *root;
long **grouping;
pointptr treenode;   /* pointers to all nodes in tree              */
vecrec *garbage;

/* these variables are to DoAll in the pascal Version. */
 aPtr aChars;
 boolean *Rarer;
 long n, MaxChars;
 SpPtr SpOrder;
 ChPtr ChOrder;

/* variables for GetMaxCliques: */
 vecrec **Comp2;
 long tcount;
 aPtr Temp, Processed, Rarer2;


void clique_gnu(vecrec **p)
{  /* this and the following are do-it-yourself garbage collectors.
     Make a new node or pull one off the garbage list */

 if (garbage != NULL) {
    *p = garbage;
    garbage = garbage->next;
  } else {
    *p = (vecrec *)Malloc((long)sizeof(vecrec));
    (*p)->vec = (aPtr)Malloc((long)chars*sizeof(boolean));
  }
  (*p)->next = NULL;
}  /* clique_gnu */


void clique_chuck(vecrec *p)
{  /* collect garbage on p -- put it on front of garbage list */

 p->next = garbage;
  garbage = p;
}  /* clique_chuck */


void nunode(node **p)
{  /* replacement for NEW */
  *p = (node *)Malloc((long)sizeof(node));
  (*p)->next = NULL;
  (*p)->tip = false;
}  /* nunode */


void getoptions(void)
{
  /* interactively set options */
  long loopcount, loopcount2;
  Char ch, ch2;
  boolean done;

  fprintf(outfile, "\nLargest clique program, version %s\n\n",VERSION);
  putchar('\n');
  ancvar = false;
  Clmin = false;
  Factors = false;
  outgrno = 1;
  outgropt = false;
  trout = true;
  weights = false;
  justwts = false;
  printdata = false;
  printcomp = false;
  progress = true;
  treeprint = true;
  loopcount = 0;
  do {
    cleerhome();
    printf("\nLargest clique program, version %s\n\n",VERSION);
    printf("Settings for this run:\n");
    printf("  A   Use ancestral states in input file?  %s\n",
           (ancvar ? "Yes" : "No"));
    printf("  F              Use factors information?  %s\n",
           Factors ? "Yes" : "No");
    printf("  W                       Sites weighted?  %s\n",
           (weights ? "Yes" : "No"));
    printf("  C          Specify minimum clique size?");
    if (Clmin)
      printf("  Yes, at size%3ld\n", Cliqmin);
    else
      printf("  No\n");
    printf("  O                        Outgroup root?  %s%3ld\n",
           (outgropt ? "Yes, at species number" :
                       "No, use as outgroup species"),outgrno);
    printf("  M           Analyze multiple data sets?");
    if (mulsets)
      printf("  Yes, %2ld %s\n", msets,
             (justwts ? "sets of weights" : "data sets"));
    else
      printf("  No\n");
    printf("  0   Terminal type (IBM PC, ANSI, none)?  %s\n",
           ibmpc ? "IBM PC" : ansi  ? "ANSI"   : "(none)");
    printf("  1    Print out the data at start of run  %s\n",
           (printdata ? "Yes" : "No"));
    printf("  2  Print indications of progress of run  %s\n",
           (progress ? "Yes" : "No"));
    printf("  3        Print out compatibility matrix  %s\n",
           (printcomp ? "Yes" : "No"));
    printf("  4                        Print out tree  %s\n",
           (treeprint ? "Yes" : "No"));
    printf("  5       Write out trees onto tree file?  %s\n",
           (trout ? "Yes" : "No"));
    if(weights && justwts){
        printf(
         "WARNING:  W option and Multiple Weights options are both on.  ");
        printf(
         "The W menu option is unnecessary and has no additional effect. \n");
    }
    printf("\n  Y to accept these or type the letter for one to change\n");
#ifdef WIN32
    phyFillScreenColor();
#endif
    fflush(stdout);
    scanf("%c%*[^\n]", &ch);
    getchar();
    uppercase(&ch);
    done = (ch == 'Y');
    if (!done) {
      if (strchr("OACMFW012345",ch) != NULL){
        switch (ch) {

        case 'A':
          ancvar = !ancvar;
          break;

        case 'F':
          Factors = !Factors;
          break;

        case 'W':
          weights = !weights;
          break;

        case 'C':
          Clmin = !Clmin;
          if (Clmin) {
            loopcount2 = 0;
            do {
              printf("Minimum clique size:\n");
#ifdef WIN32
              phyFillScreenColor();
#endif
              fflush(stdout);
              scanf("%ld%*[^\n]", &Cliqmin);
              getchar();
              countup(&loopcount2, 10);
            } while (Cliqmin < 0);
          }
          break;

        case 'O':
          outgropt = !outgropt;
          if (outgropt)
            initoutgroup(&outgrno, spp);
          break;

        case 'M':
          mulsets = !mulsets;
          if (mulsets){
            printf("Multiple data sets or multiple weights?");
            loopcount2 = 0;
            do {
              printf(" (type D or W)\n");
#ifdef WIN32
              phyFillScreenColor();
#endif
              fflush(stdout);
              scanf("%c%*[^\n]", &ch2);
              getchar();
              if (ch2 == '\n')
                ch2 = ' ';
              uppercase(&ch2);
              countup(&loopcount2, 10);
            } while ((ch2 != 'W') && (ch2 != 'D'));
            justwts = (ch2 == 'W');
            if (justwts)
              justweights(&msets);
            else
              initdatasets(&msets);
          }
          break;

        case '0':
          initterminal(&ibmpc, &ansi);
          break;

        case '1':
          printdata = !printdata;
          break;

        case '2':
          progress = !progress;
          break;
        
        case '3':
          printcomp = !printcomp;
          break;

        case '4':
          treeprint = !treeprint;
          break;

        case '5':
          trout = !trout;
          break;
        }
      } else
        printf("Not a possible option!\n");
      countup(&loopcount, 100);
    }
  } while (!done);
}  /* getoptions */


void clique_setuptree(void)
{
  /* initialization of tree pointers, variables */
  long i;

  treenode = (pointptr)Malloc((long)spp*sizeof(node *));
  for (i = 0; i < spp; i++) {
    treenode[i] = (node *)Malloc((long)sizeof(node));
    treenode[i]->next = NULL;
    treenode[i]->back = NULL;
    treenode[i]->index = i + 1;
    treenode[i]->tip = false;
  }
}  /* clique_setuptree */


void reallocchars()
{
  long i;
  Comp = (Matrix)Malloc((long)chars*sizeof(vecrec *));
  for (i = 0; i < (chars); i++)
    clique_gnu(&Comp[i]);
  ancone = (aPtr)Malloc((long)chars*sizeof(boolean));
  Factor = (Char *)Malloc((long)chars*sizeof(Char));
  ActChar = (long *)Malloc((long)chars*sizeof(long));
  oldweight = (long *)Malloc((long)chars*sizeof(long));
  weight = (long *)Malloc((long)chars*sizeof(long));
  ActualChars = chars;
  for (i = 1; i <= (chars); i++)
    ActChar[i - 1] = i;
}

void allocrest(void)
{
  long i;

  Data = (aDataPtr)Malloc((long)spp*sizeof(vecrec *));
  for (i = 0; i < (spp); i++)
    clique_gnu(&Data[i]);
  Comp = (Matrix)Malloc((long)chars*sizeof(vecrec *));
  for (i = 0; i < (chars); i++)
    clique_gnu(&Comp[i]);
  setsz = (long)ceil(((double)spp+1.0)/(double)SETBITS);
  ancone = (aPtr)Malloc((long)chars*sizeof(boolean));
  Factor = (Char *)Malloc((long)chars*sizeof(Char));
  ActChar = (long *)Malloc((long)chars*sizeof(long));
  oldweight = (long *)Malloc((long)chars*sizeof(long));
  weight = (long *)Malloc((long)chars*sizeof(long));
  nayme = (naym *)Malloc((long)spp*sizeof(naym));
}  /* allocrest */


void doinit(void)
{
  /* initializes variables */

  inputnumbersold(&spp, &chars, &nonodes, 1);
  getoptions();
  if (printdata)
    fprintf(outfile, "%2ld species, %3ld  characters\n", spp, chars);
  clique_setuptree();
  allocrest();
}  /* doinit */


void clique_inputancestors(void)
{
  /* reads the ancestral states for each character */
  long i;
  Char ch;

  for (i = 0; i < (chars); i++) {
    do {
      if (eoln(ancfile)) 
        scan_eoln(ancfile);
      ch = gettc(ancfile);
      if (ch == '\n')
        ch = ' ';
    } while (ch == ' ');
    switch (ch) {
    
    case '1':
      ancone[i] = true;
      break;

    case '0':
      ancone[i] = false;
      break;
    
    default:
      printf("BAD ANCESTOR STATE: %c AT CHARACTER %4ld\n", ch, i + 1);
      exxit(-1);
    }
  }
  scan_eoln(ancfile);
}  /* clique_inputancestors */


void clique_printancestors(void)
{
  /* print out list of ancestral states */
  long i;

  fprintf(outfile, "Ancestral states:\n");
  for (i = 1; i <= nmlngth + 2; i++)
    putc(' ', outfile);
  for (i = 1; i <= (chars); i++) {
    newline(outfile, i, 55, (long)nmlngth + 1);
    if (ancone[i - 1])
      putc('1', outfile);
    else
      putc('0', outfile);
    if (i % 5 == 0)
      putc(' ', outfile);
  }
  fprintf(outfile, "\n\n");
}  /* clique_printancestors */


void clique_inputfactors(void)
{
  /* reads the factor symbols */
  long i;

  ActualChars = 1;
  for (i = 1; i <= (chars); i++) {
    if (eoln(factfile)) 
      scan_eoln(factfile);
    Factor[i - 1] = gettc(factfile);
    if (i > 1) {
      if (Factor[i - 1] != Factor[i - 2])
        ActualChars++;
    }
    ActChar[i - 1] = ActualChars;
  }
  scan_eoln(factfile);
}  /* clique_inputfactors */


void inputoptions(void)
{
  /* reads the species names and character data */
  long i;
  if(justwts){
    if (!firstset)
      samenumsp(&chars, ith);
    if(firstset){
      ActualChars = chars;
      for (i = 1; i <= (chars); i++)
        ActChar[i - 1] = i;
      scan_eoln(infile);
    } else reallocchars();

    for (i = 0; i < (chars); i++)
      oldweight[i] = 1;
    inputweights(chars, oldweight, &weights);
    if(firstset && ancvar)
      clique_inputancestors();
    if(firstset && Factors)
      clique_inputfactors();
    if (printdata)
      printweights(outfile, 0, ActualChars, oldweight, "Characters");
    if (Factors)
      printfactors(outfile, chars, Factor, "");
    if (firstset && ancvar && printdata)
      clique_printancestors();
    noroot = !(outgropt || ancvar);
  } else {
    ActualChars = chars;
    for (i = 1; i <= (chars); i++)
      ActChar[i - 1] = i;
    for (i = 0; i < (chars); i++)
      oldweight[i] = 1;
    scan_eoln(infile);
    if(weights)
      inputweights(chars, oldweight, &weights);
    if(ancvar)
      clique_inputancestors();
    if(Factors)
      clique_inputfactors();
    if (weights && printdata)
      printweights(outfile, 0, ActualChars, oldweight, "Characters");
    if (Factors)
      printfactors(outfile, chars, Factor, "");
    if (ancvar && printdata)
      clique_printancestors();
    noroot = !(outgropt || ancvar);
  }
} /* inputoptions */


void clique_inputdata(void)
{
  long i, j;
  Char ch;

  j = chars / 2 + (chars / 5 - 1) / 2 - 5;
  if (j < 0)
    j = 0;
  if (j > 27)
    j = 27;
  if (printdata) {
    fprintf(outfile, "Species  ");
    for (i = 1; i <= j; i++)
      putc(' ', outfile);
    fprintf(outfile, "Character states\n");
    fprintf(outfile, "-------  ");
    for (i = 1; i <= j; i++)
      putc(' ', outfile);
    fprintf(outfile, "--------- ------\n\n");
  }
  for (i = 0; i < (spp); i++) {
    initname(i);
    if (printdata)
      for (j = 0; j < nmlngth; j++)
        putc(nayme[i][j], outfile);
    if (printdata)
      fprintf(outfile, "  ");
    for (j = 1; j <= (chars); j++) {
      do {
        if (eoln(infile)) 
          scan_eoln(infile);
        ch = gettc(infile);
      } while (ch == ' ' || ch == '\t');
      if (printdata) {
        putc(ch, outfile);
        newline(outfile, j, 55, (long)nmlngth + 1);
        if (j % 5 == 0)
          putc(' ', outfile);
      }
      if (ch != '0' && ch != '1') {
        printf("\n\nERROR: Bad character state: %c (not 0 or 1)", ch);
        printf(" at character %ld of species %ld\n\n", j, i + 1);
        exxit(-1);
      }
      Data[i]->vec[j - 1] = (ch == '1');
    }
    scan_eoln(infile);
    if (printdata)
      putc('\n', outfile);
  }
  putc('\n', outfile);
  for (i = 0; i < (chars); i++) {
    if (i + 1 == 1 || !Factors)
      weight[i] = oldweight[i];
    else if (Factor[i] != Factor[i - 1])
      weight[ActChar[i] - 1] = oldweight[i];
  }
}  /* clique_inputdata */


boolean Compatible(long ch1, long ch2)
{
  /* TRUE if two characters ch1 < ch2 are compatible */
  long i, j, k;
  boolean Compt, Done1, Done2;
  boolean Info[4];

  Compt = true;
  j = 1;
  while (ch1 > ActChar[j - 1])
    j++;
  Done1 = (ch1 != ActChar[j - 1]);
  while (!Done1) {
    k = j;
    while (ch2 > ActChar[k - 1])
      k++;
    Done2 = (ch2 != ActChar[k - 1]);
    while (!Done2) {
      for (i = 0; i <= 3; i++)
        Info[i] = false;
      if (ancvar) {
        if (ancone[j - 1] && ancone[k - 1])
          Info[0] = true;
        else if (ancone[j - 1] && !ancone[k - 1])
          Info[1] = true;
        else if (!ancone[j - 1] && ancone[k - 1])
          Info[2] = true;
        else
          Info[3] = true;
      }
      for (i = 0; i < (spp); i++) {
        if (Data[i]->vec[j - 1] && Data[i]->vec[k - 1])
          Info[0] = true;
        else if (Data[i]->vec[j - 1] && !Data[i]->vec[k - 1])
          Info[1] = true;
        else if (!Data[i]->vec[j - 1] && Data[i]->vec[k - 1])
          Info[2] = true;
        else
          Info[3] = true;
      }
      Compt = (Compt && !(Info[0] && Info[1] && Info[2] && Info[3]));
      k++;
      Done2 = (k > chars);
      if (!Done2)
        Done2 = (ch2 != ActChar[k - 1]);
    }
    j++;
    Done1 = (j > chars);
    if (!Done1)
      Done1 = (ch1 != ActChar[j - 1]);
  }
  return Compt;
}  /* Compatible */


void SetUp(vecrec **Comp)
{
  /* sets up the compatibility matrix */
  long i, j;

  if (printcomp) {
    if (Factors)
      fprintf(outfile, "      (For original multistate characters)\n");
    fprintf(outfile, "Character Compatibility Matrix (1 if compatible)\n");
    fprintf(outfile, "--------- ------------- ------ -- -- -----------\n\n");
  }
  for (i = 0; i < (ActualChars); i++) {
    if (printcomp) {
      for (j = 1; j <= ((48 - ActualChars) / 2); j++)
        putc(' ', outfile);
      for (j = 1; j < i + 1; j++) {
        if (Comp[i]->vec[j - 1])
          putc('1', outfile);
        else
          putc('.', outfile);
        newline(outfile, j, 70, (long)nmlngth + 1);
      }
    }
    Comp[i]->vec[i] = true;
    if (printcomp)
      putc('1', outfile);
    for (j = i + 1; j < (ActualChars); j++) {
      Comp[i]->vec[j] = Compatible(i + 1, j + 1);
      if (printcomp) {
        if (Comp[i]->vec[j])
          putc('1', outfile);
        else
          putc('.', outfile);
      }
      Comp[j]->vec[i] = Comp[i]->vec[j];
    }
    if (printcomp)
      putc('\n', outfile);
  }
  putc('\n', outfile);
}  /* SetUp */


void Intersect(boolean *V1, boolean *V2, boolean *V3)
{
  /* takes the logical intersection V1 AND V2 */
  long i;

  for (i = 0; i < (ActualChars); i++)
    V3[i] = (V1[i] && V2[i]);
}  /* Intersect */


long CountStates(boolean *V)
{
  /* counts the 1's in V */
  long i, TempCount;

  TempCount = 0;
  for (i = 0; i < (ActualChars); i++) {
    if (V[i])
      TempCount += weight[i];
  }
  return TempCount;
}  /* CountStates */


void Gen1(long i, long CurSize, boolean *aChars, boolean *Candidates,
                        boolean *Excluded)
{
  /* finds largest size cliques and prints them out */
  long CurSize2, j, k, Actual, Possible;
  boolean Futile;
  vecrec *Chars2, *Cands2, *Excl2, *Cprime, *Exprime;

  clique_gnu(&Chars2);
  clique_gnu(&Cands2);
  clique_gnu(&Excl2);
  clique_gnu(&Cprime);
  clique_gnu(&Exprime);
  CurSize2 = CurSize;
  memcpy(Chars2->vec, aChars, chars*sizeof(boolean));
  memcpy(Cands2->vec, Candidates, chars*sizeof(boolean));
  memcpy(Excl2->vec, Excluded, chars*sizeof(boolean));
  j = i;
  while (j <= ActualChars) {
    if (Cands2->vec[j - 1]) {
      Chars2->vec[j - 1] = true;
      Cands2->vec[j - 1] = false;
      CurSize2 += weight[j - 1];
      Possible = CountStates(Cands2->vec);
      Intersect(Cands2->vec, Comp2[j - 1]->vec, Cprime->vec);
      Actual = CountStates(Cprime->vec);
      Intersect(Excl2->vec, Comp2[j - 1]->vec, Exprime->vec);
      Futile = false;
      for (k = 0; k <= j - 2; k++) {
        if (Exprime->vec[k] && !Futile) {
          Intersect(Cprime->vec, Comp2[k]->vec, Temp);
          Futile = (CountStates(Temp) == Actual);
        }
      }
      if (CurSize2 + Actual >= Cliqmin && !Futile) {
        if (Actual > 0)
          Gen1(j + 1,CurSize2,Chars2->vec,Cprime->vec,Exprime->vec);
        else if (CurSize2 > Cliqmin) {
          Cliqmin = CurSize2;
          if (tcount >= 0)
            tcount = 1;
        } else if (CurSize2 == Cliqmin)
          tcount++;
      }
      if (Possible > Actual) {
        Chars2->vec[j - 1] = false;
        Excl2->vec[j - 1] = true;
        CurSize2 -= weight[j - 1];
      } else
        j = ActualChars;
    }
    j++;
  }
  clique_chuck(Chars2);
  clique_chuck(Cands2);
  clique_chuck(Excl2);
  clique_chuck(Cprime);
  clique_chuck(Exprime);
}  /* Gen1 */


boolean Ingroupstate(long i)
{
  /* the ingroup state for the i-th character */
  boolean outstate;

  if (noroot) {
    outstate = Data[0]->vec[i - 1];
    return (!outstate);
  }
  if (ancvar)
    outstate = ancone[i - 1];
  else
    outstate = Data[outgrno - 1]->vec[i - 1];
  return (!outstate);
}  /* Ingroupstate */


void makeset(void)
{
  /* make up set of species for given set of characters */
  long i, j, k, m;
  boolean instate;
  long *st;

  st = (long *)Malloc(setsz*sizeof(long));
  n = 0;
  for (i = 0; i < (MaxChars); i++) {
    for (j = 0; j < setsz; j++)
      st[j] = 0;
    instate = Ingroupstate(ChOrder[i]);
    for (j = 0; j < (spp); j++) {
      if (Data[SpOrder[j] - 1]->vec[ChOrder[i] - 1] == instate) {
        m = (long)(SpOrder[j]/SETBITS);
        st[m] = ((long)st[m]) | (1L << (SpOrder[j] % SETBITS));
      }
    }
    memcpy(grouping[++n - 1], st, setsz*sizeof(long));
  }
  for (i = 0; i < (spp); i++) {
    k = (long)(SpOrder[i]/SETBITS);
    grouping[++n - 1][k] = 1L << (SpOrder[i] % SETBITS);
  }
  free(st);
}  /* makeset */


void Init(long *ChOrder, long *Count, long *MaxChars, aPtr aChars)
{
  /* initialize vectors and character count */
  long i, j, temp;
  boolean instate;

  *MaxChars = 0;
  for (i = 1; i <= (chars); i++) {
    if (aChars[ActChar[i - 1] - 1]) {
      (*MaxChars)++;
      ChOrder[*MaxChars - 1] = i;
      instate = Ingroupstate(i);
      temp = 0;
      for (j = 0; j < (spp); j++) {
        if (Data[j]->vec[i - 1] == instate)
          temp++;
      }
      Count[i - 1] = temp;
    }
  }
}  /*Init */


void ChSort(long *ChOrder, long *Count, long MaxChars)
{
  /* sorts the characters by number of ingroup states */
  long j, temp;
  boolean ordered;

  ordered = false;
  while (!ordered) {
    ordered = true;
    for (j = 1; j < MaxChars; j++) {
      if (Count[ChOrder[j - 1] - 1] < Count[ChOrder[j] - 1]) {
        ordered = false;
        temp = ChOrder[j - 1];
        ChOrder[j - 1] = ChOrder[j];
        ChOrder[j] = temp;
      }
    }
  }
}  /* ChSort */


void PrintClique(boolean *aChars)
{
  /* prints the characters in a clique */
  long i, j;
  fprintf(outfile, "\n\n");
  if (Factors) {
    fprintf(outfile, "Actual Characters: (");
    j = 0;
    for (i = 1; i <= (ActualChars); i++) {
      if (aChars[i - 1]) {
        fprintf(outfile, "%3ld", i);
        j++;
        newline(outfile, j, (long)((FormWide - 22) / 3),
                                (long)nmlngth + 1);
      }
    }
    fprintf(outfile, ")\n");
  }
  if (Factors)
    fprintf(outfile, "Binary ");
  fprintf(outfile, "Characters: (");
  j = 0;
  for (i = 1; i <= (chars); i++) {
    if (aChars[ActChar[i - 1] - 1]) {
      fprintf(outfile, "%3ld", i);
      j++;
      if (Factors)
        newline(outfile, j, (long)((FormWide - 22) / 3), 
                                (long)nmlngth + 1);
      else
        newline(outfile, j, (long)((FormWide - 15) / 3), 
                                (long)nmlngth + 1);
    }
  }
  fprintf(outfile, ")\n\n");
}  /* PrintClique */


void bigsubset(long *st, long n)
{
  /* find a maximal subset of st among the groupings */
  long i, j;
  long *su;
  boolean max, same;

  su = (long *)Malloc(setsz*sizeof(long));
  for (i = 0; i < setsz; i++)
    su[i] = 0;
  for (i = 0; i < n; i++) {
    max = true;
    for (j = 0; j < setsz; j++)
      if ((grouping[i][j] & ~st[j]) != 0)
       max = false;
    if (max) {
      same = true;
      for (j = 0; j < setsz; j++)
        if (grouping[i][j] != st[j])
          same = false;
      if (!same) {
        for (j = 0; j < setsz; j++)
          if ((su[j] & ~grouping[i][j]) != 0)
            max = false;
        if (max) {
          same = true;
          for (j = 0; j < setsz; j++)
            if (grouping[i][j] != su[j])
              same = false;
          if (!same)
            memcpy(su, grouping[i], setsz*sizeof(long));
        }
      }
    }
  }
  memcpy(st, su, setsz*sizeof(long));
  free(su);
}  /* bigsubset */


void recontraverse(node **p, long *st, long n, long MaxChars)
{
  /* traverse to reconstruct the tree from the characters */
  long i, j, k, maxpos;
  long *tempset, *st2;
  boolean found, zero, zero2, same;
  node *q;

  j = k = 0;
  for (i = 1; i <= (spp); i++) {
    if (((1L << (i % SETBITS)) & st[(long)(i / SETBITS)]) != 0) {
      k++;
      j = i;
    }
  }
  if (k == 1) {
    *p = treenode[j - 1];
    (*p)->tip = true;
    (*p)->index = j;
    return;
  }
  nunode(p);
  (*p)->index = 0;
  tempset = (long*)Malloc(setsz*sizeof(long));
  memcpy(tempset, st, setsz*sizeof(long));
  q = *p;
  zero = true;
  for (i = 0; i < setsz; i++)
    if (tempset[i] != 0)
      zero = false;
  if (!zero)
    bigsubset(tempset, n);
  zero = true;
  zero2 = true;
  for (i = 0; i < setsz; i++)
    if (st[i] != 0)
      zero = false;
  if (!zero) {
    for (i = 0; i < setsz; i++)
      if (tempset[i] != 0)
        zero2 = false;
  }
  st2 = (long *)Malloc(setsz*sizeof(long));
  memcpy(st2, st, setsz*sizeof(long));
  while (!zero2) {
    nunode(&q->next);
    q = q->next;
    recontraverse(&q->back, tempset, n,MaxChars);
    i = 1;
    maxpos = 0;
    while (i <= MaxChars) {
      same = true;
      for (j = 0; j < setsz; j++)
        if (grouping[i - 1][j] != tempset[j])
          same = false;
      if (same)
        maxpos = i;
      i++;
    }
    q->back->maxpos = maxpos;
    q->back->back = q;
    for (j = 0; j < setsz; j++)
      st2[j] &= ~tempset[j];
    memcpy(tempset, st2, setsz*sizeof(long));
    found = false;
    i = 1;
    while (!found && i <= n) {
      same = true;
      for (j = 0; j < setsz; j++)
        if (grouping[i - 1][j] != tempset[j])
          same = false;
      if (same)
        found = true;
      else
        i++;
    }
    zero = true;
    for (j = 0; j < setsz; j++)
      if (tempset[j] != 0)
        zero = false;
    if (!zero && !found)
      bigsubset(tempset, n);
    zero = true;
    zero2 = true;
    for (j = 0; j < setsz; j++)
      if (st2[j] != 0)
        zero = false;
    if (!zero)
      for (j = 0; j < setsz; j++)
        if (tempset[j] != 0)
          zero2 = false;
}
  q->next = *p;
  free(tempset);
  free(st2);
}  /* recontraverse */


void reconstruct(long n, long MaxChars)
{  /* reconstruct tree from the subsets */
  long i;
  long *s;
  s = (long *)Malloc(setsz*sizeof(long));
  for (i = 0; i < setsz; i++) {
    if (i+1 == setsz) {
      s[i] = 1L << ((spp % SETBITS) + 1);
      if (setsz > 1)
        s[i] -= 1;
      else s[i] -= 1L << 1;
    }
    else if (i == 0) {
      if (setsz > 1)
        s[i] = ~0L - 1;
    }
    else {
      if (setsz > 2)
        s[i] = ~0L;
    }
  }
  recontraverse(&root,s,n,MaxChars);
  free(s);
}  /* reconstruct */


void reroot(node *outgroup)
{
  /* reorients tree, putting outgroup in desired position. */
  long i;
  boolean nroot;
  node *p, *q;

  nroot = false;
  p = root->next;
  while (p != root) {
    if (outgroup->back == p) {
      nroot = true;
      p = root;
    } else
      p = p->next;
  }
  if (nroot)
    return;
  p = root;
  i = 0;
  while (p->next != root) {
    p = p->next;
    i++;
  }
  if (i == 2) {
    root->next->back->back = p->back;
    p->back->back = root->next->back;
    q = root->next;
  } else {
    p->next = root->next;
    nunode(&root->next);
    q = root->next;
    nunode(&q->next);
    p = q->next;
    p->next = root;
    q->tip = false;
    p->tip = false;
  }
  q->back = outgroup;
  p->back = outgroup->back;
  outgroup->back->back = p;
  outgroup->back = q;
}  /* reroot */


void clique_coordinates(node *p, long *tipy, long MaxChars)
{
  /* establishes coordinates of nodes */
  node *q, *first, *last;
  long maxx;

  if (p->tip) {
    p->xcoord = 0;
    p->ycoord = *tipy;
    p->ymin = *tipy;
    p->ymax = *tipy;
    (*tipy) += down;
    return;
  }
  q = p->next;
  maxx = 0;
  while (q != p) {
    clique_coordinates(q->back, tipy, MaxChars);
    if (!q->back->tip) {
      if (q->back->xcoord > maxx)
        maxx = q->back->xcoord;
    }
    q = q->next;
  }
  first = p->next->back;
  q = p;
  while (q->next != p)
    q = q->next;
  last = q->back;
  p->xcoord = (MaxChars - p->maxpos) * 3 - 2;
  if (p == root)
    p->xcoord += 2;
  p->ycoord = (first->ycoord + last->ycoord) / 2;
  p->ymin = first->ymin;
  p->ymax = last->ymax;
}  /* clique_coordinates */


void clique_drawline(long i)
{
  /* draws one row of the tree diagram by moving up tree */
  node *p, *q;
  long n, m, j, k, l, sumlocpos, size, locpos, branchpos;
  long *poslist;
  boolean extra, done, plus, found, same;
  node *r, *first = NULL, *last = NULL;

  poslist = (long *)Malloc((long)(spp + MaxChars)*sizeof(long));
  branchpos = 0;
  p = root;
  q = root;
  fprintf(outfile, "  ");
  extra = false;
  plus = false;
  do {
    if (!p->tip) {
      found = false;
      r = p->next;
      while (r != p && !found) {
        if (i >= r->back->ymin && i <= r->back->ymax) {
          q = r->back;
          found = true;
        } else
          r = r->next;
      }
      first = p->next->back;
      r = p;
      while (r->next != p)
        r = r->next;
      last = r->back;
    }
    done = (p->tip || p == q);
    n = p->xcoord - q->xcoord;
    m = n;
    if (extra) {
      n--;
      extra = false;
    }
    if ((long)q->ycoord == i && !done) {
      if (!q->tip) {
        putc('+', outfile);
        plus = true;
        j = 1;
        for (k = 1; k <= (q->maxpos); k++) {
          same = true;
          for (l = 0; l < setsz; l++)
            if (grouping[k - 1][l] != grouping[q->maxpos - 1][l])
              same = false;
          if (same) {
            poslist[j - 1] = k;
            j++;
          }
        }
        size = j - 1;
        if (size == 0) {
          for (k = 1; k < n; k++)
            putc('-', outfile);
          sumlocpos = n;
        } else {
          sumlocpos = 0;
          j = 1;
          while (j <= size) {
            locpos = poslist[j - 1] * 3;
            if (j != 1)
              locpos -= poslist[j - 2] * 3;
            else
              locpos -= branchpos;
            for (k = 1; k < locpos; k++)
              putc('-', outfile);
            if (Rarer[ChOrder[poslist[j - 1] - 1] - 1])
              putc('1', outfile);
            else
              putc('0', outfile);
            sumlocpos += locpos;
            j++;
          }
          for (j = sumlocpos + 1; j < n; j++)
            putc('-', outfile);
          putc('+', outfile);
          if (m > 0)
            branchpos += m;
          extra = true;
        }
      } else {
        if (!plus) {
          putc('+', outfile);
          plus = false;
        } else
          n++;
        j = 1;
        for (k = 1; k <= (q->maxpos); k++) {
          same = true;
          for (l = 0; l < setsz; l++)
             if (grouping[k - 1][l] != grouping[q->maxpos - 1][l])
              same = false;
          if (same) {
            poslist[j - 1] = k;
            j++;
          }
        }
        size = j - 1;
        if (size == 0) {
          for (k = 1; k <= n; k++)
            putc('-', outfile);
          sumlocpos = n;
        } else {
          sumlocpos = 0;
          j = 1;
          while (j <= size) {
            locpos = poslist[j - 1] * 3;
            if (j != 1)
              locpos -= poslist[j - 2] * 3;
            else
              locpos -= branchpos;
            for (k = 1; k < locpos; k++)
              putc('-', outfile);
            if (Rarer[ChOrder[poslist[j - 1] - 1] - 1])
              putc('1', outfile);
            else
              putc('0', outfile);
            sumlocpos += locpos;
            j++;
          }
          for (j = sumlocpos + 1; j <= n; j++)
            putc('-', outfile);
          if (m > 0)
            branchpos += m;
        }
        putc('-', outfile);
      }
    } else if (!p->tip && (long)last->ycoord > i && (long)first->ycoord < i &&
               (i != (long)p->ycoord || p == root)) {
      putc('!', outfile);
      for (j = 1; j < n; j++)
        putc(' ', outfile);
      plus = false;
      if (m > 0)
        branchpos += m;
    } else {
      for (j = 1; j <= n; j++)
        putc(' ', outfile);
      plus = false;
      if (m > 0)
        branchpos += m;
    }
    if (q != p)
      p = q;
  } while (!done);
  if (p->ycoord == i && p->tip) {
    for (j = 0; j < nmlngth; j++)
      putc(nayme[p->index - 1][j], outfile);
}
  putc('\n', outfile);
  free(poslist);
}  /* clique_drawline */


void clique_printree(void)
{
  /* prints out diagram of the tree */
  long tipy, i;

  if (!treeprint)
    return;
  tipy = 1;
  clique_coordinates(root, &tipy, MaxChars);
  fprintf(outfile, "\n  Tree and");
  if (Factors)
    fprintf(outfile, " binary");
  fprintf(outfile, " characters:\n\n");
  fprintf(outfile, "   ");
  for (i = 0; i < (MaxChars); i++)
    fprintf(outfile, "%3ld", ChOrder[i]);
  fprintf(outfile, "\n   ");
  for (i = 0; i < (MaxChars); i++) {
    if (Rarer[ChOrder[i] - 1])
      fprintf(outfile, "%3c", '1');
    else
      fprintf(outfile, "%3c", '0');
  }
  fprintf(outfile, "\n\n");
  for (i = 1; i <= (tipy - down); i++)
    clique_drawline(i);
  fprintf(outfile, "\nremember: this is an unrooted tree!\n\n");
}  /* clique_printree */


void DoAll(boolean *Chars_,boolean *Processed,boolean *Rarer_,long tcount)
{
  /* print out a clique and its tree */
  long i, j;
  ChPtr Count;

  aChars = (aPtr)Malloc((long)chars*sizeof(boolean));
  SpOrder = (SpPtr)Malloc((long)spp*sizeof(long));
  ChOrder = (ChPtr)Malloc((long)chars*sizeof(long));
  Count = (ChPtr)Malloc((long)chars*sizeof(long));
  memcpy(aChars, Chars_, chars*sizeof(boolean));
  Rarer = Rarer_;
  Init(ChOrder, Count, &MaxChars, aChars);
  ChSort(ChOrder, Count, MaxChars);
  for (i = 1; i <= (spp); i++)
    SpOrder[i - 1] = i;
  for (i = 1; i <= (chars); i++) {
    if (aChars[ActChar[i - 1] - 1]) {
      if (!Processed[ActChar[i - 1] - 1]) {
        Rarer[i - 1] = Ingroupstate(i);
        Processed[ActChar[i - 1] - 1] = true;
      }
    }
  }
  PrintClique(aChars);
  grouping = (long **)Malloc((long)(spp + MaxChars)*sizeof(long *));
  for (i = 0; i < spp + MaxChars; i++) {
    grouping[i] = (long *)Malloc(setsz*sizeof(long));
    for (j = 0; j < setsz; j++)
      grouping[i][j] = 0;
  }
  makeset();
  clique_setuptree();
  reconstruct(n,MaxChars);
  if (noroot)
    reroot(treenode[outgrno - 1]);
  clique_printree();
  if (trout) {
    col = 0;
    treeout(root, tcount+1, &col, root);
  }
  free(SpOrder);
  free(ChOrder);
  free(Count);
  for (i = 0; i < spp + MaxChars; i++)
    free(grouping[i]);
  free(grouping);
}  /* DoAll */


void Gen2(long i, long CurSize, boolean *aChars, boolean *Candidates,
                        boolean *Excluded)
{
  /* finds largest size cliques and prints them out */
  long CurSize2, j, k, Actual, Possible;
  boolean Futile;
  vecrec *Chars2, *Cands2, *Excl2, *Cprime, *Exprime;

  clique_gnu(&Chars2);
  clique_gnu(&Cands2);
  clique_gnu(&Excl2);
  clique_gnu(&Cprime);
  clique_gnu(&Exprime);
  CurSize2 = CurSize;
  memcpy(Chars2->vec, aChars, chars*sizeof(boolean));
  memcpy(Cands2->vec, Candidates, chars*sizeof(boolean));
  memcpy(Excl2->vec, Excluded, chars*sizeof(boolean));
  j = i;
  while (j <= ActualChars) {
    if (Cands2->vec[j - 1]) {
      Chars2->vec[j - 1] = true;
      Cands2->vec[j - 1] = false;
      CurSize2 += weight[j - 1];
      Possible = CountStates(Cands2->vec);
      Intersect(Cands2->vec, Comp2[j - 1]->vec, Cprime->vec);
      Actual = CountStates(Cprime->vec);
      Intersect(Excl2->vec, Comp2[j - 1]->vec, Exprime->vec);
      Futile = false;
      for (k = 0; k <= j - 2; k++) {
        if (Exprime->vec[k] && !Futile) {
          Intersect(Cprime->vec, Comp2[k]->vec, Temp);
          Futile = (CountStates(Temp) == Actual);
        }
      }
      if (CurSize2 + Actual >= Cliqmin && !Futile) {
        if (Actual > 0)
          Gen2(j + 1,CurSize2,Chars2->vec,Cprime->vec,Exprime->vec);
        else
          DoAll(Chars2->vec,Processed,Rarer2,tcount);
      }
      if (Possible > Actual) {
        Chars2->vec[j - 1] = false;
        Excl2->vec[j - 1] = true;
        CurSize2 -= weight[j - 1];
      } else
        j = ActualChars;
    }
    j++;
  }
  clique_chuck(Chars2);
  clique_chuck(Cands2);
  clique_chuck(Excl2);
  clique_chuck(Cprime);
  clique_chuck(Exprime);
}  /* Gen2 */


void GetMaxCliques(vecrec **Comp_)
{
  /* recursively generates the largest cliques */
  long i;
  aPtr aChars, Candidates, Excluded;

  Temp = (aPtr)Malloc((long)chars*sizeof(boolean));
  Processed = (aPtr)Malloc((long)chars*sizeof(boolean));
  Rarer2 = (aPtr)Malloc((long)chars*sizeof(boolean));
  aChars = (aPtr)Malloc((long)chars*sizeof(boolean));
  Candidates = (aPtr)Malloc((long)chars*sizeof(boolean));
  Excluded = (aPtr)Malloc((long)chars*sizeof(boolean));
  Comp2 = Comp_;
  putc('\n', outfile);
  if (Clmin) {
    fprintf(outfile, "Cliques with at least%3ld characters\n", Cliqmin);
    fprintf(outfile, "------- ---- -- ----- -- ----------\n");
  } else {
    Cliqmin = 0;
    fprintf(outfile, "Largest Cliques\n");
    fprintf(outfile, "------- -------\n");
    for (i = 0; i < (ActualChars); i++) {
      aChars[i] = false;
      Excluded[i] = false;
      Candidates[i] = true;
    }
    tcount = 0;
    Gen1(1, 0, aChars, Candidates, Excluded);
  }
  for (i = 0; i < (ActualChars); i++) {
    aChars[i] = false;
    Candidates[i] = true;
    Processed[i] = false;
    Excluded[i] = false;
  }
  Gen2(1, 0, aChars, Candidates, Excluded);
  putc('\n', outfile);
  free(Temp);
  free(Processed);
  free(Rarer2);
  free(aChars);
  free(Candidates);
  free(Excluded);
}  /* GetMaxCliques */


int main(int argc, Char *argv[])
{  /* Main Program */
#ifdef MAC
   argc = 1;                /* macsetup("Clique","Clique");                */
   argv[0] = "Clique";
#endif
  init(argc, argv);
  openfile(&infile,INFILE,"input file", "r",argv[0],infilename);
  openfile(&outfile,OUTFILE,"output file", "w",argv[0],outfilename);
  openfile(&outtree,OUTTREE,"output tree file", "w",argv[0],outtreename);
  ibmpc = IBMCRT;
  ansi = ANSICRT;
  mulsets = false;
  firstset = true;
  msets = 1;
  doinit();
  if(ancvar)
    openfile(&ancfile,ANCFILE,"ancestors file", "r",argv[0],ancfilename);
  if(Factors)
      openfile(&factfile,FACTFILE,"factors file", "r",argv[0],factfilename);
  if (weights || justwts)
    openfile(&weightfile,WEIGHTFILE,"weights file","r",argv[0],weightfilename);
  for (ith = 1; ith <= (msets); ith++) {
    inputoptions();
    if(!justwts || firstset)
      clique_inputdata();
    firstset = false;
    SetUp(Comp);
    if (msets > 1 && !justwts) {
      fprintf(outfile, "Data set # %ld:\n\n",ith);
      if (progress)
        printf("\nData set # %ld:\n",ith);
    }
    if (justwts){
      fprintf(outfile, "Weights set # %ld:\n\n", ith);
      if (progress)
        printf("\nWeights set # %ld:\n\n", ith);
    }
    GetMaxCliques(Comp);
    if (progress) {
      printf("\nOutput written to file \"%s\"\n",outfilename);
      if (trout)
        printf("\nTree");
        if (tcount > 1)
          printf("s");
        printf(" written on file \"%s\"\n\n", outtreename);
    }
  }
  FClose(infile);
  FClose(outfile);
  FClose(outtree);
#ifdef MAC
  fixmacfile(outfilename);
  fixmacfile(outtreename);
#endif
#ifdef WIN32
  phyRestoreConsoleAttributes();
#endif
  printf("Done.\n\n");
  return 0;
}

