#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/times.h>
#include <math.h>
#define maxN 500000

#define randOBJ ((int) N*((float) rand()/(RAND_MAX)))

int id[maxN];
int sz[maxN];
int cnt, cntE;

//Quick find
void quickF(int N)
  { int i, p, q, t;
    cnt = 0; cntE = 0;
    for (i = 0; i < N; i++) id[i] = i;
    while (cnt < N-1)
      { 
        cntE++;
        p = randOBJ; q = randOBJ;
        if (id[p] == id[q]) continue;
        for (t = id[p], i = 0; i < N; i++)
          if (id[i] == t) id[i] = id[q];
        cnt++;
      }
 }

//Quick union
void quickU(int N)
  { int i, j, p, q, t;
    cnt = 0; cntE = 0;
    for (i = 0; i < N; i++) id[i] = i;
    while (cnt < N-1)
      { 
        cntE++;
        p = randOBJ; q = randOBJ;
        for (i = p; i != id[i]; i = id[i]) ;
        for (j = q; j != id[j]; j = id[j]) ;
        if (i == j) continue;
        id[i] = j;
        cnt++;
      }
// printf("COUNT %d\n", cnt);
// for (i = 0; i < N; i++) printf("%2d ", id[i]); printf("\n");
 }

//Weighted quick union
void quickUW(int N)
  { int i, j, p, q, t;
    cnt = 0; cntE = 0;
    for (i = 0; i < N; i++) id[i] = i;
    for (i = 0; i < N; i++) sz[i] = 1;
    while (cnt < N-1)
      { 
        cntE++;
        p = randOBJ; q = randOBJ;
        for (i = p; i != id[i]; i = id[i]) ;
        for (j = q; j != id[j]; j = id[j]) ;
        if (i == j) continue;
        if (sz[i] < sz[j])
             { id[i] = j; sz[j] += sz[i]; }
        else { id[j] = i; sz[i] += sz[j]; }
        cnt++;
      }
 }

//Weighted quick union with path compression by halving
void quickUWH(int N)
  { int i, j, p, q, t;
    cnt = 0; cntE = 0;
    for (i = 0; i < N; i++) id[i] = i;
    for (i = 0; i < N; i++) sz[i] = 1;
    while (cnt < N-1)
      { 
        cntE++;
        p = randOBJ; q = randOBJ;
        for (i = p; i != id[i]; i = id[i]) 
          { t = i; i = id[t]; id[t] = id[i]; } 
        for (j = q; j != id[j]; j = id[j]) ;
          { t = j; j = id[t]; id[t] = id[j]; } 
        if (i == j) continue;
        if (sz[i] < sz[j])
             { id[i] = j; sz[j] += sz[i]; }
        else { id[j] = i; sz[i] += sz[j]; }
        cnt++;
      }
 }

//Weighted quick union with full compression
void quickUWPC(int N)
  { int i, j, p, q, t;
    cnt = 0; cntE = 0;
    for (i = 0; i < N; i++) id[i] = i;
    for (i = 0; i < N; i++) sz[i] = 1;
    while (cnt < N-1)
      { 
        cntE++;
        p = randOBJ; q = randOBJ;
        for (i = p; i != id[i]; i = id[i]) ;
        for (j = q; j != id[j]; j = id[j]) ;
        if (i == j) continue;
        if (sz[i] < sz[j])
             { id[i] = j; sz[j] += sz[i]; }
        else { id[j] = i; sz[i] += sz[j]; }
        while (p != id[p]) 
          { i = p; p = id[p]; id[i] = t; }
        while (q != id[q]) 
          { j = q; q = id[q]; id[j] = t; }
        cnt++;
      }
 }

// Driver code
struct tms buffer; 

long doit(void (*ufprog)(), int N)
  {
    int i; long t;
    times(&buffer);
    t = buffer.tms_utime + buffer.tms_stime;
    (*ufprog)(N);
    times(&buffer);
    return buffer.tms_utime + buffer.tms_stime - t;
  }

main(int argc, char *argv[])
  { 
    int N; int debug = atoi(argv[--argc]);
    int c1, c2, c3, c4, c5;

   printf("    N     edges      F      U      W      H      P\n");
   for (N = 1000; N < 100001; N *= 2)
      {
        c1 = 0; c2 = 0; c3 = 0; c4 = 0; c5 = 0;
        if (N == 2000) N += 500;
        if (N == 20000) N += 5000;
        if (N <= 10000) c1 = doit(quickF, N);
        if (N <= 10000) c2 = doit(quickU, N);
        c3 = doit(quickUW, N);
        c4 = doit(quickUWH, N);
        c5 = doit(quickUWPC, N);
        printf("%6d %8d %6d %6d %6d %6d %6d\n", N, cntE, c1, c2, c3, c4, c5);
      }

    printf("\n");
    printf("           F:  quick find\n");
    printf("           U:  quick union\n");
    printf("           W:  weighted quick union\n");
    printf("           H:  weighted QU with path compression by halving\n");
    printf("           P:  weighted QU with full path compression\n");
  }

