#include "utils.h"

void test_time(void);

ivec_array *mk_intersect_test_array(int size,int maxval,int n)
{
  ivec_array *iva = mk_empty_ivec_array();
  int i;

  printf("Begin making test_array...\n");

  for ( i = 0 ; i < n ; i++ )
  {
    ivec *a = mk_identity_ivec(maxval);
    ivec *s;
    shuffle_ivec(a);
    while ( ivec_size(a) > size )
      ivec_remove_last_element(a);
    s = mk_sivec_from_ivec(a);
    add_to_ivec_array(iva,s);
    free_ivec(a);
    free_ivec(s);
  }

  printf("...end making test_array\n\n");

  return iva;
}

int intersect_main_given_asize(int argc,char *argv[],int asize,int n)
{
  int amax = int_from_args("amax",argc,argv,10000);
  int bsize = int_from_args("bsize",argc,argv,5000);
  int bmax = int_from_args("bmax",argc,argv,10000);
  ivec_array *a = mk_intersect_test_array(asize,amax,n);
  ivec_array *b = mk_intersect_test_array(bsize,bmax,n);
  int i,j;
  int sum_int_lengths = 0;
  int start_time = global_time();

  for ( i = 0 ; i < n ; i++ )
    for ( j = 0 ; j < n ; j++ )
    {
      ivec *ai = ivec_array_ref(a,i);
      ivec *bj = ivec_array_ref(b,j);
      ivec *intersection = mk_sivec_intersection(ai,bj);
      /*
      fprintf_ivec(stdout,"a",ai,"\n");
      fprintf_ivec(stdout,"b",bj,"\n");
      fprintf_ivec(stdout,"intersection",intersection,"\n");
      wait_for_key();
      */

      sum_int_lengths += ivec_size(intersection);

      free_ivec(intersection);
    }

  printf("sum_int_lengths = %d\nThat took %d sec(s)\n",
	 sum_int_lengths,global_time() - start_time);

  free_ivec_array(a);
  free_ivec_array(b);

  return global_time() - start_time;
}

void intersect_main(int argc,char *argv[])
{
  ivec *a = mk_ivec_6(1,2,3,6,7,8);
  ivec *b = mk_ivec_6(-1,2,3,5,6,9);
  ivec *i = mk_sivec_intersection(a,b);

  {
    int s1 = intersect_main_given_asize(argc,argv,10,700);
    int s2 = intersect_main_given_asize(argc,argv,100,600);
    int s3 = intersect_main_given_asize(argc,argv,1000,500);
    int s4 = intersect_main_given_asize(argc,argv,10000,250);

    printf("(asize : secs) results:\n");

    printf("(10,%d) (100,%d) (1000,%d) (10000,%d)\n",s1,s2,s3,s4);
  }

  free_ivec(a);
  free_ivec(b);
  free_ivec(i);
}

void test_mkdir(void)
{
#ifdef PC_PLATFORM
am_mkdir("here\\there");
printf("Look in here\\there\n");
#else
am_mkdir_deeply("here/there");
printf("Look for here/there\n");
am_mkdir_deeply("/tmp/alpha/beta");
printf("Look for /tmp for alpha/beta\n");
am_mkdir_deeply("somewhere");
printf("Look for somewhere\n");
#endif
if(am_isdir("here"))
  printf("directory 'here' exists\n");
if(am_isdir("shere"))
  printf("directory 'shere' exists\n");
if(am_isfile("main.c"))
  printf("file main.c exists\n");
if(am_isfile("goooooo.c"))
  printf("file gooooooo.c exists\n");
}

void test_isd( void)
{
  int idx;
  double key;
  dyv *dv;

  dv = mk_dyv_x( 11, -2.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 3.0);

  printf( "[paul_]index_in_sorted_dyv:\n");
  fprintf_oneline_dyv( stdout, "dv:", dv, "\n");

  for (key = -2.5; key<4.0; key += 0.5) {
    printf( "key: %+.2f", key);
    idx = index_in_sorted_dyv( dv, key);
    printf( "  idx: %d", idx);
    idx = paul_index_in_sorted_dyv( dv, key);
    printf( "  paul idx: %d\n", idx);
  }

  free_dyv( dv);
  return;
}

void test_mktemp( void)
{
  char pattern[500], *foo;
  sprintf( pattern, "/tmp/abcXXXXXX");
  printf( "\npattern = %s\n", pattern);
  foo = am_mktemp( pattern);
  if (foo == NULL) printf( "Unable to create unused file name %s.\n",pattern);
  else printf( "Created name %s.\n", pattern);
  return;
}

void test_backtrace_sigusr2( void)
{
  backtrace_install_sigusr2_handler();
  while (1) {
    fprintf( stderr, "Waiting 5 seconds for SIGUSR2 (press ctrl-c to exit)\n");
    am_sleep(5);
  }
}

void test_backtrace_sigsegv( void)
{
  backtrace_install_sigsegv_handler();
  fprintf( stderr, "\n\nAbout to invoke fprintf() on (FILE *) NULL\n");
  fprintf( NULL, "foo\n");
}

void test_backtrace( void)
{
  /* Getting backtrace info directly and printing. */
  btinfo *bt;
  bt = mk_btinfo( 15);  /* Parameter is the max number of stack levels to
			   print. */
  fprintf_btinfo( stdout, "\n", bt, "\n");
  free_btinfo( bt);

  /* Triggering a backtrace through a call to my_breakpoint() via
     my_error(). */
  printf( "******************************************************\n");
  printf( "*                                                    *\n");
  printf( "* Now trying call to my_error; this should be fatal. *\n");
  printf( "*                                                    *\n");
  printf( "******************************************************\n\n");
  my_error( "Don't Panic.\n");
  return;
}

void test_erf( void)
{
  int i;
  for ( i = -100 ; i <= 100 ; i+=5 )
  {
    double x = i / 10.0;
    printf("am_gauss_cdf(%g) = %g\n",x,am_gauss_cdf(x));
  }
  return;
}

void test_robust( int argc, char **argv)
{
  int size, i, j;
  double seed, val, diff;
  ivec *goodlist;
  dym *A;

  size = int_from_args( "size", argc, argv, 10);
  seed = int_from_args( "seed", argc, argv, 1);

  goodlist = mk_identity_ivec( size);
  A = mk_dym( size, size);

  srand( seed);
  for (i=0; i<size; ++i) {
    for (j=0; j<=i; ++j) {
      val = rand() / (double) RAND_MAX;
      dym_set( A, i, j, val);
      dym_set( A, j, i, val);
    }
  }
  
  diff = check_robust_cholesky_factorization( A, goodlist);
  printf( "diff=%g\n", diff);
  return;
}

void test_dym_mult_subset(void)
{
  ivec *goodArows, *goodAcols, *goodBcols, *goodRrows, *goodRcols;
  dym *A, *B, *R;

  /* Make matrix factors. */
  A = mk_dym_x( 3,2,
		1.0, 0.0,
		0.0, 1.0,
		0.0, 2.0);

  B = mk_dym_x( 2,3,
		1.0, 3.0, 5.0,
		2.0, 4.0, 6.0);

  fprintf_dym( stdout, "A:", A, "");
  fprintf_dym( stdout, "B:", B, "");

  /* Select subsets. */
  goodArows = mk_ivec_x( 2,  0, 1);
  goodAcols = mk_ivec_x( 2,  0, 1);
  goodBcols = mk_ivec_x( 2,  0, 2);
  goodRrows = mk_ivec_x( 2,  1,2);
  goodRcols = mk_ivec_x( 2,  1,2);
  R = mk_dym( 3,3);

  fprintf_ivec( stdout, "goodArows:", goodArows, "\n");
  fprintf_ivec( stdout, "goodAcols:", goodAcols, "\n");
  fprintf_ivec( stdout, "goodBcols:", goodBcols, "\n");
  fprintf_ivec( stdout, "goodRrows:", goodRrows, "\n");
  fprintf_ivec( stdout, "goodRcols:", goodRcols, "\n\n");

  /* Multiply. */
  dym_mult_subset( A, B, R, goodArows, goodAcols, goodBcols,
		   goodRrows, goodRcols);

  fprintf_dym( stdout, "A*B:", R, "");

  free_dym( A);
  free_dym( B);
  free_dym( R);
  free_ivec( goodArows);
  free_ivec( goodAcols);
  free_ivec( goodBcols);
  free_ivec( goodRrows);
  free_ivec( goodRcols);

  return;
}

void test_dym_times_dyv_subset(void)
{
  ivec *goodArows, *goodAcols, *goodRrows;
  dyv *x, *R;
  dym *A;

  /* Make matrix and vector. */
  A = mk_dym_x( 3,3,
		1.0, 0.0, 0.0,
		0.0, 2.0, 0.0,
		0.0, 0.0, 3.0);

  x = mk_dyv_x( 3,  4.0, 5.0, 6.0);

  fprintf_dym( stdout, "A:", A, "");
  fprintf_dyv( stdout, "x:", x, "");

  /* Select subsets. */
  goodArows = mk_ivec_x( 2,  0, 2);
  goodAcols = mk_ivec_x( 2,  0, 2);
  goodRrows = mk_ivec_x( 2,  0, 2);
  R = mk_dyv( 3);

  fprintf_ivec( stdout, "goodArows:", goodArows, "\n");
  fprintf_ivec( stdout, "goodAcols:", goodAcols, "\n");
  fprintf_ivec( stdout, "goodRrows:", goodRrows, "\n\n");

  /* Multiply. */
  dym_times_dyv_subset( A, x, R, goodArows, goodAcols, goodRrows);

  fprintf_dyv( stdout, "A*x:", R, "");

  free_dym( A);
  free_dyv( R);
  free_ivec( goodArows);
  free_ivec( goodAcols);
  free_ivec( goodRrows);

  return;
}

void test_subset(void)
{
  test_dym_mult_subset();
  printf( "---------------------------------------------------------------\n");
  test_dym_times_dyv_subset();
  return;
}

void mdp(void)
{
  double r1 = 4.0;
  double r2 = -1.0;
  double r4 = 2.0;
  double v1 = 0.0;
  double v2 = 0.0;
  double v3 = 0.0;
  double v4 = 0.0;
  while ( TRUE )
  {
    double q1u = r1 + v2;
    double q1d = r1 + 0.5 * v4;
    double q2u = r2 + 0.3333333333333 * (v1 + v2);
    double q2d = r2 + 0.5 * v4;
    double q4u = r4 + v2;
    double q4d = r4 + 0.5 * (v4 + v2);

    printf("%g %g %g %g\n",v1,v2,v3,v4);

    v1 = real_max(q1u,q1d);
    v2 = real_max(q2u,q2d);
    v4 = real_max(q4u,q4d);

    wait_for_key();
  }
}

dyv *mk_bootstrap_dyv(dyv *x)
{
  int size = dyv_size(x);
  dyv *b = mk_dyv(size);
  int i;
  for ( i = 0 ; i < size ; i++ )
     dyv_set(b,i,dyv_ref(x,int_random(size)));
  return b;
}

dyv *mk_random_dyv_in_unit_cube(int size)
{
  dyv *x = mk_dyv(size);
  int i;
  for ( i = 0 ; i < size ; i++ )
    dyv_set(x,i,range_random(0.0,1.0));
  return x;
}

int num_bootstraps_above_zero(int num_cats,int num_bootstraps)
{
  dyv *x1 = mk_random_dyv_in_unit_cube(num_cats);
  dyv *x2 = mk_random_dyv_in_unit_cube(num_cats);
  dyv *diff = mk_dyv_subtract(x1,x2);
  int i;
  int result = 0;

  for ( i = 0 ; i < num_bootstraps ; i++ )
  {
    dyv *boot = mk_bootstrap_dyv(diff);
    if ( dyv_mean(boot) > 0.1 )
      result += 1;
    free_dyv(boot);
  }

  free_dyv(diff);
  free_dyv(x1);
  free_dyv(x2);
  return result;
}

void boot_main(int argc,char *argv[])
{
  int num_trials = int_from_args("num_trials",argc,argv,300);
  int num_bootstraps = int_from_args("num_bootstraps",argc,argv,5000);
  int num_cats = int_from_args("num_cats",argc,argv,200);
  int i;
  for ( i = 0 ; i < num_trials ; i++ )
    printf("%9.6f\n",num_bootstraps_above_zero(num_cats,num_bootstraps) /
                     (double) num_bootstraps);
}

void matrix_example(void)
{
  dym *x = mk_dym_22(1.0,2.0,3.0,0.0);
  dym *x_inv = mk_invert_dym(x);
  fprintf_dym(stdout,"x",x,"\n");
  fprintf_dym(stdout,"x_inverse",x_inv,"\n");
  free_dym(x_inv);
  free_dym(x);
}

void test_malloc(int argc,char *argv[])
{
  int size = int_from_args("size",argc,argv,100);
  int delta = int_from_args("delta",argc,argv,1);
  int i;
  int ivec_size = 0;
  int next_n = 1;
  int next_t = 1;
  extern int Num_mallocs;
  extern int Total_mallocked;
  int loops = int_from_args("loops",argc,argv,1);
  int j;

  for ( j = 0 ; j < loops ; j++ )
  {
    ivec_array *iva = mk_empty_ivec_array();
    for ( i = 0 ; i < size ; i++ )
    {
      ivec *iv = mk_identity_ivec(ivec_size);
      bool pn = TRUE;
      bool pt = TRUE;

      add_to_ivec_array(iva,iv);
      free_ivec(iv);
      ivec_size += delta;

      while ( Num_mallocs >= next_n && next_n < 1000000000 )
      {
	if ( pn ) printf("Num_mallocs = %d\n",Num_mallocs);
	next_n *= 2;
	pn = FALSE;
      }

      while ( Total_mallocked >= next_t  && next_t < 1000000000 )
      {
	if ( pt ) printf("Total_mallocked = %d\n",Total_mallocked);
	next_t *= 2;
	pt = FALSE;
      }
    }
    free_ivec_array(iva);
  }

}

void testthis( void)
{
  int a;
  volatile int b;
  b = 0;
  for (a=0; a<10000; ++a) b += a;
  return;
}

void test_itimer_benchmark( void)
{
  double T;
  T = itimer_benchmark( ITIMER_PROF, 0.02, testthis);
  printf( "T: %g usecs with relative error less than 2%%\n", T);
  return;
}

void erf_main(int argc,char *argv[])
{
  double x = double_from_args("x",argc,argv,0.0);
  printf("Will compute am_gauss_cdf(%g)\n",x);
  printf("am_gauss_cdf(%g) = %g\n",x,am_gauss_cdf(x));
}

int main(int argc, char* argv[])
{
  prepare_timers();
  start_itimer(ITIMER_VIRTUAL);
  printf("sqrt(2 * PI) = %20.18f\n",sqrt(2 * PI));
  printf("log(2 * PI) = %20.18f\n",log(2 * PI));
  printf("log(sqrt(2 * PI)) = %20.18f\n",0.5 * log(2 * PI));
  
  if ((argc > 1) && (eq_string(argv[1],"boot")))
    boot_main(argc,argv);
  else if ((argc > 1) && (eq_string(argv[1],"erf")))
    erf_main(argc,argv);
  else if ((argc > 1) && (eq_string(argv[1],"intersect")))
    intersect_main(argc,argv);
  else if ((argc > 1) && (eq_string(argv[1],"timestring")))
  {
    void timestring_main(int argc,char *argv[]);
    timestring_main(argc,argv);
  }
  else if ((argc > 1) && (eq_string(argv[1],"mdp")))
    mdp();
  else if ((argc > 1) && (eq_string(argv[1],"erf")))
    test_erf();
  else if ((argc > 1) && (eq_string(argv[1],"matrix")))
    matrix_example();
  else if ((argc > 1) && (eq_string(argv[1],"robust")))
    test_robust( argc, argv);
  else if ((argc > 1) && (eq_string(argv[1],"subset")))
    test_subset();
  else if ((argc > 1) && (eq_string(argv[1],"malloc")))
  {
    test_malloc(argc,argv);
    am_malloc_report();
    wait_for_key();
  }
  else if ((argc > 1) && (eq_string(argv[1],"backtrace")))
    test_backtrace();
  else if ((argc > 1) && (eq_string(argv[1],"btsegv")))
    test_backtrace_sigsegv();
  else if ((argc > 1) && (eq_string(argv[1],"btusr2")))
    test_backtrace_sigusr2();
  else if ((argc > 1) && (eq_string(argv[1],"mktemp")))
    test_mktemp();
  else if ((argc > 1) && (eq_string(argv[1],"isd")))
    test_isd();
  else if ((argc > 1) && (eq_string(argv[1],"time")))
    test_time();
  else if ((argc > 1) && (eq_string(argv[1],"mkdir")))
    test_mkdir();
  else if ((argc > 1) && (eq_string(argv[1],"ibench")))
    test_itimer_benchmark();
  /*
    else if ((argc > 1) && (eq_string(argv[1],"iqueue")))
    test_iqueue();
  */
  else
  {
    printf("Nothing to see here, move along, move along [%f]...\n",
	   stop_itimer(ITIMER_VIRTUAL));
  }
  return 0;
}

void test_time(void)
{
  printf("Date: %s\n", curr_date());
}

