/* Copyright (c) 2014, Giuseppe Argentieri <giuseppe.argentieri@ts.infn.it>

 * 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.
 *
 */
/*
 *
 *
 *       Filename:  posit.c
 *
 *    Description:  Determine the positivity violation of the Redfield dynamics
 *    			on a given initial pure state
 *
 *        Version:  1.0
 *        Created:  20/07/2014 09:59:35
 *       Revision:  none
 *        License:  BSD
 *
 *         Author:  Giuseppe Argentieri (ga), giuseppe.argentieri@ts.infn.it
 *   Organization:  Università degli Studi di Trieste
 *
 *
 */

#include "initial.h"
#include "funcs.h"

#include <stdlib.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>

/*
 *      FUNCTION
 *         Name:  main
 *  Description:
 *
 */
int main ( int argc, char *argv[] )
{
	double o_eff = gsl_hypot(OMEGA, D);

	/* sigma^z eigenstates, Bloch vectorial form */
	double z_minus[] = { 1, 0, -OMEGA/o_eff, -D/o_eff };
	double z_plus[]  = { 1, 0,  OMEGA/o_eff,  D/o_eff };
	gsl_vector* z_m = gsl_vector_calloc(4);
	gsl_vector* z_p = gsl_vector_calloc(4);

	int i;
	for ( i = 0; i < 4; i++ ) {
		gsl_vector_set( z_m, i, z_minus[i] );
		gsl_vector_set( z_p, i, z_plus[i]  );
	}

	/* parameters */
	double r, theta;

	return EXIT_SUCCESS;
}				/* ----------  end of function main  ---------- */

/*
 *      FUNCTION
 *         Name:  state_matrix
 *  Description:  return the density patrix of the pure state |psi><psi|
 *
 */
gsl_matrix_complex* state_matrix ( double r, double theta )
{
	gsl_matrix_complex* psi = gsl_matrix_complex_calloc (2, 2);
	gsl_matrix_complex_set ( psi, 1, 1, gsl_complex_rect(r*r, 0) );
	gsl_matrix_complex_set ( psi, 1, 2, gsl_complex_rect(r*sqrt(1-r*r)*cos(theta),
				r*sqrt(1-r*r)*sin(theta)) );
	gsl_matrix_complex_set ( psi, 2, 1, gsl_complex_rect(r*sqrt(1-r*r)*cos(theta),
				-r*sqrt(1-r*r)*sin(theta)) );
	gsl_matrix_complex_set ( psi, 2, 2, gsl_complex_rect(1-r*r, 0) );

	return psi;
}		/* -----  end of function state_matrix  ----- */

/*
 *      FUNCTION
 *         Name:  rotate
 *  Description:  return the density matrix in the rotated frame of reference
 *  			of an angle OMEGA*t
 *
 */
gsl_matrix_complex* rotate ( gsl_matrix_complex* m, double t )
{
	/* copy m into a working matrix M */
	gsl_matrix_complex* M = gsl_matrix_complex_calloc ( 2, 2);
	gsl_matrix_complex_memcpy( M, m );

	/* rotation matrix */
	gsl_matrix_complex* Rot = gsl_matrix_complex_calloc( 2, 2 );

	gsl_matrix_complex_set ( Rot, 1, 1, gsl_complex_rect( cos(OMEGA*t/2), 0) );
	gsl_matrix_complex_set ( Rot, 1, 2, gsl_complex_rect( -sin(OMEGA*t/2), 0));
	gsl_matrix_complex_set ( Rot, 2, 1, gsl_complex_rect( sin(OMEGA*t/2), 0) );
	gsl_matrix_complex_set ( Rot, 2, 2, gsl_complex_rect( cos(OMEGA*t/2), 0) );

	/* inverse of Rot:
	 *
	 * first perform the LU decomposition of Rot: */
	gsl_permutation* perm = gsl_permutation_calloc(2);
	int signum;
	gsl_matrix_complex* Rot_lu = gsl_matrix_complex_calloc( 2, 2 );
	gsl_matrix_complex_memcpy ( Rot_lu, Rot );
	gsl_linalg_complex_LU_decomp ( Rot_lu, perm, &signum);
	/* then calculate Rot_inv: */
	gsl_matrix_complex* Rot_inv = gsl_matrix_complex_calloc( 2, 2 );
	gsl_linalg_complex_LU_invert ( Rot_lu, perm, Rot_inv );

	/* Finally calculate Rot * m * Rot_inv */
	gsl_blas_zgemm ( CblasNoTrans, CblasNoTrans, gsl_complex_rect(1.,0),
			Rot, M, gsl_complex_rect(0,0), M );
	gsl_blas_zgemm ( CblasNoTrans, CblasNoTrans, gsl_complex_rect(1.,0),
			M, Rot_inv, gsl_complex_rect(0,0), M );

	return (M);
}		/* -----  end of function rotate  ----- */

/*
 *      FUNCTION
 *         Name:  D_psi
 *  Description:  the matrix D[|psi><psi|]
 *
 */
gsl_matrix_complex* D_psi ( gsl_matrix_complex* dpsi )
{
	gsl_matrix_complex* dp = gsl_matrix_complex_calloc(2, 2);

	return (dp);
}		/* -----  end of function D_psi  ----- */


/* rng/random.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
 *
 * This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_rng.h>

/* This file provides support for random() generators. There are three
   versions in widespread use today,

   - The original BSD version, e.g. on SunOS 4.1 and FreeBSD.

   - The Linux libc5 version, which is differs from the BSD version in
   its seeding procedure, possibly due to the introduction of a typo
   in the multiplier.

   - The GNU glibc2 version, which has a new (and better) seeding
   procedure.

   They all produce different numbers, due to the different seeding
   algorithms, but the algorithm for the generator is the same in each
   case.

 */

static inline long int random_get (int * i, int * j, int n, long int * x);

static inline unsigned long int random8_get (void *vstate);
static inline unsigned long int random32_get (void *vstate);
static inline unsigned long int random64_get (void *vstate);
static inline unsigned long int random128_get (void *vstate);
static inline unsigned long int random256_get (void *vstate);

static double random8_get_double (void *vstate);
static double random32_get_double (void *vstate);
static double random64_get_double (void *vstate);
static double random128_get_double (void *vstate);
static double random256_get_double (void *vstate);

static void random8_glibc2_set (void *state, unsigned long int s);
static void random32_glibc2_set (void *state, unsigned long int s);
static void random64_glibc2_set (void *state, unsigned long int s);
static void random128_glibc2_set (void *state, unsigned long int s);
static void random256_glibc2_set (void *state, unsigned long int s);

static void random8_libc5_set (void *state, unsigned long int s);
static void random32_libc5_set (void *state, unsigned long int s);
static void random64_libc5_set (void *state, unsigned long int s);
static void random128_libc5_set (void *state, unsigned long int s);
static void random256_libc5_set (void *state, unsigned long int s);

static void random8_bsd_set (void *state, unsigned long int s);
static void random32_bsd_set (void *state, unsigned long int s);
static void random64_bsd_set (void *state, unsigned long int s);
static void random128_bsd_set (void *state, unsigned long int s);
static void random256_bsd_set (void *state, unsigned long int s);

static void bsd_initialize (long int * x, int n, unsigned long int s);
static void libc5_initialize (long int * x, int n, unsigned long int s);
static void glibc2_initialize (long int * x, int n, unsigned long int s);

typedef struct
  {
    long int x;
  }
random8_state_t;

typedef struct
  {
    int i, j;
    long int x[7];
  }
random32_state_t;

typedef struct
  {
    int i, j;
    long int x[15];
  }
random64_state_t;

typedef struct
  {
    int i, j;
    long int x[31];
  }
random128_state_t;

typedef struct
  {
    int i, j;
    long int x[63];
  }
random256_state_t;

static inline unsigned long int
random8_get (void *vstate)
{
  random8_state_t *state = (random8_state_t *) vstate;

  state->x = (1103515245 * state->x + 12345) & 0x7fffffffUL;
  return state->x;
}

static inline long int
random_get (int * i, int * j, int n, long int * x)
{
  long int k ;

  x[*i] += x[*j] ;
  k = (x[*i] >> 1) & 0x7FFFFFFF ;

  (*i)++ ;
  if (*i == n)
    *i = 0 ;

  (*j)++ ;
  if (*j == n)
    *j = 0 ;

  return k ;
}

static inline unsigned long int
random32_get (void *vstate)
{
  random32_state_t *state = (random32_state_t *) vstate;
  unsigned long int k = random_get (&state->i, &state->j, 7, state->x) ;
  return k ;
}

static inline unsigned long int
random64_get (void *vstate)
{
  random64_state_t *state = (random64_state_t *) vstate;
  long int k = random_get (&state->i, &state->j, 15, state->x) ;
  return k ;
}

static inline unsigned long int
random128_get (void *vstate)
{
  random128_state_t *state = (random128_state_t *) vstate;
  unsigned long int k = random_get (&state->i, &state->j, 31, state->x) ;
  return k ;
}

static inline unsigned long int
random256_get (void *vstate)
{
  random256_state_t *state = (random256_state_t *) vstate;
  long int k = random_get (&state->i, &state->j, 63, state->x) ;
  return k ;
}

static double
random8_get_double (void *vstate)
{
  return random8_get (vstate) / 2147483648.0 ;
}

static double
random32_get_double (void *vstate)
{
  return random32_get (vstate) / 2147483648.0 ;
}

static double
random64_get_double (void *vstate)
{
  return random64_get (vstate) / 2147483648.0 ;
}

static double
random128_get_double (void *vstate)
{
  return random128_get (vstate) / 2147483648.0 ;
}

static double
random256_get_double (void *vstate)
{
  return random256_get (vstate) / 2147483648.0 ;
}

static void
random8_bsd_set (void *vstate, unsigned long int s)
{
  random8_state_t *state = (random8_state_t *) vstate;

  if (s == 0)
    s = 1;

  state->x = s;
}

static void
random32_bsd_set (void *vstate, unsigned long int s)
{
  random32_state_t *state = (random32_state_t *) vstate;
  int i;

  bsd_initialize (state->x, 7, s) ;

  state->i = 3;
  state->j = 0;

  for (i = 0 ; i < 10 * 7 ; i++)
    random32_get (state) ;
}

static void
random64_bsd_set (void *vstate, unsigned long int s)
{
  random64_state_t *state = (random64_state_t *) vstate;
  int i;

  bsd_initialize (state->x, 15, s) ;

  state->i = 1;
  state->j = 0;

  for (i = 0 ; i < 10 * 15 ; i++)
    random64_get (state) ;
}

static void
random128_bsd_set (void *vstate, unsigned long int s)
{
  random128_state_t *state = (random128_state_t *) vstate;
  int i;

  bsd_initialize (state->x, 31, s) ;

  state->i = 3;
  state->j = 0;

  for (i = 0 ; i < 10 * 31 ; i++)
    random128_get (state) ;
}

static void
random256_bsd_set (void *vstate, unsigned long int s)
{
  random256_state_t *state = (random256_state_t *) vstate;
  int i;

  bsd_initialize (state->x, 63, s) ;

  state->i = 1;
  state->j = 0;

  for (i = 0 ; i < 10 * 63 ; i++)
    random256_get (state) ;
}

static void
bsd_initialize (long int * x, int n, unsigned long int s)
{
  int i;

  if (s == 0)
    s = 1 ;

  x[0] = s;

  for (i = 1 ; i < n ; i++)
    x[i] = 1103515245 * x[i-1] + 12345 ;
}

static void
libc5_initialize (long int * x, int n, unsigned long int s)
{
  int i;

  if (s == 0)
    s = 1 ;

  x[0] = s;

  for (i = 1 ; i < n ; i++)
    x[i] = 1103515145 * x[i-1] + 12345 ;
}

static void
glibc2_initialize (long int * x, int n, unsigned long int s)
{
  int i;

  if (s == 0)
    s = 1 ;

  x[0] = s;

  for (i = 1 ; i < n ; i++)
    {
      const long int h = s / 127773;
      const long int t = 16807 * (s - h * 127773) - h * 2836;
      if (t < 0)
        {
          s = t + 2147483647 ;
        }
      else
        {
          s = t ;
        }

    x[i] = s ;
    }
}

static void
random8_glibc2_set (void *vstate, unsigned long int s)
{
  random8_state_t *state = (random8_state_t *) vstate;

  if (s == 0)
    s = 1;

  state->x = s;
}

static void
random32_glibc2_set (void *vstate, unsigned long int s)
{
  random32_state_t *state = (random32_state_t *) vstate;
  int i;

  glibc2_initialize (state->x, 7, s) ;

  state->i = 3;
  state->j = 0;

  for (i = 0 ; i < 10 * 7 ; i++)
    random32_get (state) ;
}

static void
random64_glibc2_set (void *vstate, unsigned long int s)
{
  random64_state_t *state = (random64_state_t *) vstate;
  int i;

  glibc2_initialize (state->x, 15, s) ;

  state->i = 1;
  state->j = 0;

  for (i = 0 ; i < 10 * 15 ; i++)
    random64_get (state) ;
}

static void
random128_glibc2_set (void *vstate, unsigned long int s)
{
  random128_state_t *state = (random128_state_t *) vstate;
  int i;

  glibc2_initialize (state->x, 31, s) ;

  state->i = 3;
  state->j = 0;

  for (i = 0 ; i < 10 * 31 ; i++)
    random128_get (state) ;
}

static void
random256_glibc2_set (void *vstate, unsigned long int s)
{
  random256_state_t *state = (random256_state_t *) vstate;
  int i;

  glibc2_initialize (state->x, 63, s) ;

  state->i = 1;
  state->j = 0;

  for (i = 0 ; i < 10 * 63 ; i++)
    random256_get (state) ;
}

static void
random8_libc5_set (void *vstate, unsigned long int s)
{
  random8_state_t *state = (random8_state_t *) vstate;

  if (s == 0)
    s = 1;

  state->x = s;
}

static void
random32_libc5_set (void *vstate, unsigned long int s)
{
  random32_state_t *state = (random32_state_t *) vstate;
  int i;

  libc5_initialize (state->x, 7, s) ;

  state->i = 3;
  state->j = 0;

  for (i = 0 ; i < 10 * 7 ; i++)
    random32_get (state) ;
}

static void
random64_libc5_set (void *vstate, unsigned long int s)
{
  random64_state_t *state = (random64_state_t *) vstate;
  int i;

  libc5_initialize (state->x, 15, s) ;

  state->i = 1;
  state->j = 0;

  for (i = 0 ; i < 10 * 15 ; i++)
    random64_get (state) ;
}

static void
random128_libc5_set (void *vstate, unsigned long int s)
{
  random128_state_t *state = (random128_state_t *) vstate;
  int i;

  libc5_initialize (state->x, 31, s) ;

  state->i = 3;
  state->j = 0;

  for (i = 0 ; i < 10 * 31 ; i++)
    random128_get (state) ;
}

static void
random256_libc5_set (void *vstate, unsigned long int s)
{
  random256_state_t *state = (random256_state_t *) vstate;
  int i;

  libc5_initialize (state->x, 63, s) ;

  state->i = 1;
  state->j = 0;

  for (i = 0 ; i < 10 * 63 ; i++)
    random256_get (state) ;
}

static const gsl_rng_type random_glibc2_type =
{"random-glibc2",                       /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random128_state_t),
 &random128_glibc2_set,
 &random128_get,
 &random128_get_double};

static const gsl_rng_type random8_glibc2_type =
{"random8-glibc2",                      /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random8_state_t),
 &random8_glibc2_set,
 &random8_get,
 &random8_get_double};

static const gsl_rng_type random32_glibc2_type =
{"random32-glibc2",                     /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random32_state_t),
 &random32_glibc2_set,
 &random32_get,
 &random32_get_double};

static const gsl_rng_type random64_glibc2_type =
{"random64-glibc2",                     /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random64_state_t),
 &random64_glibc2_set,
 &random64_get,
 &random64_get_double};

static const gsl_rng_type random128_glibc2_type =
{"random128-glibc2",                    /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random128_state_t),
 &random128_glibc2_set,
 &random128_get,
 &random128_get_double};

static const gsl_rng_type random256_glibc2_type =
{"random256-glibc2",                    /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random256_state_t),
 &random256_glibc2_set,
 &random256_get,
 &random256_get_double};

static const gsl_rng_type random_libc5_type =
{"random-libc5",                        /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random128_state_t),
 &random128_libc5_set,
 &random128_get,
 &random128_get_double};

static const gsl_rng_type random8_libc5_type =
{"random8-libc5",                       /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random8_state_t),
 &random8_libc5_set,
 &random8_get,
 &random8_get_double};

static const gsl_rng_type random32_libc5_type =
{"random32-libc5",                      /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random32_state_t),
 &random32_libc5_set,
 &random32_get,
 &random32_get_double};

static const gsl_rng_type random64_libc5_type =
{"random64-libc5",                      /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random64_state_t),
 &random64_libc5_set,
 &random64_get,
 &random64_get_double};

static const gsl_rng_type random128_libc5_type =
{"random128-libc5",                     /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random128_state_t),
 &random128_libc5_set,
 &random128_get,
 &random128_get_double};

static const gsl_rng_type random256_libc5_type =
{"random256-libc5",                     /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random256_state_t),
 &random256_libc5_set,
 &random256_get,
 &random256_get_double};

static const gsl_rng_type random_bsd_type =
{"random-bsd",                  /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random128_state_t),
 &random128_bsd_set,
 &random128_get,
 &random128_get_double};

static const gsl_rng_type random8_bsd_type =
{"random8-bsd",                 /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random8_state_t),
 &random8_bsd_set,
 &random8_get,
 &random8_get_double};

static const gsl_rng_type random32_bsd_type =
{"random32-bsd",                        /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random32_state_t),
 &random32_bsd_set,
 &random32_get,
 &random32_get_double};

static const gsl_rng_type random64_bsd_type =
{"random64-bsd",                        /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random64_state_t),
 &random64_bsd_set,
 &random64_get,
 &random64_get_double};

static const gsl_rng_type random128_bsd_type =
{"random128-bsd",               /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random128_state_t),
 &random128_bsd_set,
 &random128_get,
 &random128_get_double};

static const gsl_rng_type random256_bsd_type =
{"random256-bsd",               /* name */
 0x7fffffffUL,                  /* RAND_MAX */
 0,                             /* RAND_MIN */
 sizeof (random256_state_t),
 &random256_bsd_set,
 &random256_get,
 &random256_get_double};

const gsl_rng_type *gsl_rng_random_libc5    = &random_libc5_type;
const gsl_rng_type *gsl_rng_random8_libc5   = &random8_libc5_type;
const gsl_rng_type *gsl_rng_random32_libc5  = &random32_libc5_type;
const gsl_rng_type *gsl_rng_random64_libc5  = &random64_libc5_type;
const gsl_rng_type *gsl_rng_random128_libc5 = &random128_libc5_type;
const gsl_rng_type *gsl_rng_random256_libc5 = &random256_libc5_type;

const gsl_rng_type *gsl_rng_random_glibc2    = &random_glibc2_type;
const gsl_rng_type *gsl_rng_random8_glibc2   = &random8_glibc2_type;
const gsl_rng_type *gsl_rng_random32_glibc2  = &random32_glibc2_type;
const gsl_rng_type *gsl_rng_random64_glibc2  = &random64_glibc2_type;
const gsl_rng_type *gsl_rng_random128_glibc2 = &random128_glibc2_type;
const gsl_rng_type *gsl_rng_random256_glibc2 = &random256_glibc2_type;

const gsl_rng_type *gsl_rng_random_bsd    = &random_bsd_type;
const gsl_rng_type *gsl_rng_random8_bsd   = &random8_bsd_type;
const gsl_rng_type *gsl_rng_random32_bsd  = &random32_bsd_type;
const gsl_rng_type *gsl_rng_random64_bsd  = &random64_bsd_type;
const gsl_rng_type *gsl_rng_random128_bsd = &random128_bsd_type;
const gsl_rng_type *gsl_rng_random256_bsd = &random256_bsd_type;


/* Copyright 2014-2018 The PySCF Developers. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

#include<stdio.h>
#include<stdlib.h>
#include <omp.h>
//#include <cblas.h>

/*
!
!
!Compute Y += A*X for CSR matrix A and dense vectors X,Y
! From scipy/sparse/sparsetools/csr.h
!
!
! Input Arguments:
!   I  n_row         - number of rows in A
!   I  n_col         - number of columns in A
!   I  Ap[n_row+1]   - row pointer
!   I  Aj[nnz(A)]    - column indices
!   T  Ax[nnz(A)]    - nonzeros
!   T  Xx[n_col]     - input vector
!
! Output Arguments:
!  T  Yx[n_row]     - output vector
!
! Note:
!   Output array Yx must be preallocated
!
!   Complexity: Linear.  Specifically O(nnz(A) + n_row)
*/
void scsr_matvec(int nrow, int ncol, int nnz, int *Ap, int *Aj,
    float *Ax, float *Xx, float *Yx)
{

  int i, jj;
  float sum = 0.0;

  # pragma omp parallel \
  shared (nrow, Yx, Ap, Ax, Xx, Aj) \
  private (i, jj, sum)
  {
    #pragma omp for
    for(i = 0; i < nrow; i++){
      sum = Yx[i];
      for(jj = Ap[i]; jj < Ap[i+1]; jj++){
        sum += Ax[jj] * Xx[Aj[jj]];
      }
      Yx[i] = sum;
    }
  }
}

void dcsr_matvec(int nrow, int ncol, int nnz, int *Ap, int *Aj,
    double *Ax, double *Xx, double *Yx)
{

  int i, jj;
  double sum = 0.0;

  # pragma omp parallel \
  shared (nrow, Yx, Ap, Ax, Xx, Aj) \
  private (i, jj, sum)
  {
    #pragma omp for
    for(i = 0; i < nrow; i++){
      sum = Yx[i];
      for(jj = Ap[i]; jj < Ap[i+1]; jj++){
        sum += Ax[jj] * Xx[Aj[jj]];
      }
      Yx[i] = sum;
    }
  }
}

/*
 * Compute Y += A*X for CSC matrix A and dense vectors X,Y
 * From scipy/sparse/sparsetools/csc.h
 *
 *
 * Input Arguments:
 *   I  n_row         - number of rows in A
 *   I  n_col         - number of columns in A
 *   I  Ap[n_row+1]   - column pointer
 *   I  Ai[nnz(A)]    - row indices
 *   T  Ax[n_col]     - nonzeros
 *   T  Xx[n_col]     - input vector
 *
 * Output Arguments:
 *   T  Yx[n_row]     - output vector
 *
 * Note:
 *   Output array Yx must be preallocated
 *
 *   Complexity: Linear.  Specifically O(nnz(A) + n_col)
 *
 */
void scsc_matvec(int n_row, int n_col, int nnz,
            int *Ap, int *Ai, float *Ax, float *Xx, float *Yx)
{
    int col_start, col_end, j, ii, i;

    for( j = 0; j < n_col; j++){
        col_start = Ap[j];
        col_end   = Ap[j+1];

        for( ii = col_start; ii < col_end; ii++){
            i    = Ai[ii];
            Yx[i] += Ax[ii] * Xx[j];
        }
    }
}

void dcsc_matvec(int n_row, int n_col, int nnz,
            int *Ap, int *Ai, double *Ax, double *Xx, double *Yx)
{
    int col_start, col_end, j, ii, i;

    for( j = 0; j < n_col; j++){
        col_start = Ap[j];
        col_end   = Ap[j+1];

        for( ii = col_start; ii < col_end; ii++){
            i    = Ai[ii];
            Yx[i] += Ax[ii] * Xx[j];
        }
    }
}

/*
 * Compute Y += A*X for CSC matrix A and dense block vectors X,Y
 * From scipy/sparse/sparsetools/csc.h
 *
 *
 * Input Arguments:
 *   I  n_row            - number of rows in A
 *   I  n_col            - number of columns in A
 *   I  n_vecs           - number of column vectors in X and Y
 *   I  Ap[n_row+1]      - row pointer
 *   I  Aj[nnz(A)]       - column indices
 *   T  Ax[nnz(A)]       - nonzeros
 *   T  Xx[n_col,n_vecs] - input vector
 *
 * Output Arguments:
 *   T  Yx[n_row,n_vecs] - output vector
 *
 * Note:
 *   Output array Yx must be preallocated
 *
*/
void scsc_matvecs(int n_row, int n_col, int n_vecs,
      int *Ap, int *Ai, float *Ax, float *Xx, float *Yx)
{
  int i, j, ii;
  printf("dcsc_matvecs not working in m_sparsetools.c\n");
  exit(1);
  /*
  # pragma omp parallel \
  shared (n_row, n_col, n_vecs, Ap, Ai, Ax, Xx, Yx) \
  private (i, ii, j)
  {
    #pragma omp for
    for( j = 0; j < n_col; j++){
      for( ii = Ap[j]; ii < Ap[j+1]; ii++){
        i = Ai[ii];
        //axpy(n_vecs, Ax[ii], Xx + (int)n_vecs * j, Yx + (int)n_vecs * i);
        cblas_saxpy (n_vecs, Ax[ii], &Xx[n_vecs*j], 1, &Yx[n_vecs*i], 1);
      }
    }
  //}
    */
}

void dcsc_matvecs(int n_row, int n_col, int n_vecs,
      int *Ap, int *Ai, double *Ax, double *Xx, double *Yx)
{
  int i, j, ii;
  printf("dcsc_matvecs not working in m_sparsetools.c\n");
  exit(1);
  /*
  # pragma omp parallel \
  shared (n_row, n_col, n_vecs, Ap, Ai, Ax, Xx, Yx) \
  private (i, ii, j)
  {
    #pragma omp for
    for( j = 0; j < n_col; j++){
      for( ii = Ap[j]; ii < Ap[j+1]; ii++){
        i = Ai[ii];
        //axpy(n_vecs, Ax[ii], Xx + (int)n_vecs * j, Yx + (int)n_vecs * i);
        cblas_daxpy (n_vecs, Ax[ii], &Xx[n_vecs*j], 1, &Yx[n_vecs*i], 1);
      }
    }
  //}
  */
}


/*System includes*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

/*GSL includes*/
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_sf.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_statistics_double.h>
#include <gsl/gsl_fft_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_multimin.h>
#include <pthread.h>

/*User includes*/

#include "Subsample.h"

static char *usage[] = {"Subsample - Subsamples an emprical rarefraction curve by resampling\n",
                        "Required parameters:\n",
			"   -in   filename      parameter file  \n",
			"   -i    integer       sample to subsample from\n",
			"   -r    integer       number of samples to generate\n",
			"   -n    integer       new sample size\n",
                        "Optional:\n",
			"   -seed long          seed random number generator\n",
			"   -v                  verbose\n"};

static int  nLines   = 9;

static int  verbose  = FALSE;

void setCP(double* adP, double *adC, int *anN, int nS, int nL)
{
  int i = 0;

  for(i = 0; i < nS; i++){
    adP[i] = ((double) anN[i])/((double) nL);
  }

  adC[i] = 0;
  for(i = 1; i <= nS; i++){
    adC[i] = adP[i - 1] + adC[i - 1];
  }
}

int sampleSpecies(gsl_rng *ptGSLRNG, double *adC, int nS)
{
  double dRand = gsl_rng_uniform(ptGSLRNG);
  int    nRet  = ((double) nS)*dRand;

  while(dRand <= adC[nRet]) nRet--;

  while(dRand > adC[nRet + 1]) nRet++;

  return nRet;
}

void writeSample(t_Params *ptParams, int nSample, int nR, int nS, int* anF)
{
  FILE *ofp = NULL;
  int i   = 0;
  char szFileName[MAX_LINE_LENGTH];

  sprintf(szFileName, "%s_%d_%d.dat",ptParams->szOutFileStub,nR, nSample);

  ofp = fopen(szFileName, "w");

  for(i = 0; i < nS; i++){
    fprintf(ofp,"%d %d\n",i, anF[i]);
  }

  fclose(ofp);
}

int main(int argc, char* argv[])
{
  int  i = 0, j = 0, r = 0;
  int  nCount = 0, nRS = 0, nD = 0;
  t_Params tParams;
  int      nS = 0, nL = 0;
  double   *adP = NULL;
  double   *adC = NULL;
  int      *anN = NULL;
  int      **aanF = NULL;
  gsl_rng            *ptGSLRNG = NULL;
  const gsl_rng_type *ptGSLRNGType = NULL;

  gsl_rng_env_setup();

  gsl_set_error_handler_off();

  /*get command line params*/
  getCommandLineParams(&tParams, argc, argv);

  ptGSLRNGType = gsl_rng_default;
  ptGSLRNG     = gsl_rng_alloc(ptGSLRNGType);

  gsl_rng_set(ptGSLRNG, tParams.lSeed);

  /*read in abundance distribution*/
  readAbundanceData(tParams.nI, tParams.szInputFile,&anN,&nS);

  for(i = 0; i < nS; i++){
    nL += anN[i];
  }

  adP = (double *) malloc(sizeof(double)*nS);
  adC = (double *) malloc(sizeof(double)*(nS + 1));
  aanF = (int    **) malloc(sizeof(int*)*nS);
  for(i = 0; i < nS; i++){
    aanF[i] = (int *) malloc(tParams.nR*sizeof(int));
  }

  setCP(adP, adC, anN, nS, nL);

  while(r < tParams.nR){

    for(i = 0; i < nS; i++){
      aanF[i][r] = 0;
    }

    for(i = 0; i < tParams.nN; i++){
      nD = sampleSpecies(ptGSLRNG, adC, nS);

      aanF[nD][r]++;
    }/*sample*/

    r++;
  }
  gsl_rng_free(ptGSLRNG);

  printf("OTU,");
  for(r = 0; r < tParams.nR - 1; r++){
    printf("r%d,",r);
  }
  printf("r%d\n",r);

  for(i = 0; i < nS; i++){
    printf("C%d,",i);
    for(r = 0; r < tParams.nR - 1; r++){
      printf("%d,",aanF[i][r]);
    }
    printf("%d\n",aanF[i][r]);
  }

  for(i = 0; i < nS; i++){
    free(aanF[i]);
  }
  free(aanF);
  free(anN);
  free(adC);
  free(adP);

  exit(EXIT_SUCCESS);
}

void writeUsage(FILE* ofp)
{
  int i = 0;
  char *line;

  for(i = 0; i < nLines; i++){
    line = usage[i];
    fputs(line,ofp);
  }
}

char *extractParameter(int argc, char **argv, char *param,int when)
{
  int i = 0;

  while((i < argc) && (strcmp(param,argv[i]))){
    i++;
  }

  if(i < argc - 1){
    return(argv[i + 1]);
  }

  if((i == argc - 1) && (when == OPTION)){
    return "";
  }

  if(when == ALWAYS){
    fprintf(stdout,"Can't find asked option %s\n",param);
  }

  return (char *) NULL;
}

void getCommandLineParams(t_Params *ptParams,int argc,char *argv[])
{
  char *szTemp = NULL;
  char *cError = NULL;

  /*get parameter file name*/
  ptParams->szInputFile  = extractParameter(argc,argv, INPUT_FILE,ALWAYS);
  if(ptParams->szInputFile == NULL)
    goto error;

  /*get out file stub*/
  //ptParams->szOutFileStub  = extractParameter(argc,argv,OUT_FILE_STUB,ALWAYS);
  //if(ptParams->szOutFileStub == NULL)
    //goto error;

  szTemp  = extractParameter(argc,argv,SAMPLE,OPTION);
  if(szTemp != NULL){
    ptParams->nI = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->nI = 1;
  }

  szTemp  = extractParameter(argc,argv,N_SAMPLES,OPTION);
  if(szTemp != NULL){
    ptParams->nR = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->nR = 10;
  }

  szTemp  = extractParameter(argc,argv,N_SIZE,OPTION);
  if(szTemp != NULL){
    ptParams->nN = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->nN = 1000;
  }

  /*get out file stub*/
  szTemp  = extractParameter(argc,argv,SEED,OPTION);
  if(szTemp != NULL){
    ptParams->lSeed = strtol(szTemp,&cError,10);
    if(*cError != '\0'){
      goto error;
    }
  }
  else{
    ptParams->lSeed = 0;
  }

  /*verbosity*/
  szTemp = extractParameter(argc, argv, VERBOSE, OPTION);
  if(szTemp != NULL){
    verbose = TRUE;
  }

  return;

 error:
  writeUsage(stdout);
  exit(EXIT_FAILURE);
}

void readAbundanceData(int nI, const char *szFile, int **panN, int *pnS)
{
  int  *anN = NULL;
  int  i = 0, j = 0, nS = 0;
  char szLine[MAX_LINE_LENGTH];
  FILE* ifp = NULL;

  ifp = fopen(szFile, "r");

  if(ifp){
    char* szTok   = NULL;
    char* pcError = NULL;

    fgets(szLine, MAX_LINE_LENGTH, ifp);

    while(fgets(szLine, MAX_LINE_LENGTH, ifp) != NULL){
    	nS++;
    }
    fclose(ifp);

    ifp = fopen(szFile, "r");
    fgets(szLine, MAX_LINE_LENGTH, ifp);
    anN = (int *) malloc(nS*sizeof(int));

    for(i = 0; i < nS; i++){

      fgets(szLine, MAX_LINE_LENGTH, ifp);

      szTok = strtok(szLine, DELIM);

      for(j = 0; j < nI; j++){
 	szTok = strtok(NULL, DELIM);
      }
      anN[i] = strtol(szTok,&pcError,10);

      if(*pcError != '\0'){
	goto formatError;
      }
    }
  }
  else{
    fprintf(stderr, "Failed to open abundance data file %s aborting\n", szFile);
    fflush(stderr);
    exit(EXIT_FAILURE);
  }

  (*pnS) = nS;
  (*panN) = anN;
  return;

 formatError:
  fprintf(stderr, "Incorrectly formatted abundance data file\n");
  fflush(stderr);
  exit(EXIT_FAILURE);
}

int compare_doubles(const void* a, const void* b)
{
  double* arg1 = (double *) a;
  double* arg2 = (double *) b;
  if( *arg1 < *arg2 ) return -1;
  else if( *arg1 == *arg2 ) return 0;
  else return 1;
}