penGAM <- function(x, y, lambda.pen, lambda.curv, knots,
                   model = LinReg(), save.x = TRUE,
                   control = grpl.control()){
  ## Purpose:
  ## ----------------------------------------------------------------------
  ## Arguments:
  ## ----------------------------------------------------------------------
  ## Author: Lukas Meier, Date: 13 Mar 2008, 11:30

  if(any(lambda.pen > 1))
    stop("lambda.pen is on a *relative* scale with respect to lambda.max")

  n <- NROW(x)
  p <- NCOL(x)

  ## Construct the design matrix and the penalty matrices
  d <- getDesign(x, knots = knots)

  ## Extract the necessary information
  m.orig <- d$m     ## list of p design matrices
  pen    <- d$pen   ## list of p penalty matrices
  index  <- d$index ## index vector (without intercept)

  ## Coefficients for all combinations of lambda.pen * lambda.curv * coef
  coefficients <- array(0,
                        dim = c(length(lambda.pen),
                                length(lambda.curv),
                                length(index) + 1))

  lambda.max.all <- numeric(length(lambda.curv))

  for(u in 1:length(lambda.curv)){
    if(control@trace >= 1)
      cat("\n*** lambda.curv", lambda.curv[u], "***\n")

    lcurv <- lambda.curv[u]

    ## m contains the whole design matrix but *without* intercept column
    m           <- matrix(0, nrow = n, ncol = length(index))
    colnames(m) <- unlist(lapply(m.orig, colnames))

    ## We have to calculate the penalty matrix for each value of lambda.curv
    ## Transform each predictor group such that we can work with an
    ## "ordinary" Group Lasso model

    chol.decomp <- list(); length(chol.decomp) <- p

    for(j in 1:p){
      ## This will be the penalty matrix for each block
      penmat  <- lcurv * pen[[j]] + crossprod(m.orig[[j]])

      ## Now calculate the cholesky decomposition
      chol.decomp[[j]] <- chol(penmat)
      m[,index == j] <-
        t(forwardsolve(t(chol.decomp[[j]]), t(m.orig[[j]])))
    }

    ## Now we are ready to get the estimates using the grplasso function
    ## for *all* values of lambda.pen

    ## We do *not* have to rescale the penalty
    mypenscale <- function(x) rep(1, length(x))

    ## Calculate lambda.max
    lambda.max <- lambdamax(x = cbind(1, m), y = y, index = c(NA, index),
                            penscale = mypenscale, model = model,
                            standardize = FALSE)

    lambda.max.all[u] <- lambda.max

    ## Actual lambda values
    lambda.use <- lambda.max * lambda.pen

    coef.init    <- rep(0, NCOL(m) + 1)
    coef.init[1] <- model@link(mean(y))

    ## Call now group lasso algorithm on the *transformed* data
    fit.grp <- grplasso(x = cbind(1, m), y = y, index = c(NA, index),
                        lambda = lambda.use, model = model,
                        coef.init = coef.init,
                        standardize = FALSE, penscale = mypenscale,
                        control = control)

    ## Transform the estimates back to the original scale using backsolve
    coef.pre <- coef(fit.grp)[-1,,drop = FALSE] ## without intercept
    coef.out <- matrix(0, nrow = NROW(coef.pre), ncol = NCOL(coef.pre))
    for(j in 1:p){
      sel            <- index == j
      coef.out[sel,] <- backsolve(chol.decomp[[j]], coef.pre[sel,])
    }
    ## Now transform the values back to the original spline-basis and
    ## add intercept.
    ## Put everything into the "coefficients" array.
    coefficients[,u,] <- t(rbind(coef(fit.grp)[1,], coef.out))
  } ## end for(u in 1:length(lambda.curv))

  dimnames(coefficients) <- list(lambda.pen,
                                 lambda.curv,
                                 c("(Intercept)", colnames(m)))

  if(is.null(colnames(x))){
    colnam <- 1:NCOL(x)
  }else{
    colnam <- colnames(x)
  }

  rownam <- rownames(x)

  if(!save.x)
    x <- NULL

  out <- list(coefficients = coefficients,
              x            = x,
              colnames     = colnam,
              rownames     = rownam,
              splineobj    = list(basis = d$basis, index = d$index),
              lambda.max   = lambda.max.all,
              lambda.pen   = lambda.pen,
              lambda.curv  = lambda.curv,
              model        = model)
  structure(out, class = "penGAM")
}

predict.penGAM <- function(object, newdata, type = c("link", "response"), ...)
{
  ## Purpose: Predict fitted object on a new data set
  ## ----------------------------------------------------------------------
  ## Arguments: fit object
  ## ----------------------------------------------------------------------
  ## Author: Lukas Meier, Date: 13 Mar 2008, 12:09

  type <- match.arg(type)

  nr.lambda.pen  <- length(object$lambda.pen)
  nr.lambda.curv <- length(object$lambda.curv)

  if(missing(newdata) || is.null(newdata)){ ## if we don't have new data object
    if(is.null(object$x))
      stop("No x matrix found in 'object'. Set save.x = TRUE or provide newdata!")

    ## Create the whole design matrix (*all* components)
    ## Re-create the design matrix using the given spline basis
    m.list <- list()
    for(j in 1:NCOL(object$x))
      m.list[[j]] <- eval.basis(x[,j], basisobj = object$splineobj$basis[[j]])

    n    <- NROW(m.list[[1]])
    pred <- array(0, dim = c(nr.lambda.pen, nr.lambda.curv, n))

    ## Create predictions
    m <- matrix(unlist(m.list), nrow = n)
    for(l in 1:nr.lambda.pen){
      for(u in 1:nr.lambda.curv){
        pred[l,u,] <- cbind(1, m) %*% coef(object)[l,u,]
      }
    }
    dimnames(pred) <- list(object$lambda.pen, object$lambda.curv,
                           object$rownames)
  }else{ ## if we have *new* data object (!!!add some test-functions later!!!)
    x <- newdata
    n <- NROW(x); p <- NCOL(x)

    pred <- array(0, dim = c(nr.lambda.pen, nr.lambda.curv, n))
    dimnames(pred) <- list(object$lambda.pen, object$lambda.curv, rownames(x))
    lowdiff <- highdiff <- outrange <-
      matrix(FALSE, nrow = NROW(newdata), ncol = p)

    ## Perform *linear* extrapolation

    ## Create the 'cut' design matrix: Sets values of x which exceed the
    ## training range to their corresponding boundaries (given by the range
    ## of the training data)
    x.cut  <- matrix(0, nrow = n, ncol = p)
    m.list <- m.deriv.list <- list()
    length(m.list) <- length(m.deriv.list) <- p

    lower.slopes <- upper.slopes <- list()

    for(j in 1:p){
      ## Extract current predictor
      x.current     <- x[,j]
      x.cut[,j]     <- x.current

      ## Current basis matrix (B-spline basis)
      bas           <- object$splineobj$basis[[j]]

      ## Get the smallest and largest knots (=boundaries)
      lower.end     <- bas$rangeval[1]
      upper.end     <- bas$rangeval[2]

      ## Which observations are exceeding the boundary-values?
      ind.lower     <- x.current < lower.end
      ind.upper     <- x.current > upper.end

      lowdiff[ind.lower,j]  <- (x.current - lower.end)[ind.lower]
      highdiff[ind.upper,j] <- (x.current - upper.end)[ind.upper]

      x.cut[ind.lower,j] <- lower.end
      x.cut[ind.upper,j] <- upper.end

      ## Get the slopes at the boundaries
      m.list[[j]] <- eval.basis(x.cut[,j], bas)
      deriv.info  <- eval.basis(c(lower.end, upper.end), bas, Lfdobj = 1)

      lower.slopes[[j]] <- deriv.info[1,]
      upper.slopes[[j]] <- deriv.info[2,]
    }
    m     <- matrix(unlist(m.list), nrow = n) ## Gets filled *columnwise*
    index <- object$splineobj$index ## without intercept

    for(l in 1:nr.lambda.pen){
      ##cat(l, "\n")
      for(u in 1:nr.lambda.curv){
        beta <- coef(object)[l,u,]

        pred.pre <- cbind(1, m) %*% coef(object)[l,u,]

        ## Put the design matrix of the first derivates (lower.slopes,
        ## upper.slopes) into one long vector each (same length as index) and
        ## multiply with beta vector and take the sum. I.e. perform the matrix
        ## operation in a bit a special form.
        ## The result are the derivatives at the left- and the right-hand side
        ## boundaries of the training range (of the fitted object with the
        ## current coefficients)
        slopes.left  <- rowsum(unlist(lower.slopes) * beta[-1],
                               group = index)
        slopes.right <- rowsum(unlist(upper.slopes) * beta[-1],
                               group = index)

        ## Now we have to multiply the derivatives with the difference
        ## in the x-values (contained in lowdiff and highdiff)
        ## lowdiff and highdiff are matrices with dimensions n x p, i.e. the
        ## dimension of the newdata object.
        ## Each column of lowdiff and highdiff is multiplied with the slope
        ## value. The result will be what we have to add beyond the boundaries.
        ## add.left and add.right will also have dimension n x p.

        ## 'as.array' is here to force a warning message if recycling would
        ## take place (see help file of sweep)
        add.left  <- sweep(lowdiff, MARGIN = 2, STATS = as.array(slopes.left),
                           FUN = "*")
        add.right <- sweep(highdiff, MARGIN = 2, STATS = as.array(slopes.right),
                           FUN = "*")

        ## Calculate the final prediction:
        ## Take the prediction of the 'cut-down' matrix and add the linear
        ## extrapolation part (add.left + add.right). We have to take the sum
        ## in each row of the linear extrapolation part (add.left + add.right)
        pred[l,u,] <- pred.pre + rowSums(add.left + add.right)
      }
    }
  }

  ## Transform to original scale if necessary
  pred <- switch(type,
                 link     = pred,
                 response = object$model@invlink(pred))

  dims <- dim(pred)

  ## If we have only one combination of lpen/lcurv we allow drop = TRUE.
  ## In all other cases
  if(dims[1] == 1 & dims[2] == 1)
    pred <- pred[1,1,,drop = TRUE]

  attr(pred, "lambda.pen")  <- object$lambda.pen
  attr(pred, "lambda.curv") <- object$lambda.curv

  pred
  ##out <- list(pred = pred,
  ##            lambda.pen  = object$lambda.pen,
  ##            lambda.curv = object$lambda.curv)
  ##out
}

plot.penGAM <- function(x, which = NULL, ask = TRUE && dev.interactive(),
                        nrgrid = 100, ...)
{
  ## Purpose:
  ## ----------------------------------------------------------------------
  ## Arguments:
  ## ----------------------------------------------------------------------
  ## Author: Lukas Meier, Date:  6 May 2008, 12:08

  n      <- NROW(x$splineobj$m[[1]])
  index  <- x$splineobj$index

  p      <- index[length(index)]

  dims   <- dim(coef(x))
  if(dims[1] > 1 | dims[2] > 1)
    stop("Use plot(fit[i,j]) to select the desired penalty parameters")

  coefs <- coef(x)[1, 1,][-1] ## without intercept!

  if(is.null(which))
    which <- 1:p
  if(ask){
    op <- par(ask = TRUE)
    on.exit(par(op)) ## use original settings when we leave the function
  }

  for(j in 1:length(which)){
    bas       <- x$splineobj$basis[[j]]
    rng       <- bas$range
    x.current <- seq(rng[1], rng[2], length = nrgrid)
    m.current <- eval.basis(x.current, basisobj = bas)
    ind       <- index == which[j]
    fn        <- scale(m.current %*% coefs[ind], scale = FALSE)
    plot(sort(x.current), fn[order(x.current)], type = "l",
         xlab = x$colnames[which[j]], ylab = bquote(f[.(which[j])]), ...)
  }
}

"[.penGAM" <- function(x, i, j){
  ## Purpose:
  ## ----------------------------------------------------------------------
  ## Arguments:
  ## ----------------------------------------------------------------------
  ## Author: Lukas Meier, Date:  6 May 2008, 12:08

  ## First get dimensions of the original object x
  dim <- dim(coef(x))
  nrlambda.pen  <- dim[1]
  nrlambda.curv <- dim[2]

  if(missing(i))
    i <- 1:nrlambda.pen
  if(missing(j))
    j <- 1:nrlambda.curv

  ## Subset the object
  fit.red <- x

  fit.red$coefficients <- coef(x)[i,j,,drop = FALSE]

  ## We do not allow the situation where everything is removed
  if(length(fit.red$coefficients) == 0)
    stop("Not allowed to remove everything!")

  fit.red$lambda.max   <- x$lambda.max[j]
  fit.red$lambda.pen   <- x$lambda.pen[i]
  fit.red$lambda.curv  <- x$lambda.curv[j]

  fit.red
}

getDesign <- function(x, knots = 10, norder = 4)
{
  ## Purpose: Get design matrix and penalty matrix in the basis of
  ##          B-splines
  ## ----------------------------------------------------------------------
  ## Arguments: x:       Design matrix, *without* intercept column
  ##            nrknots: Number of quantiles at which to put knots
  ##                     (these will be evenly spaced on the quantile scale
  ##                      between 0 and 1)
  ##            norder:  Set norder = 4 for piecewise cubic polynomials
  ## ----------------------------------------------------------------------
  ## Author: Lukas Meier, Date:  4 Mar 2008, 14:49

  p <- NCOL(x)

  if(is.null(colnames(x)))
    colnames(x) <- paste("x", 1:p, sep = "")

  ## If knots is only a number, we have to create the knots first for each
  ## predictor
  if(is.numeric(knots)){
    knots.use <- list()
    for(j in 1:p){
      x.current <- x[,j]
      ## Make sure that we do not place multiple knots at the same
      ## x value (if we have replicates etc.)
      ux.current <- unique(x.current)

      if(knots > length(ux.current)){
        nrknots.use <- length(ux.current)
        cat("Too many knots in predictor", j, "...\n")
        cat("Using as many knots as there are unique predictor values.\n")
      }else{
        nrknots.use <- knots
      }
      knots.use[[j]] <- quantile(ux.current, seq(0, 1, length = nrknots.use))
    }
  }else if(is.list(knots)){
    if(length(list) != p)
      stop("knots has wrong length")

    knots.use <- knots
  }

  ## Create the design and the penalty matrix
  m     <- list()
  pen   <- list()
  basis <- list()
  length(m) <- length(pen) <- length(basis) <- p

  df    <- numeric(p)

  for(j in 1:p){
    x.current <- x[,j]

    ## Create knot sequence
    breaks  <- knots.use[[j]]
    stopifnot(all.equal(range(breaks), range(x.current)))

    ## Get basis-object for the j-th spline
    basis[[j]] <- create.bspline.basis(rangeval = range(breaks),
                                       breaks = breaks, norder = norder)
    ## removed range(x.current)

    ## Get the design matrix for the j-th spline (with the basis of above)
    m[[j]] <- eval.basis(x.current, basis[[j]])

    ## Get the degrees of freedom for the current predictor
    df[j] <- NCOL(m[[j]])

    colnames(m[[j]]) <- paste(colnames(x)[j], ".", 1:df[j], sep = "")

    ## Get penalty matrix ## !!! DOUBLE-CHECK THIS !!!
    pen[[j]] <- getbasispenalty(basis[[j]], Lfdobj = 2)
  }
  list(m = m, pen = pen, basis = basis, index = rep(1:p, times = df))
}

.onAttach <- function(libname, pkgname){
  cat("----------------------------------------------------------------------",
      "Please note that this is an early test release of package 'penGAM'.",
      "It should only be used for experimental reasons. Use at your own risk!",
      "----------------------------------------------------------------------",
      sep = "\n")
}


###############################################################################
# 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 3 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, see <http://www.gnu.org/licenses/>.
###############################################################################
# Optimization Functions
# Copyright (C) 2011  Michael Kapler
#
# For more information please visit my blog at www.SystematicInvestor.wordpress.com
# or drop me a line at TheSystematicInvestor at gmail
###############################################################################

###############################################################################
# Solve LP problem, handling negative x
###############################################################################
# Tradional LP problem : http://lpsolve.sourceforge.net/5.5/LPBasics.htm
# maximize     C x
# subject to   A x <= B
#              x >= 0 - NOTE LP assumes all x >= 0
###############################################################################
# http://r.789695.n4.nabble.com/help-using-R-s-linprog-for-LP-td906987.html
# NOTE: Linear Programming (LP) assumes x >= 0
#
# The constraints x >= 0 are used in most linear programming realizations.
# Some bounds from below are needed. The trick to circumvent the restriction
# is as follows:
#
# Assume you know  x >= d  where some or all of the d_i can be negative.
# Replace x with  y = x - d >= 0  and minimize c'y with  Ay <= b - A d !
# Your solution is then  x = y + d , that is
#
# solveLP(cvec, bvec - Amat %*% dvec, Amat)$solution + dvec
#
# For portfolio weights, it is safe to assume that x.i >= -100
#' @export
###############################################################################
solve.LP.bounds <- function
(
	direction, 		# lp parameters
	objective.in, 	# lp parameters
	const.mat, 		# lp parameters
	const.dir,		# lp parameters
	const.rhs, 		# lp parameters
	binary.vec = 0,	# lp parameters
	lb = 0,			# vector with lower bounds
	ub = +Inf,		# vector with upper bounds
	default.lb = -100	# the smallest possible value for any variable
)
{
	# number variables
	n = len(objective.in)

	if( len(lb) == 1 ) lb = rep(lb, n)
	if( len(ub) == 1 ) ub = rep(ub, n)

	lb = ifna(lb, default.lb)
	ub = ifna(ub, +Inf)

	lb[ lb < default.lb ] = default.lb
	dvec = lb

	# add ub constraints
	index = which( ub < +Inf )
	if( len(index) > 0 ) {
		const.rhs = c(const.rhs, ub[index])
		const.dir = c(const.dir, rep('<=', len(index)))
		const.mat = rbind(const.mat, diag(n)[index, ])
	}

	# main logic
	if ( binary.vec[1] == 0 ) {
		sol = lp( direction, objective.in, const.mat, const.dir,
				const.rhs - const.mat %*% dvec )
	} else {
		dvec[binary.vec] = 0
		sol = lp( direction, objective.in, const.mat, const.dir,
				const.rhs - const.mat %*% dvec, binary.vec = binary.vec )

	}

	# update solution
	sol$solution = sol$solution + dvec
	sol$value = objective.in %*% sol$solution

	return( sol )
}

###############################################################################
# Solve QP problem, handling binary variables using Binary Branch and Bound
#' @export
###############################################################################
solve.QP.bounds <- function
(
	Dmat,				# solve.QP parameters
	dvec, 				# solve.QP parameters
	Amat, 				# solve.QP parameters
	bvec, 				# solve.QP parameters
	meq=0, 				# solve.QP parameters
	factorized=FALSE, 	# solve.QP parameters
	binary.vec = 0,		# vector with indices of binary variables
	lb = -Inf,			# vector with lower bounds
	ub = +Inf			# vector with upper bounds
)
{
	Amat1 = Amat
	bvec1 = bvec

	# number variables
	n = len(dvec)

	if( len(lb) == 1 ) lb = rep(lb, n)
	if( len(ub) == 1 ) ub = rep(ub, n)

	lb = ifna(lb, -Inf)
	ub = ifna(ub, +Inf)

	# add ub constraints, A^T b >= b_0
	index = which( ub < +Inf )
	if( len(index) > 0 ) {
		bvec = c(bvec, -ub[index])
		Amat = cbind(Amat, -diag(n)[, index])
	}

	# add lb constraints, A^T b >= b_0
	index = which( lb > -Inf )
	if( len(index) > 0 ) {
		bvec = c(bvec, lb[index])
		Amat = cbind(Amat, diag(n)[, index])
	}

	# main logic
	if ( binary.vec[1] == 0 ) {
		# logic to check for constant variables
		qp.data.final = solve.QP.remove.equality.constraints(Dmat, dvec, Amat, bvec, meq)
			Dmat = qp.data.final$Dmat
			dvec = qp.data.final$dvec
			Amat = qp.data.final$Amat
			bvec = qp.data.final$bvec
			meq = qp.data.final$meq

		# solve.QP: min(-d^T w.i + 1/2 w.i^T D w.i) subject to A^T w.i >= b_0
		sol = try(solve.QP(Dmat, dvec, Amat, bvec, meq, factorized),TRUE)

		# lot's of errors take place because covariance matrix is not positive defined
		if(inherits(sol, 'try-error')) {
#cat('solve.QP error\n')
#print(sol)
			ok = F
			sol = list()
		} else {
			# check that solve.QP solution satisfies constraints
			tol = 1e-3
			ok = T

			check = sol$solution %*% Amat - bvec
			if(meq > 0) ok = ok & all(abs(check[1:meq]) <= tol)
			ok = ok & all(check[-c(1:meq)] > -tol)
#if(!ok) cat('solve.QP violates constraints\n')
#if(!ok) print(sol$solution)
		}

		# try to solve problem one more time using ipop function from kernlab package
		if(!ok) {
			require(kernlab)
#cat('kernlab\n')
			index.constant.variables = which(!is.na(qp.data.final$solution))
			if( len(index.constant.variables) > 0 ) {
				# Amat1 and bvec1 might be incorrect
				Amat1 = Amat[,1:ncol(Amat1)]
				bvec1 = bvec[1:ncol(Amat1)]
				lb = lb[-index.constant.variables]
				ub = ub[-index.constant.variables]
			}

			# ipop: min(c'*x + 1/2 * x' * H * x) subject to b <= A * x <= b + r and l <= x <= u
			sv = ipop(c = matrix(-dvec), H = Dmat, A = t(Amat1),
					b = bvec1, l = ifna(lb,-100), u = ifna(ub,100),
					r = c(rep(0,meq), rep(100, len(bvec1) - meq))
				)
			sol$solution = primal(sv)
		}

		# logic to check for constant variables
		x = qp.data.final$solution
		x[qp.data.final$var.index] = sol$solution
		sol$solution = x

	} else {
		# use Binary Branch and Bound
		qp_data = qp_new(binary.vec, Dmat = Dmat, dvec = dvec,
						Amat=Amat, bvec=bvec, meq=meq, factorized=factorized)

		sol = binary_branch_bound(binary.vec, qp_data, qp_solve,
				control = bbb_control(silent=T, branchvar='max', searchdir='best' ))

		qp_delete(qp_data)

		sol$value = sol$fmin
		sol$solution = sol$xmin
	}

	return(sol)
}

###############################################################################
# QP interface for Binary Branch and Bound algorithm
###############################################################################
# control list for binary_branch_bound
#' @export
###############################################################################
bbb_control <- function
(
	itermax = 200, 			# maximum number of iterations
	depthmax = Inf, 		# maximum search depth
	bineps = 1e-4, 			# eps to determine whether a solution is 0/1
	precisioneps = 0, 		# stop condition for incremental improvement in best solution
	silent = T,				# quite or verbose
	branchvar = c('first', 'max','min'),
							# first = first free variable
							# max = variable with max frac part
							# min = variable with min frac part
	proborder = c('0', '1', 'mindiff'),
							# 0 = problems are put onto the stack '0' first, '1' next
							# 1 = problems are put onto the stack '1' first, '0' next
							# mindiff = problems are put onto the stack closest to '0' or '1' first
	searchdir = c('depth', 'breadth', 'best', 'normbest')
							# depth = depth first
							# breadth = breadth first
							# best = best first
							# normbest = best first with normalized cost
)
{
	# parse default arguments
	branchvar = switch(branchvar[1],
						'first' = 0,
						'max' = 1,
						'min' = 2,
						0)
	branchvar = iif( is.null(branchvar),0, branchvar)

	proborder = switch(proborder[1],
						'0' = 0,
						'1' = 1,
						'mindiff' = 2,
						0)
	proborder = iif( is.null(proborder),0, proborder)

	searchdir = switch(searchdir[1],
						'depth' = 0,
						'breadth' = 1,
						'best' = 2,
						'normbest' = 2,
						0)
	searchdir = iif( is.null(searchdir),0, searchdir)

	control = list(itermax = itermax, depthmax = depthmax, bineps = bineps, precisioneps = precisioneps, silent = silent,
				branchvar = branchvar,
				proborder = proborder,
				searchdir = searchdir)
	return(control)
}

###############################################################################
# Initialize QP
###############################################################################
qp_new <- function
(
	index_binvar, 	# index of binary variables
	Dmat, 			# paramters from solve.QP from quaprog library
	dvec,
	Amat,
	bvec,
	meq = 0,
	factorized = FALSE
)
{
	# add 0,1 bounds for the binary variables
		nbinvar = length(index_binvar)
		nx = nrow(Dmat)
		nbvec = length(bvec)

	# Note 0, -1 bounds 0 to 1
	Amat = cbind( Amat, diag(nx)[,index_binvar], -diag(nx)[,index_binvar] )
	bvec = c(bvec, rep(0,nx)[index_binvar], rep(1,nx)[index_binvar] )
		lb_bin_index = (1:nbinvar) + nbvec
		ub_bin_index = (1:nbinvar) + nbvec + nbinvar

	# create output
	qp_data = new.env()
		qp_data$Dmat = Dmat
		qp_data$dvec = dvec
		qp_data$Amat = Amat
		qp_data$bvec = bvec
		qp_data$meq = meq
		qp_data$factorized = factorized
		qp_data$x0 = rep(0,nx)

		qp_data$lb_bin_index = lb_bin_index
		qp_data$ub_bin_index = ub_bin_index
		qp_data$lb = bvec[lb_bin_index]
		qp_data$ub = bvec[ub_bin_index]

	return(qp_data)
}

###############################################################################
# Delete QP
###############################################################################
qp_delete <- function(qp_data)
{
	rm(list = ls(qp_data,all=TRUE), envir = qp_data)
}

###############################################################################
# Solve QP problem
###############################################################################
qp_solve <- function
(
	qp_data,
	lb,
	ub
)
{
	bvec = qp_data$bvec
	bvec[qp_data$lb_bin_index] = lb
	# Note 0, -1 bounds 0 to 1
	bvec[qp_data$ub_bin_index] = -ub

	# NEW logic to remove reduandant equality constraints in QP problem
	qp.data.final = solve.QP.remove.equality.constraints(qp_data$Dmat, qp_data$dvec,
							qp_data$Amat, bvec, qp_data$meq)
	# end of NEW logic

	sol = tryCatch( solve.QP(Dmat=qp.data.final$Dmat, dvec=qp.data.final$dvec, Amat=qp.data.final$Amat,
						bvec=qp.data.final$bvec, meq=qp.data.final$meq, factorized=qp_data$factorized),
		error=function( err ) FALSE,
	    warning=function( warn ) FALSE )

	if( !is.logical( sol ) ) {
		x = qp.data.final$solution
		x[qp.data.final$var.index] = sol$solution

		return(list( ok = TRUE, x = x, fval = sol$value ))
	} else {
		return(list( ok = FALSE ))
	}
}

###############################################################################
# Test QP interface to Binary Branch and Bound algorithm
###############################################################################
mbqp.test <- function()
{
	load.packages('quadprog')

	# Test problem
	# min         0.5*x'Q x + b' x
	# subject to  Cx <= d
	Q = diag(4)
	b	= c( 2, -3, -2, -3)
	C	= matrix(c(-1,  -1,  -1,  -1,
    				10,	5,   3,	4,
    				-1,	0,   0,	0),
    		3,4,byrow = TRUE)
	d = c(-2, 10,  0)

	vlb  = c(-1e10, 0, 0, 0);
	vub  = c( 1e10, 1, 1, 1);

	index_binvar = c(2, 3, 4);

	# Solve.QP
	# min(-d^T b + 1/2 b^T D b) : A^T b >= b_0
	# meq the first meq constraints are treated as equality constraints, all further as inequality constraints (defaults to 0).
	Dmat = Q
	dvec = -b
	Amat = -t(C)
	bvec = -d

	# add vlb / vub
	n = nrow(Dmat)
	Amat = cbind( Amat, diag(n), -diag(n) )
	bvec = c( bvec, vlb, -vub )

	sol = solve.QP(Dmat=Dmat, dvec=dvec, Amat=Amat, bvec=bvec, meq=0)
		xsol = sol$solution
		fsol = sol$value

	cat('QP.solve fsol =', fsol, 'xsol =', xsol, '\n')

	# Start Branch and Bound
	sol = solve.QP.bounds(Dmat=Dmat, dvec=dvec, Amat=Amat, bvec=bvec, meq=0, binary.vec = index_binvar)
		xsol = sol$solution
		fsol = sol$value

	cat('QP.solve Binary Branch and Bound fsol =', fsol, 'xsol =', xsol, '\n')

}

###############################################################################
# Oriingal version is too slow working on a new faster version
###############################################################################
# Logic to remove reduandant equality constraints in QP problem, based on qp_solve
solve.QP.remove.equality.constraints <- function
(
	Dmat,				# solve.QP parameters
	dvec, 				# solve.QP parameters
	Amat, 				# solve.QP parameters
	bvec, 				# solve.QP parameters
	meq=0 				# solve.QP parameters
)
{
	qp.data = list()
		qp.data$Amat = Amat
		qp.data$bvec = bvec
		qp.data$Dmat = Dmat
		qp.data$dvec = dvec
		qp.data$meq = meq

	Amat1 = t(qp.data$Amat)
	bvec1 = qp.data$bvec
	Dmat1 = qp.data$Dmat
	dvec1 = qp.data$dvec
	meq1 = qp.data$meq

#Amat - var are rows, constr are columns
#Amat1 - constr are rows, var are columns
# 	Amat = matrix(1:20,nr=4)
#	Amat1 = t(Amat)
#	(1+0*Amat1[2:4,])/ c(1,2,3)

	# default
	qp.data$solution = rep(NA, ncol(Amat1))
	qp.data$var.index = 1:ncol(Amat1)

while(T) {

	# 1. find columns with just one non-zero element
	one.non.zero.index = which( rowSums(Amat1!=0) == 1 )

	if( len(one.non.zero.index) == 0 ) break

	# 2. divide these columns, and corresponding bvec by column's abs value
	temp0 = rowSums(Amat1[one.non.zero.index,])
	temp = abs( temp0 )
	bvec1[one.non.zero.index] = bvec1[one.non.zero.index] / temp
	Amat1[one.non.zero.index,] = Amat1[one.non.zero.index,] / temp

	temp0.index = matrix(1:ncol(Amat1), nr=ncol(Amat1), nc=len(one.non.zero.index))[t(Amat1[one.non.zero.index,]!=0)]

#Amat - var are rows, constr are columns
#Amat1 - constr are rows, var are columns

	# 3. look for pairs among one.non.zero.index colums
	# x >= value , -x>= -value => x = value
	equality.constraints = rep(NA, ncol(Amat1))
	lb = ub = rep(NA, ncol(Amat1))

	# Make sure if there are multiple matches only keep max for lb and min for ub

	index = temp0 > 0
	#lb[temp0.index[index]] = bvec1[one.non.zero.index[index]]
	##temp = tapply(bvec1[one.non.zero.index[index]], temp0.index[index], max)
	##lb[ as.double(names(temp)) ] = temp
	temp = order(bvec1[one.non.zero.index[index]], decreasing = FALSE)
	lb[temp0.index[index][temp]] = bvec1[one.non.zero.index[index]][temp]

	index = temp0 < 0
	#ub[temp0.index[index]] = -bvec1[one.non.zero.index[index]]
	##temp = tapply(-bvec1[one.non.zero.index[index]], temp0.index[index], min)
	##ub[ as.double(names(temp)) ] = temp
	temp = order(-bvec1[one.non.zero.index[index]], decreasing = TRUE)
	ub[temp0.index[index][temp]] = -bvec1[one.non.zero.index[index]][temp]

#a = rep(0,5)
#a[c(2,2,2)] = c(2,1,0)

	# 4. remove variables
	remove.index = which(lb == ub)
	if( len(remove.index) > 0 ) {
		equality.constraints[remove.index] = lb[remove.index]

		# remove variables
		Dmat1 = Dmat1[-remove.index, -remove.index,drop=F]
		dvec1 = dvec1[-remove.index]
		bvec1 = bvec1 - Amat1[,remove.index,drop=F] %*% equality.constraints[remove.index]
		Amat1 = Amat1[,-remove.index,drop=F]

		qp.data$solution[ qp.data$var.index[remove.index] ] = lb[remove.index]
		qp.data$var.index = which(is.na(qp.data$solution))

		# check if there are variables left
		if( ncol(Amat1) > 0 ) {

			# remove constraints
			remove.index = which( rowSums(Amat1!=0) == 0 & bvec1 == 0 )
			if(len(remove.index)>0) {
				bvec1 = bvec1[-remove.index]
				Amat1 = Amat1[-remove.index,,drop=F]

				# handle euality constraints
				if( meq1 > 0 ) meq1 = meq1 - len(intersect((1:meq1), remove.index))
			}
		} else break

	} else break

	}	# while(T)

	# prepare return object
	qp.data$Amat = t(Amat1)
	qp.data$bvec = bvec1
	qp.data$Dmat = Dmat1
	qp.data$dvec = dvec1
	qp.data$meq = meq1

	return(qp.data)
}

###############################################################################
# Find and remove equality constraints in QP problem (slow)
###############################################################################
# Logic to remove reduandant equality constraints in QP problem, based on qp_solve
solve.QP.remove.equality.constraints12 <- function
(
	Dmat,				# solve.QP parameters
	dvec, 				# solve.QP parameters
	Amat, 				# solve.QP parameters
	bvec, 				# solve.QP parameters
	meq=0 				# solve.QP parameters
)
{
	qp.data.temp = list()
		qp.data.temp$Amat = Amat
		qp.data.temp$bvec = bvec
		qp.data.temp$Dmat = Dmat
		qp.data.temp$dvec = dvec
		qp.data.temp$meq = meq

	qp.data.temp = remove.equality.constraints.old(qp.data.temp)
	# if no equality constraints found go to optimization
	if( len(qp.data.temp$var.index) == len(qp.data.temp$solution) ) {
		qp.data.final = qp.data.temp
	} else {
		# test for equality constraints one more time
		qp.data.final = remove.equality.constraints.old(qp.data.temp)
			qp.data.temp$solution[qp.data.temp$var.index] = qp.data.final$solution
				qp.data.final$solution = qp.data.temp$solution
			qp.data.final$var.index = qp.data.temp$var.index[qp.data.final$var.index]
	}

	return(qp.data.final)
}

remove.equality.constraints.old <- function(qp.data)
{
	#qp.data1 = qp.data.temp
	Amat1 = qp.data$Amat
	bvec1 = qp.data$bvec
	Dmat1 = qp.data$Dmat
	dvec1 = qp.data$dvec
	meq1 = qp.data$meq

	# 1. find columns with just one non-zero element
	one.non.zero.index = which( colSums(Amat1!=0) == 1 )

	if( len(one.non.zero.index) == 0 ) {
		equality.constraints = rep(NA, nrow(Amat1))
		qp.data$solution = equality.constraints
		qp.data$var.index = which(is.na(equality.constraints))
		return(qp.data)
	}

	# 2. divide these columns, and corresponding bvec by column's abs value
	bvec1[one.non.zero.index] = bvec1[one.non.zero.index] / abs( colSums(Amat1[,one.non.zero.index]) )
	Amat1[,one.non.zero.index] = Amat1[,one.non.zero.index] / abs( Amat1[,one.non.zero.index] )
	Amat1[is.na(Amat1)] = 0

	# 3. look for pairs among one.non.zero.index colums
	# x >= value , -x>= -value => x = value
	equality.constraints = rep(NA, nrow(Amat1))
	remove.constraints = rep(NA, ncol(Amat1))

	#for( r in 1:nrow(Amat1) ) {
	for( r in which(rowSums(Amat1[,one.non.zero.index]!=0) > 1) ) {
		temp.index = which( Amat1[,one.non.zero.index][r,] != 0 )

		for( r1 in temp.index[-1] ) {
			temp.index1 = colSums(abs(
			rbind(Amat1,bvec1)[,one.non.zero.index[temp.index]] +
			rbind(Amat1,bvec1)[,one.non.zero.index[r1]])
			)

			if( any(temp.index1 == 0 ) ) {
				equality.constraints[r] =
					bvec1[one.non.zero.index[r1]] / Amat1[r,one.non.zero.index[r1]]

				remove.constraints[ one.non.zero.index[r1] ] = 1
				remove.constraints[ one.non.zero.index[ temp.index[ temp.index1 == 0]]] = 1

				break;
			}
		}
	}

	# 4. remove variables
	remove.index = which(!is.na(equality.constraints))
	if(len(remove.index)>0) {
		# remove variables
		Dmat1 = Dmat1[-remove.index, -remove.index,drop=F]
		dvec1 = dvec1[-remove.index]
		bvec1 = bvec1 - equality.constraints[remove.index] %*% Amat1[remove.index,,drop=F]
		Amat1 = Amat1[-remove.index,,drop=F]

		# remove constraints
		#remove.index1 = which( colSums(abs(Amat1)) == 0 & bvec1 == 0)
		remove.index1 = which(remove.constraints==1)
		if(len(remove.index1)>0) {
			bvec1 = bvec1[-remove.index1]
			Amat1 = Amat1[,-remove.index1,drop=F]

			# handle euality constraints
			if( meq1 > 0 ) meq1 = meq1 - len(intersect((1:meq1), remove.index1))
		}
	}

	# prepare return object
	qp.data$Amat = Amat1
	qp.data$bvec = bvec1
	qp.data$Dmat = Dmat1
	qp.data$dvec = dvec1
	qp.data$meq = meq1

	qp.data$solution = equality.constraints
	qp.data$var.index = which(is.na(equality.constraints))

	return(qp.data)
}

###############################################################################
# Run linear least squares regression with constraints
#' @export
###############################################################################
lm.constraint <- function
(
	x,
	y,
	constraints = NULL
)
{
	if( is.null(constraints) ) {
		fit = lm.fit(x, y)
		return( ols.summary(x, y, fit$coefficients) )
	} else {
    	temp = cov(cbind(y, x))
		Dmat = temp[-1,-1]
		dvec = temp[-1,1]

		sol = solve.QP.bounds(Dmat = Dmat, dvec = dvec ,
				Amat=constraints$A, bvec=constraints$b, constraints$meq,
				lb = constraints$lb, ub = constraints$ub)
		return( ols.summary(x, y, sol$solution) )
	}
}

###############################################################################
# Run linear least squares regression
#' @export
###############################################################################
ols <- function
(
	x,
	y,
	computeSummary=F
)
{
	xx = t(x) %*% x

	if(is.null(ncol(xx))) { xinv = inv1(xx)
	} else if(ncol(xx) == 1) { xinv = inv1(xx)
	} else if(ncol(xx) == 2) { xinv = inv2(xx)
	} else if(ncol(xx) == 3) { xinv = inv3(xx)
	} else { xinv = inv(xx) }

	coefficients = xinv %*% t(x) %*% y
	if(computeSummary) {
		return( ols.summary(x, y, coefficients, xinv) )
	} else {
		return(list(coefficients = coefficients))
	}
}

#' @export
ols.summary <- function
(
	x,
	y,
	coefficients,
	xinv = NULL
)
{
	n = length(y)
	p = length(coefficients)
	rdf = n-p

	# error
   	e = y - x %*% coefficients
    ess=sum(e^2)
	mss = sum((y - sum(y)/n)^2)
	r.squared = 1 - ess/mss
	#r.squared = 1 - (var(e)/var(y))
	#adj.r.squared = 1 - (1 - r.squared)*(n - 1)/(n - p - 1)

	if( !is.null(xinv) ) {
		s2=ess/(rdf)
		seb=sqrt(diag(s2*xinv))
		tratio=coefficients/seb

		return(list(coefficients = coefficients, seb = seb,tratio = tratio, r.squared = r.squared))
	} else {
		return(list(coefficients = coefficients, r.squared = r.squared))
	}
}

ols.test <- function() {
	x = matrix( rnorm(4*10), ncol=4)
	y = rnorm(10)

	summary(lm(y ~ x+0))
	ols(x, y, T)
}

###############################################################################
# Compute matrix inverse
###############################################################################
#' @export
inv <- function(x) { solve(x) }

#' @export
inv1 <- function(x) { 1/x }

#http://www.mathwords.com/i/inverse_of_a_matrix.htm
#' @export
inv2 <- function(x)
{
	matrix(c(x[2,2],-x[1,2],-x[2,1],x[1,1]),nrow=2,byrow=T) / (x[1,1]*x[2,2] - x[1,2]*x[2,1])
}

#http://www.dr-lex.be/random/matrix_inv.html
#' @export
inv3 <- function(x)
{
	matrix(c(x[3,3]*x[2,2]-x[3,2]*x[2,3],-(x[3,3]*x[1,2]-x[3,2]*x[1,3]),x[2,3]*x[1,2]-x[2,2]*x[1,3],
	-(x[3,3]*x[2,1]-x[3,1]*x[2,3]),x[3,3]*x[1,1]-x[3,1]*x[1,3],-(x[2,3]*x[1,1]-x[2,1]*x[1,3]),
	x[3,2]*x[2,1]-x[3,1]*x[2,2],-(x[3,2]*x[1,1]-x[3,1]*x[1,2]),x[2,2]*x[1,1]-x[2,1]*x[1,2]),nrow=3,byrow=T) /
	(x[1,1]*(x[3,3]*x[2,2]-x[3,2]*x[2,3])-x[2,1]*(x[3,3]*x[1,2]-x[3,2]*x[1,3])+x[3,1]*(x[2,3]*x[1,2]-x[2,2]*x[1,3]))
}

inv.test <- function() {
	m=matrix(c(4,3,3,2),nrow=2,byrow=T)
	inv2(m) %*% m
	inv(m) %*% m

	m = matrix(c(1,2,3,4,5,6,7,8,8),ncol=3,byrow=T)
	inv3(m) %*% m
	m %*% inv3(m)
	inv(m) %*% m

}

###############################################################################
# Maximum Distance Point on the curve
# http://stackoverflow.com/questions/2018178/finding-the-best-trade-off-point-on-a-curve
#' @export
###############################################################################
find.maximum.distance.point <- function
(
	y,
	x=1:len(y)
)
{
	allCoord = rbind(vec(y), vec(x))

	firstPoint = allCoord[,1]
	lineVec = allCoord[,len(y)] - firstPoint
	lineVecN = lineVec / sqrt(sum(lineVec^2))

	vecFromFirst = allCoord - firstPoint
	scalarProduct = lineVecN %*% vecFromFirst

	vecFromFirstParallel = t(scalarProduct) %*% lineVecN
	vecToLine = t(vecFromFirst) - vecFromFirstParallel
	distToLine = sqrt(rowSums(vecToLine^2,2))
	which.max(distToLine)
}


subroutine qtest(h,i,j,k,shdswp,x,y,ntot,eps,nerror)

# Test whether the LOP is satisified; i.e. whether vertex j
# is outside the circumcircle of vertices h, i, and k of the
# quadrilateral.  Vertex h is the vertex being added; i and k are
# the vertices of the quadrilateral which are currently joined;
# j is the vertex which is ``opposite'' the vertex being added.
# If the LOP is not satisfied, then shdswp ("should-swap") is true,
# i.e. h and j should be joined, rather than i and k.  I.e. if j
# is outside the circumcircle of h, i, and k then all is well as-is;
# *don't* swap ik for hj.  If j is inside the circumcircle of h,
# i, and k then change is needed so swap ik for hj.
# Called by swap.

implicit double precision(a-h,o-z)
dimension x(-3:ntot), y(-3:ntot)
integer h
logical shdswp

nerror = -1

# Look for ideal points.
if(i<=0) ii = 1
else ii = 0
if(j<=0) jj = 1
else jj = 0
if(k<=0) kk = 1
else kk = 0
ijk = ii*4+jj*2+kk

# All three corners other than h (the point currently being
# added) are ideal --- so h, i, and k are co-linear; so
# i and k shouldn't be joined, and h should be joined to j.
# So swap.  (But this can't happen, anyway!!!)
# case 7:
if(ijk==7) {
	shdswp = .true.
	return
}

# If i and j are ideal, find out which of h and k is closer to the
# intersection point of the two diagonals, and choose the diagonal
# emanating from that vertex.  (I.e. if h is closer, swap.)
# Unless swapping yields a non-convex quadrilateral!!!
# case 6:
if(ijk==6) {
	xh = x(h)
	yh = y(h)
	xk = x(k)
	yk = y(k)
	ss = 1 - 2*mod(-j,2)
	test = (xh*yk+xk*yh-xh*yh-xk*yk)*ss
	if(test>0.d0) shdswp = .true.
	else shdswp = .false.
# Check for convexity:
	if(shdswp) call acchk(j,k,h,shdswp,x,y,ntot,eps)
	return
}

# Vertices i and k are ideal --- can't happen, but if it did, we'd
# increase the minimum angle ``from 0 to more than 2*0'' by swapping ...
#
# 24/7/2011 --- I now think that the forgoing comment is misleading,
# although it doesn't matter since it can't happen anyway.  The
# ``2*0'' is wrong.  The ``new minimum angle would be min{alpha,beta}
# where alpha and beta are the angles made by the line joining h
# to j with (any) line with slope = -1.  This will be greater than
# 0 unless the line from h to j has slope = - 1.  In this case h,
# i, j, and k are all co-linear, so i and k should not be joined
# (and h and j should be) so swapping is called for.  If h, i,
# j and j are not co-linear then the quadrilateral is definitely
# convex whence swapping is OK.  So let's say swap.
# case 5:
if(ijk==5) {
	shdswp = .true.
	return
}

# If i is ideal we'd increase the minimum angle ``from 0 to more than
# 2*0'' by swapping, so just check for convexity:
# case 4:
if(ijk==4) {
	call acchk(j,k,h,shdswp,x,y,ntot,eps)
	return
}

# If j and k are ideal, this is like unto case 6.
# case 3:
if(ijk==3) {
	xi = x(i)
	yi = y(i)
	xh = x(h)
	yh = y(h)
	ss = 1 - 2*mod(-j,2)
	test = (xh*yi+xi*yh-xh*yh-xi*yi)*ss
	if(test>0.d0) shdswp = .true.
	else shdswp = .false.
# Check for convexity:
	if(shdswp) call acchk(h,i,j,shdswp,x,y,ntot,eps)
	return
}

# If j is ideal we'd decrease the minimum angle ``from more than 2*0
# to 0'', by swapping; so don't swap.
# case 2:
if(ijk==2) {
	shdswp = .false.
	return
}

# If k is ideal, this is like unto case 4.
# case 1:
if(ijk==1) {
	call acchk(h,i,j,shdswp,x,y,ntot,eps) # This checks
                                              # for convexity.
                                              # (Was i,j,h,...)
	return
}

# If none of the `other' three corners are ideal, do the Lee-Schacter
# test for the LOP.
# case 0:
if(ijk==0) {
	call qtest1(h,i,j,k,x,y,ntot,eps,shdswp,nerror)
	return
}

# default:  # This CAN'T happen.
nerror = 7
return

end


################################################################################
# Part of the R/EpiILM package
#
# AUTHORS:
#         Waleed Almutiry <wkmtierie@qu.edu.sa>,
#         Vineetha Warriyar. K. V. <vineethawarriyar.kod@ucalgary.ca> and
#         Rob Deardon <robert.deardon@ucalgary.ca>
#
# Based on:
#                Deardon R, Brooks, S. P., Grenfell, B. T., Keeling, M. J., Tildesley,
#                M. J., Savill, N. J., Shaw, D. J.,  Woolhouse, M. E. (2010).Inference
#                for individual level models of infectious diseases in large
#                populations. Statistica Sinica, 20, 239-261.
#
#
# Free software under the terms of the GNU General Public License, version 2,
# a copy of which is available at http://www.r-project.org/Licenses/.
################################################################################

epimcmc <- function (object, tmin = NULL, tmax, niter,
                     sus.par.ini, trans.par.ini = NULL, beta.ini = NULL, spark.ini = NULL,
                     Sformula = NULL, Tformula = NULL,
                     pro.sus.var, pro.trans.var = NULL, pro.beta.var = NULL, pro.spark.var = NULL,
                     prior.sus.dist, prior.trans.dist = NULL, prior.beta.dist = NULL, prior.spark.dist = NULL,
                     prior.sus.par, prior.trans.par = NULL, prior.beta.par = NULL, prior.spark.par = NULL,
                     adapt = FALSE, acc.rate = NULL) {

  if (!is(object, "epidata")) {
    stop("The object must be in a class of \"epidata\"", call. = FALSE)
  } else {

    # error checks for input arguments

    if (any(is.null(object$type) | !(object$type %in% c("SI", "SIR"))) == TRUE) {
      stop("epimcmc: Specify type as \"SI\" or \"SIR\" ", call. = FALSE)
    }

    if (!is.vector(object$inftime)) {
      stop('epimcmc: inftime is not a vector')
    }

    n <- length(object$inftime)

    if (object$type == "SIR") {
      infperiod = object$remtime - object$inftime
    }

    if (all(is.null(object$contact) &  is.null(object$XYcoordinates)) == TRUE) {
      stop("epimcmc: Specify contact network or x, y coordinates")
    }

    if (is.null(tmin)) {
      tmin <- 1
    }

    # susceptibility parameters:

    ns <- length(sus.par.ini)

    # formula for susceptibility function
    if (!is.null(Sformula)) {
      covmat.sus <- model.matrix(Sformula)
      if (all((ncol(covmat.sus) == length(all.vars(Sformula))) & (ns != length(all.vars(Sformula)))) == TRUE) {
        stop("epimcmc: Check Sformula (no intercept term) and the dimension of susceptibility parameters", call. = FALSE)
      } else if (all((ncol(covmat.sus) > length(all.vars(Sformula))) & (ns != ncol(covmat.sus))) == TRUE) {
        stop("epimcmc: Check Sformula (intercept term) and the dimension of susceptibility parameters", call. = FALSE)
      }
    } else {
      if (ns == 1) {
        covmat.sus <- matrix(1.0, nrow = n, ncol = ns)
      }
    }

    if (any(is.null(prior.sus.dist) | !(all(prior.sus.dist %in% c("halfnormal", "gamma","uniform")))) == TRUE) {
      stop("epimcmc: Specify prior for susceptibility parameters as \"halfnormal\" ,  \"gamma\" or \"uniform\"  ", call. = FALSE)
    }

    prostda <- vector(mode = "numeric", length = ns)
    if (ns == 1) {
      prostda <- pro.sus.var
    } else if (ns > 1) {
      if (length(pro.sus.var) != ns) {
        stop('epimcmc: Specify proposal variance for each susceptibility parameter')
      }
      for (i in 1:ns) {
        prostda[i] <- pro.sus.var[i]
      }
    }

    alphapar <- list(NULL)
    len.alphapar <- ns - sum(prostda == 0)
    if (len.alphapar != 0){
      j <- 1
      for (i in 1:ns) {
        if (prostda[i] != 0){
          alphapar[[j]] <- list(NULL)
          alphapar[[j]][[1]] <- prior.sus.dist[i]
          j <- j + 1
        } else {
          j <- j
        }
      }
    } else {
      alphapar[[1]] <- list(NULL)
      alphapar[[1]][[1]] <- "uniform" # just to fill the list and it is not being used at all.
    }

    if (all(ns == 1 & len.alphapar != 0) == TRUE) {
      if (any((prior.sus.dist == "gamma") | (prior.sus.dist == "uniform")) == TRUE) {
        if (is.null(prior.sus.par)) {
          stop('epimcmc: Specify prior.sus.par as a vector or a 1 by 2 matrix.')
        } else {
          alphapar[[1]][[2]] <- prior.sus.par
        }
      } else if (prior.sus.dist == "halfnormal") {
        if (is.null(prior.sus.par)) {
          stop('epimcmc: Specify the variance of the half normal prior distribution for prior.sus.par.')
        } else {
          alphapar[[1]][[2]] <- c(prior.sus.par[1], 0)
        }
      }
    } else if (all(ns == 1 & len.alphapar == 0) == TRUE) {
      alphapar[[1]][[2]] <- c(0, 0)
    } else if (all(ns > 1 & len.alphapar != 0) == TRUE) {
      if (any(is.null(prior.sus.par) |  !is.matrix(prior.sus.par)) == TRUE) {
        stop('epimcmc: Specify prior.sus.par as a matrix with each row corresponds to each susceptibility parameter')
      } else {
        j <- 1
        for (i in 1:ns) {
          if (prostda[i] != 0){
            alphapar[[j]][[2]] <- prior.sus.par[i,]
            j <- j + 1
          } else {
            j <- j
          }
        }
      }
    } else if (all(ns > 1 & len.alphapar == 0) == TRUE) {
      alphapar[[1]][[2]] <- c(0, 0)
    }# end if - ns

    # transmissibility parameters:

    if (!is.null(trans.par.ini)) {
      nt <- length(trans.par.ini)
      flag.trans <- 1
    } else {
      nt <- 1
      flag.trans <- 0
      phipar <- 1
    }

    # formula for transmissibility function
    if (flag.trans == 1) {
      if (is.null(Tformula)) {
        stop("epimcmc: Tformula is missing. It has to be specified with no intercept term and number of columns equal to the length of trans.par", call. = FALSE)
      } else if (!is.null(Tformula)) {
        covmat.trans <- model.matrix(Tformula)
        if (all((ncol(covmat.trans) == length(all.vars(Tformula))) & (nt != length(all.vars(Tformula)))) == TRUE) {
          stop("epimcmc: Check Tformula. It has to be with no intercept term and number of columns equal to the length of trans.par", call. = FALSE)
        }
      }
    } else if (flag.trans == 0) {
      covmat.trans <- matrix(1.0, nrow = n, ncol = nt)
    }

    if (all(!is.null(prior.trans.dist) & !(all(prior.trans.dist %in% c("halfnormal", "gamma","uniform")))) == TRUE) {
      stop("epimcmc: Specify prior for transmissibility parameters as \"halfnormal\" ,  \"gamma\" or \"uniform\"  ", call. = FALSE)
    }

    if (all(is.null(prior.trans.dist) & flag.trans == 1) == TRUE) {
      stop("epimcmc: Specify prior for transmissibility parameters as \"halfnormal\" ,  \"gamma\" or \"uniform\"  ", call. = FALSE)
    }

    # proposal variance for transmissibility parameters
    prostdt <- vector(mode = "numeric", length = nt)
    if (all(flag.trans == 1 & nt == 1) == TRUE) {
      if (is.null(pro.trans.var)) {
        stop('epimcmc: Specify proposal variance for the transmissibility parameter')
      }
      prostdt <- pro.trans.var
    } else if (all(flag.trans == 1 & nt > 1) == TRUE) {
      if (is.null(pro.trans.var)) {
        stop('epimcmc: Specify proposal variance for each transmissibility parameter')
      }
      if (length(pro.trans.var) != nt) {
        stop('epimcmc: Specify proposal variance for each transmissibility parameter')
      }
      for (i in 1:nt) {
        prostdt[i] <- pro.trans.var[i]
      }
    } else {
      prostdt <- NULL
    }

    if (flag.trans == 1){
      phipar <- list(NULL)
      len.phipar <- nt - sum(prostdt == 0)
      if (len.phipar != 0){
        j <- 1
        for (i in 1:nt) {
          if (prostdt[i] != 0){
            phipar[[j]] <- list(NULL)
            phipar[[j]][[1]] <- prior.trans.dist[i]
            j <- j + 1
          } else {
            j <- j
          }
        }
      } else {
        phipar[[1]] <- list(NULL)
        phipar[[1]][[1]] <- "uniform" # just to fill the list and it is not being used at all.
      }
    } else {
      phipar <- 1
    }

    # prior selection for transmissibility parameters
    if (flag.trans == 1) {

      if (nt == 1 & len.phipar != 0) {
        if (any((prior.trans.dist == "gamma") | (prior.trans.dist == "uniform")) == TRUE) {
          if (is.null(prior.trans.par)) {
            stop('epimcmc: Specify prior.trans.par as a vector or a 1 by 2 matrix.')
          } else {
            phipar[[1]][[2]] <- prior.trans.par
          }
        } else if (prior.trans.dist == "halfnormal") {
          if (is.null(prior.trans.par)) {
            stop('epimcmc: Specify the variance of the half normal prior distribution for prior.trans.par.')
          } else {
            phipar[[1]][[2]] <- c(prior.trans.par[1], 0)
          }
        }
      } else if (all(nt == 1 & len.phipar == 0) == TRUE) {
        phipar[[1]][[2]] <- c(0, 0)
      } else if (all(nt > 1 & len.phipar != 0) == TRUE) {
        if (any(is.null(prior.trans.par) |  !is.matrix(prior.trans.par)) == TRUE) {
          stop('epimcmc: Specify prior.trans.par as a matrix with each row corresponds to each transmissibility parameter')
        } else {
          j <- 1
          for (i in 1:nt) {
            if (prostdt[i] != 0){
              phipar[[j]][[2]] <- prior.trans.par[i,]
              j <- j + 1
            } else {
              j <- j
            }
          }
        }
      } else if (all(nt > 1 & len.phipar == 0) == TRUE) {
        phipar[[1]][[2]] <- c(0, 0)
      }# end if - nt
    }# end if - flag.trans

    # BETA:

    if (!is.null(object$contact)) {
      if (length(object$contact)/(n * n) == 1) {
        ni <- 1

        if (any(!is.null(beta.ini) | !is.null(prior.beta.dist) | !is.null(pro.beta.var) | !is.null(prior.beta.par)) == TRUE) {
          stop("As the model has only one contact network, The model does not have a network parameter beta, beta.ini, prior.dist.beta, prior.beta.var, and pro.beta.var must be assigned to their default values = NULL", .call = FALSE)
        }

        flag.beta <- 0

        betapar <- list(NULL)
        betapar[[1]] <- "uniform" # just to fill the list and it is not being used at all.
        betapar[[2]] <- c(0, 0)

      } else if (length(object$contact)/(n * n) > 1) {

        if (is.null(beta.ini)) {
          stop("epimcmc: The initial values beta.ini of the network parameters has to specified", call. = FALSE)
        }
        ni <- length(beta.ini)

        if (length(object$contact)/(n * n) != ni) {
          stop('epimcmc:  Dimension of beta  and the number of contact networks are not matching')
        }

        flag.beta <- 1

        if (any(is.null(prior.beta.dist) | !(all(prior.beta.dist %in% c("halfnormal", "gamma","uniform")))) == TRUE) {
          stop("epimcmc: Specify prior for beta as \"halfnormal\" ,  \"gamma\" or \"uniform\"  ", call. = FALSE)
        }

        if (length(prior.beta.dist) != ni) {
          stop('epimcmc: Specify prior distributions for each of the network parameters beta, prior.beta.dist')
        }

        # proposal variance for  beta
        prostdb <- vector(mode = "numeric", length = ni)
        if (length(pro.beta.var) != ni) {
          stop('epimcmc: Specify proposal variance for each beta parameter')
        }
        for (i in 1:ni) {
          prostdb[i] <- pro.beta.var[i]
        }

        betapar <- list(NULL)
        if (flag.beta == 0) {
          len.betapar <- 0
        } else {
          len.betapar <- ni - sum(prostdb == 0)
        }

        if (len.betapar != 0){
          j <- 1
          for (i in 1:ni) {
            if (prostdb[i] != 0){
              betapar[[j]] <- list(NULL)
              betapar[[j]][[1]] <- prior.beta.dist[i]
              j <- j + 1
            } else {
              j <- j
            }
          }
        } else {
          betapar[[1]] <- list(NULL)
          betapar[[1]][[1]] <- "uniform" # just to fill the list and it is not being used at all.
        }
        # prior selection for kernel parameters
        if (len.betapar != 0) {
          if (any(is.null(prior.beta.par) |  !is.matrix(prior.beta.par)) == TRUE) {
            stop('epimcmc: Specify prior.beta.par as a matrix with each row corresponds to each beta parameter')
          } else {
            j <- 1
            for (i in 1:ni) {
              if (prostdb[i] != 0){
                betapar[[j]][[2]] <- prior.beta.par[i,]
                j <- j + 1
              } else {
                j <- j
              }
            }
          }
        } else if (len.betapar == 0) {
          betapar[[1]][[2]] <- c(0, 0)
        }# end if - ni
      }

    } else if (is.null(object$contact)) {

      if (is.null(object$XYcoordinates)) {
        stop("epimcmc: Specify contact network or x, y coordinates")
      }

      ni <- length(beta.ini)

      if (ni > 1) {
        stop("epimcmc: The input of beta.ini has more than one value while the considered distance-based ILM needs only one spatial parameter, beta.")
      }

      if (any(is.null(prior.beta.dist) | !(all(prior.beta.dist %in% c("halfnormal", "gamma","uniform")))) == TRUE) {
        stop("epimcmc: Specify prior for beta as \"halfnormal\" ,  \"gamma\" or \"uniform\"  ", call. = FALSE)
      }

      if (length(pro.beta.var) != ni) {
        stop('epimcmc: Specify only one proposal variance for the spatial parameter beta, pro.beta.var')
      }

      if (length(prior.beta.dist) != ni) {
        stop('epimcmc: Specify only one prior distribution for the spatial parameter beta, prior.beta.dist')
      }

      flag.beta <- 1

      prostdb <- pro.beta.var

      betapar <- list(NULL)
      len.betapar <- ni - sum(prostdb == 0)
      betapar[[1]] <- list(NULL)
      if (len.betapar != 0){
        betapar[[1]][[1]] <- prior.beta.dist
      } else {
        betapar[[1]][[1]] <- "uniform" # just to fill the list and it is not being used at all.
      }
      # prior selection for kernel parameters
      if (len.betapar != 0) {
        if (any((prior.beta.dist == "gamma") | (prior.beta.dist == "uniform")) == TRUE) {
          if (is.null(prior.beta.par)) {
            stop('epimcmc: Specify prior.beta.par as a vector or a 1 by 2 matrix.')
          } else {
            betapar[[1]][[2]] <- prior.beta.par
          }
        } else if (prior.beta.dist == "halfnormal") {
          if (is.null(prior.beta.par)) {
            stop('epimcmc: Specify the variance of the half normal prior distribution for prior.beta.par.')
          } else {
            betapar[[1]][[2]] <- c(prior.beta.par[1], 0)
          }
        }

      } else if (len.betapar == 0) {
        betapar[[1]][[2]] <- c(0, 0)
      }
    }

    # SPARK parameter:

    if (!is.null(spark.ini)) {
      flag <- 1
      if (is.null(pro.spark.var)) {
        stop("epimcmc: Specify proposal variance for spark", call. = FALSE)
      }
      if (is.null(prior.spark.dist)) {
        stop("epimcmc22: Specify prior for spark", call. = FALSE)
      }
      if (any(is.null(prior.spark.dist) | !(prior.spark.dist %in% c("halfnormal", "gamma","uniform"))) == TRUE) {
        stop("epimcmc2: Specify prior for spark term as \"halfnormal\" ,  \"gamma\" or \"uniform\"  ", call. = FALSE)
      }
      sparkpar <- list(NULL)
      length(sparkpar) <- 2
      sparkpar[[1]] <- prior.spark.dist
      if (any((prior.spark.dist == "gamma") | (prior.spark.dist == "uniform")) == TRUE) {
        if (is.null(prior.spark.par)) {
          stop('epimcmc: Specify prior.spark.par as a vector or a 1 by 2 matrix.')
        } else {
          sparkpar[[2]] <- c(prior.spark.par)
        }
      } else if (prior.spark.dist == "halfnormal") {
        if (is.null(prior.spark.par)) {
          stop('epimcmc: Specify the variance of the half normal prior distribution for prior.spark.par.')
        } else {
          sparkpar[[2]] <- c(prior.spark.par[1], 0)
        }
      }

    } else if (is.null(spark.ini)) {
      flag <-  0
      sparkpar <- list(NULL)
      sparkpar[[2]] <- c(0, 0)
    }

    # proposal variance for  spark

    if (flag == 1) {
      prostdsp <- pro.spark.var
    } else {
      prostdsp <- 0.0
    }

    if (all(flag == 0 & flag.trans == 0 & flag.beta == 0) == TRUE) {
      mcmcscale <- c(prostda)
    } else if (all(flag == 0 & flag.trans == 0 & flag.beta == 1) == TRUE) {
      mcmcscale <- c(prostda, prostdb)
    } else if (all(flag == 0 & flag.trans == 1 & flag.beta == 0) == TRUE) {
      mcmcscale <- c(prostda, prostdt)
    } else if (all(flag == 0 & flag.trans == 1 & flag.beta == 1) == TRUE) {
      mcmcscale <- c(prostda, prostdt, prostdb)
    } else if (all(flag == 1 & flag.trans == 0 & flag.beta == 0) == TRUE) {
      mcmcscale <- c(prostda, prostdsp)
    } else if (all(flag == 1 & flag.trans == 0 & flag.beta == 1) == TRUE) {
      mcmcscale <- c(prostda, prostdb, prostdsp)
    } else if (all(flag == 1 & flag.trans == 1 & flag.beta == 0) == TRUE) {
      mcmcscale <- c(prostda, prostdt, prostdsp)
    } else if (all(flag == 1 & flag.trans == 1 & flag.beta == 1) == TRUE) {
      mcmcscale <- c(prostda, prostdt, prostdb, prostdsp)
    }

    scalemc <- mcmcscale[mcmcscale !=0]

    if (all(flag == 0 & flag.trans == 0 & flag.beta == 0) == TRUE) {
      mcmcinit <- c(sus.par.ini)
    } else if (all(flag == 0 & flag.trans == 0 & flag.beta == 1) == TRUE) {
      mcmcinit <- c(sus.par.ini, beta.ini)
    } else if (all(flag == 0 & flag.trans == 1 & flag.beta == 0) == TRUE) {
      mcmcinit <- c(sus.par.ini, trans.par.ini)
    } else if (all(flag == 0 & flag.trans == 1 & flag.beta == 1) == TRUE) {
      mcmcinit <- c(sus.par.ini, trans.par.ini, beta.ini)
    } else if (all(flag == 1 & flag.trans == 0 & flag.beta == 0) == TRUE) {
      mcmcinit <- c(sus.par.ini, spark.ini)
    } else if (all(flag == 1 & flag.trans == 0 & flag.beta == 1) == TRUE) {
      mcmcinit <- c(sus.par.ini, beta.ini, spark.ini)
    } else if (all(flag == 1 & flag.trans == 1 & flag.beta == 0) == TRUE) {
      mcmcinit <- c(sus.par.ini, trans.par.ini, spark.ini)
    } else if (all(flag == 1 & flag.trans == 1 & flag.beta == 1) == TRUE) {
      mcmcinit <- c(sus.par.ini, trans.par.ini, beta.ini, spark.ini)
    }

    initmc <- mcmcinit[mcmcscale!=0]

    p <- function(xx) {

      ms <- which(mcmcscale[1: ns] == 0)
      nns <- ns - length(ms)
      if (length(ms) != 0) {
        xs <- vector("double", length = ns)
        xs[ms] <- mcmcinit[ms]
        xs[-ms] <- xx[1: nns]
      } else {
        xs <- xx[1: nns]
      }

      if (flag.trans == 1) {
        mt <- which(mcmcscale[(1+ns): (ns+nt)] == 0)
        nnt <- nt - length(mt)
        if (length(mt) != 0) {
          xt <- vector("double", length = nt)
          xt[mt] <- mcmcinit[mt+ns]
          xt[-mt] <- xx[(1+nns): (nns+nnt)]
        } else {
          xt <- xx[(1+nns): (nns+nnt)]
        }
      } else {
        nnt <- 0
        xt <- NULL
      }

      if (flag.beta == 1) {
        if (flag.trans == 1) {
          mi <- which(mcmcscale[(1+ns+nt): (ns+nt+ni)] == 0)
        } else {
          mi <- which(mcmcscale[(1+ns): (ns+ni)] == 0)
        }
        nni <- ni - length(mi)

        if (flag.trans == 1) {
          if (length(mi) != 0) {
            xi <- vector("double", length = ni)
            xi[mi] <- mcmcinit[mi+ns+nt]
            xi[-mi] <- xx[(1+nns+nnt): (nns+nnt+nni)]
          } else {
            xi <- xx[(1+nns+nnt): (nns+nnt+nni)]
          }
        } else {
          if (length(mi) != 0) {
            xi <- vector("double", length = ni)
            xi[mi] <- mcmcinit[mi+ns+nnt]
            xi[-mi] <- xx[(1+nns+nnt): (nns+nnt+nni)]
          } else {
            xi <- xx[(1+nns+nnt): (nns+nnt+nni)]
          }
        }
      } else {
        nni <- 0
        xi <- NULL
      }

      if (flag == 1) {
        if(length(xx) > nns+nnt+nni) {
          xsp <- xx[nns+nnt+nni+1]
        } else if (all(length(xx) == nns+nnt+nni & mcmcscale[ns+nt+ni+1] == 0) == TRUE) {
          xsp <- mcmcinit[ns+nt+ni+1]
        }
      } else {
        xsp <- NULL
      }

      loglikeILM <- epilike(object = object, tmin = tmin, tmax = tmax, sus.par = xs,
                            trans.par = xt, beta = xi, spark = xsp,
                            Sformula = Sformula, Tformula = Tformula)

      if (nns != 0) {
        palpha = 0
        for (i in 1:nns) {
          if (alphapar[[i]][[1]] == "uniform") {
            palpha <- palpha + dunif(xx[i], min = alphapar[[i]][[2]][1],
                                     max = alphapar[[i]][[2]][2], log = TRUE)
          } else if (alphapar[[i]][[1]] == "gamma") {
            palpha <- palpha + dgamma(xx[i], shape = alphapar[[i]][[2]][1],
                                      rate = alphapar[[i]][[2]][2], log = TRUE)
          } else if (alphapar[[i]][[1]] == "halfnormal") {
            palpha <- palpha + dhalfnorm(xx[i], scale = alphapar[[i]][[2]][1], log = TRUE)
          }
        }
      } else {
        palpha = 0
      }

      if (all(flag.trans == 1 & nnt != 0) == TRUE) {
        pphi = 0
        for (i in 1:nnt) {
          if (phipar[[i]][[1]] == "uniform") {
            pphi <- pphi + dunif(xx[i+nns], min = phipar[[i]][[2]][1],
                                 max = phipar[[i]][[2]][2], log = TRUE)
          } else if (phipar[[i]][[1]] == "gamma") {
            pphi <- pphi + dgamma(xx[i+nns], shape = phipar[[i]][[2]][1],
                                  rate = phipar[[i]][[2]][2], log = TRUE)
          } else if (phipar[[i]][[1]] == "halfnormal") {
            pphi <- pphi + dhalfnorm(xx[i+nns], scale = phipar[[i]][[2]][1], log = TRUE)
          }
        }
      } else {
        pphi = 0
      }

      if (nni != 0) {
        pbeta = 0
        for (i in 1:nni) {
          if (betapar[[i]][[1]] == "uniform") {
            pbeta <- pbeta + dunif(xx[i+nns+nnt], min = betapar[[i]][[2]][1],
                                   max = betapar[[i]][[2]][2], log = TRUE)
          } else if (betapar[[i]][[1]] == "gamma") {
            pbeta <- pbeta + dgamma(xx[i+nns+nnt], shape = betapar[[i]][[2]][1],
                                    rate = betapar[[i]][[2]][2], log = TRUE)
          } else if (betapar[[i]][[1]] == "halfnormal") {
            pbeta <- pbeta + dhalfnorm(xx[i+nns+nnt], scale = betapar[[i]][[2]][1], log = TRUE)
          }
        }
      } else {
        pbeta = 0
      }

      if (flag == 1) {
        if (sparkpar[[1]] == "uniform") {
          pspark <- dunif(xx[nns+nnt+nni+1], min = sparkpar[[2]][1],
                          max = sparkpar[[2]][2], log = TRUE)
        } else if (sparkpar[[1]] == "gamma") {
          pspark <- dgamma(xx[nns+nnt+nni+1], shape = sparkpar[[2]][1],
                           rate = sparkpar[[2]][2], log = TRUE)
        } else if (sparkpar[[1]] == "halfnormal") {
          pspark <- pspark + dhalfnorm(xx[nns+nnt+nni+1], scale = sparkpar[[2]][1], log = TRUE)
        }
      } else {
        pspark = 0.0
      }

      return(loglikeILM + palpha + pphi + pbeta + pspark)
    }

    if (is.null(object$contact)) {
      kernel.type <- "distance-based discrete-time ILM"
      result <- MCMC(p = p, n = niter, init = initmc, scale = scalemc,
                     adapt = adapt, acc.rate = acc.rate)
    } else if (!is.null(object$contact)) {
      kernel.type <- "network-based discrete-time ILM"
      result <- MCMC(p = p, n = niter, init = initmc, scale = scalemc,
                     adapt = adapt, acc.rate = acc.rate)
    }

    alphanames <- c()
    for (i in 1:ns) {
      alphanames <- c(alphanames, paste("alpha.", i, sep = ""))
    }

    if (flag.trans == 1) {
      phinames <- c()
      for (i in 1:nt) {
        phinames <- c(phinames, paste("phi.", i, sep = ""))
      }
    }

    if (flag.beta == 1) {
      betanames <- c()
      for (i in 1:ni) {
        betanames <- c(betanames, paste("beta.", i, sep = ""))
      }
    }

    if (all(flag == 0 & flag.trans == 0 & flag.beta == 0) == TRUE) {
      nnames <- c(alphanames)
    } else if (all(flag == 0 & flag.trans == 0 & flag.beta == 1) == TRUE) {
      nnames <- c(alphanames, betanames)
    } else if (all(flag == 1 & flag.trans == 0 & flag.beta == 0) == TRUE) {
      nnames <- c(alphanames, "spark")
    } else if (all(flag == 1 & flag.trans == 0 & flag.beta == 1) == TRUE) {
      nnames <- c(alphanames, betanames, "spark")
    } else if (all(flag == 0 & flag.trans == 1 & flag.beta == 0) == TRUE) {
      nnames <- c(alphanames, phinames)
    } else if (all(flag == 0 & flag.trans == 1 & flag.beta == 1) == TRUE) {
      nnames <- c(alphanames, phinames, betanames)
    } else if (all(flag == 1 & flag.trans == 1 & flag.beta == 0) == TRUE) {
      nnames <- c(alphanames, phinames, "spark")
    } else if (all(flag == 1 & flag.trans == 1 & flag.beta == 1) == TRUE) {
      nnames <- c(alphanames, phinames, betanames, "spark")
    }

    finalnames <- nnames[mcmcscale!=0]

    Estimates <- data.frame(result$samples)
    colnames(Estimates) <- finalnames

    Loglikelihood <- c(result$log.p)
    #      colnames(Loglikelihood) <- "Loglikelihood"

    ki <- match(nnames,finalnames)

    nki <- which(nnames %in% finalnames)

    fullsamples <- matrix(0, ncol = length(nnames), nrow = niter)
    fullsamples[,nki] <- result$samples
    nnki <- which(is.na(ki))

    if (length(nnki) == 1) {
      fullsamples[,nnki] <- rep(mcmcinit[nnki], niter)
    } else if (length(nnki) > 1){
      for (i in 1:length(nnki)) {
        fullsamples[,nnki[i]] <- rep(mcmcinit[nnki[i]], niter)
      }
    }
  }

  # mcmc result as a coda object

  if (flag.trans == 1) {
    n.trans.par <- nt
  } else {
    n.trans.par <- 0
  }
  if (flag.beta == 1) {
    n.ker.par <- ni
  } else {
    n.ker.par <- 0
  }

  output <- list(type = object$type, kernel.type = kernel.type, Estimates = Estimates,
                 Loglikelihood = Loglikelihood, Fullsample = fullsamples,
                 n.sus.par = ns, n.trans.par = n.trans.par, n.ker.par = n.ker.par)

  class(output) <- "epimcmc"
  output

} # End of function

summary.epimcmc <- function(object, ...) {
  if (!is(object, "epimcmc")) {
    stop("The object has to be of \"epimcmc\" class", call. = FALSE)
  }
  if (object$type == "SI") {
    cat(paste("Model:", object$type, object$kernel.type,"\n"))
    cat(paste("Method: Markov chain Monte Carlo (MCMC) \n"))
    summary(window(mcmc(object$Estimates), ...))
  } else if (object$type == "SIR") {
    cat(paste("Model:", object$type, object$kernel.type,"\n"))
    cat(paste("Method: Markov chain Monte Carlo (MCMC) \n"))
    summary(window(mcmc(object$Estimates), ...))
  }
}

plot.epimcmc <- function(x, partype, start = 1, end = NULL, thin = 1, ...) {
  if (is.null(end)) {
    end = nrow(x$Estimates)
  }
  if (!is(x, "epimcmc")) {
    stop("The object x has to be of \"epimcmc\" class", call. = FALSE)
  }
  if (partype == "parameter") {
    plot(window(as.mcmc(x$Estimates), start = start, end = end, thin = thin), ...)
  } else if (partype == "loglik") {
    plot(window(as.mcmc(x$Loglikelihood), start = start, end = end, thin = thin), ...)
  }
}


#dod方法下的射出频率与参数之间的关系
#########说明########
#乙二醇液体
#voltage_pluse，说明高峰电压和基准电压下的射出时间ms
#duty_pluse，随着fv电压频率上升，占空比不同时的射出时间
#height，不同占空比下，随fv上升，弯月面高度的变化
#angle，同上，弯月面锥角的变化
#flowrate_pulse，不同占空比下，随流量变化的射出时间
#flowrate_height，此时的弯月面高度
#flowrate_angle，弯月面角度的变化
#setduty_pulse, 固定占空比为50%时，不同电压频率下，不同流量下的射出时间
#setduty_height，同上，弯月面高度
#setduty_angle，同上，弯月面锥角

#####第一组：随电压频率上升，不同占空比下的射出时间和弯月面信息#####
#####第二组：固定电压频率下，不同流量，不同占空比下的射出时间和弯月面信息####
#####第三组：固定占空比下，不同流量，不同电压频率的射出时间和弯月面信息####

#设定工作区间
setwd("D:/data")
library(xlsx)

setp<-read.xlsx("dod_v_pulse.xls", sheetName = "setduty_pulse", header = TRUE)
seth<-read.xlsx("dod_v_pulse.xls", sheetName = "setduty_height", header = TRUE)
seta<-read.xlsx("dod_v_pulse.xls", sheetName = "setduty_angle", header = TRUE)
#规划区域
par(mfrow=c(2,2), mar=c(4,4,2,2), oma=c(2,1,1,1))

#保存pdf
#pdf("dod_frequency.pdf")

#固定占空比为20%

################################画图-流量与pulse射出时间的关系###########################
plot(setp$flowrate, setp$X10, col=0, xaxs="i", xlim=c(0, 0.001), ylim=c(0.2, 0.5),
xlab="Flow rate (ml/min)", cex.lab=1.2, ylab="Pulse time (ms)", main="Pulse time in various fv", cex.main = 1.5)

model1 = loess(setp$X10~setp$flowrate)
lines(setp$flowrate,  model1$fit, col="#458B74", pch=21, lwd=2.5, type="b", lty=2)

model2 = loess(setp$X25~setp$flowrate)
lines(setp$flowrate,  model2$fit, col="#FF00FF", pch=22, lwd=2.5, type="b", lty=2)

model3 = loess(setp$X50~setp$flowrate)
lines(setp$flowrate,  model3$fit, col="#EE0000", pch=23, lwd=2.5, type="b", lty=2)

model4 = loess(setp$X75~setp$flowrate)
lines(setp$flowrate, model4$fit, col="#EEAD0E", pch=24, lwd=2.5, type="b", lty=2)

legend("bottomright", c("10Hz", "25Hz", "50Hz", "75Hz"), inset=.05, col=c("#458B74", "#FF00FF", "#EE0000", "#EEAD0E"), pch=c(21,22,23, 24), lwd=1.5, lty=2, cex=1.2, bty="n")

################################画图-信息###########################

plot(setp$flowrate, setp$X10,  yaxt="n", xaxt= "n", col=0,  xlab="", ylab="", xlim=c(0,0.001), ylim=c(1, 3), main="Information", cex.main = 1.5)
text(0.00025, 2.8, "Liquids - Ethylene glycol", col="black", adj=0, cex=1.5)
text(0.00025, 2.5, "Distance - 1mm", col="black", adj=0, cex=1.5)
text(0.00025, 2.2, "Nozzle - 24G - 0.55mm", col="black", adj=0, cex=1.5)
text(0.00025, 1.9, "Flowrate -  0.0002ml/min", col="black", adj=0, cex=1.5)
text(0.00025, 1.6, "Voltage frequency - 20Hz", col="black", adj=0, cex=1.5)
text(0.00025, 1.3, "Duty cycle - 20%", col="black", adj=0, cex=1.5)

#legend("bottomleft", c("k=0.1", "k=0.2", "k=0.3", "k=0.5", "k=0.8"), inset=.05, col=c("#458B74", "#FF00FF", "#EE0000", "#EEAD0E", "#27408B"), pch=c(21,22,23,24,25), lwd=1.5, lty=2, cex=1.2, bty="n")

#################################画图--弯月面高度#############################
plot(seth$flowrate, seth$X10, col=0, xaxs="i", xlim=c(0, 0.001), ylim=c(0.3, 0.4),
xlab="Flow rate (ml/min)", cex.lab=1.2, ylab="Height of Meniscus (mm)", main="Meniscus height in various fv", cex.main = 1.5)

lines(lowess(seth$flowrate, seth$X10), col="#458B74", pch=21, lwd=2.5, type="b", lty=2)

lines(lowess(seth$flowrate, seth$X25), col="#FF00FF", pch=22, lwd=2.5, type="b", lty=2)

lines(lowess(seth$flowrate, seth$X50), col="#EE0000", pch=23, lwd=2.5, type="b", lty=2)

lines(lowess(seth$flowrate, seth$X75), col="#EEAD0E", pch=24, lwd=2.5, type="b", lty=2)

legend("bottomright", c("10Hz", "25Hz", "50Hz", "75Hz"), inset=.05, col=c("#458B74", "#FF00FF", "#EE0000", "#EEAD0E"), pch=c(21,22,23,24), lwd=1.5, lty=2, cex=1.2, bty="n")

###############################画图--弯月面角度#############################
plot(seta$flowrate, seta$X10, col=0, xaxs="i",xlim=c(0, 0.001), ylim=c(65, 90),
xlab="Flow rate (ml/min)", cex.lab=1.2, ylab="Taylor fale (°)", main="Meniscus Angle in various fv", cex.main = 1.5)

lines(lowess(seta$flowrate, seta$X10), col="#458B74", pch=21, lwd=2.5, type="b", lty=2)

lines(lowess(seta$flowrate, seta$X25), col="#FF00FF", pch=22, lwd=2.5, type="b", lty=2)

lines(lowess(seta$flowrate, seta$X50), col="#EE0000", pch=23, lwd=2.5, type="b", lty=2)

lines(lowess(seta$flowrate, seta$X75), col="#EEAD0E", pch=24, lwd=2.5, type="b", lty=2)

legend("topright", c("10Hz", "25Hz", "50Hz", "75Hz"), inset=.05, col=c("#458B74", "#FF00FF", "#EE0000", "#EEAD0E"), pch=c(21,22,23,24), lwd=1.5, lty=2, cex=1.2, bty="n")

#mtext("乙二醇 | H = 1mm | Nozzle 24G | Q = 0.0002ml/min | Fv = 20Hz | Duty = 20%", 1, line=1, col="black", cex=1.3, font=2, outer=TRUE)

#dev.off()


model_soilevaporation <- function (diffusionLimitedEvaporation = 6605.505,
         energyLimitedEvaporation = 448.24){
    #'- Name: SoilEvaporation -Version: 1.0, -Time step: 1
    #'- Description:
    #'            * Title: SoilEvaporation Model
    #'            * Author: Pierre Martre
    #'            * Reference: Modelling energy balance in the wheat crop model SiriusQuality2:
    #'            Evapotranspiration and canopy and soil temperature calculations
    #'            * Institution: INRA Montpellier
    #'            * ExtendedDescription: Starting from a soil at field capacity, soil evaporation  is assumed to
    #'                be energy limited during the first phase of evaporation and diffusion limited thereafter.
    #'                Hence, the soil evaporation model considers these two processes taking the minimum between
    #'                the energy limited evaporation (PtSoil) and the diffused limited
    #'                evaporation
    #'            * ShortDescription: Starting from a soil at field capacity, soil evaporation  is assumed to
    #'            be energy limited during the first phase of evaporation and diffusion limited thereafter.
    #'            Hence, the soil evaporation model considers these two processes taking the minimum between
    #'            the energy limited evaporation (PtSoil) and the diffused limited
    #'            evaporation
    #'- inputs:
    #'            * name: diffusionLimitedEvaporation
    #'                          ** description : diffusion Limited Evaporation
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** default : 6605.505
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'            * name: energyLimitedEvaporation
    #'                          ** description : energy Limited Evaporation
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** default : 448.240
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    #'                          ** inputtype : variable
    #'- outputs:
    #'            * name: soilEvaporation
    #'                          ** description : soil Evaporation
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 5000
    #'                          ** unit : g m-2 d-1
    #'                          ** uri : http://www1.clermont.inra.fr/siriusquality/?page_id=547
    soilEvaporation <- min(diffusionLimitedEvaporation, energyLimitedEvaporation)
    return (list('soilEvaporation' = soilEvaporation))
}


"
Fit Nicolas's model to the Colombia data.
"
library(RJSONIO)
source("contagious_infected/model.r")
source("visualization.r")

params = list(
  "alpha" = 2 ^ 40,
  "beta_min" = 0,
  "beta_max" = 2,
  "gamma_min" = 1/3,
  "gamma_max" = 1/3,
  "lambda_min" = 1/40,
  "lambda_max" = 1/4,
  "N0_min" = 0,
  "N0_max" = 2000,
  "C0_min" = 0,
  "C0_max" = 2000,
  "t0" = 1
)
bootstrap_params = list(
  "number_samples" = 500,
  "window_size" = 10,
  "confidence" = 0.95
)
# load data and initialize parameters
load("CasosAjustados_Diagnostico.Rda")
observed_I = data$`Estimados Totales`
observed_I = c(observed_I[1], 0, tail(observed_I, -1))
observed_I = observed_I[params$t0:length(observed_I)]
for (i in 1:100) {
  # params$beta0 = read.table("BetaInitials.txt", quote="\"", comment.char="")$V1
  params$beta0 = runif(length(observed_I), params$beta_min, params$beta_max)
  params$gamma0 = runif(1, params$gamma_min, params$gamma_max)
  params$lambda0 = runif(1, params$lambda_min, params$lambda_max)
  params$N00 = runif(1, params$N0_min, params$N0_max)
  params$C00 = runif(1, params$C0_min, params$C0_max)
  params$observed_I = observed_I
  # fit Nicolas's model
  model = fit(observed_I=observed_I, beta0=params$beta0, beta_min=params$beta_min,
              beta_max=params$beta_max, gamma0=params$gamma0,
              gamma_min=params$gamma_min, gamma_max=params$gamma_max,
              lambda0=params$lambda0, lambda_min=params$lambda_min,
              lambda_max=params$lambda_max, N00=params$N00, N0_min=params$N0_min,
              N0_max=params$N0_max, C00=params$C00, C0_min=params$C0_min,
              C0_max=params$C0_max, alpha=params$alpha, ignore_beta_diff=27)
  # save fitted model and parameters
  timestamp = format(Sys.time(), "%Y%m%d%H%M")
  out_dir = paste("contagious_infected", "tuned", timestamp, sep="/")
  dir.create(out_dir, recursive=TRUE)
  write(toJSON(params), paste(out_dir, "params.json", sep="/"))
  write(toJSON(model), paste(out_dir, "model.json", sep="/"))
  # plot reproduction number series
  R = model$beta / model$lambda
  png(paste(out_dir, "R(t).png", sep="/"))
  plot(R, xlab="t", ylab="R(t)", type="l")
  dev.off()
  # plot expected new cases with confidence intervals
  expected_I = get_expected_I(model$N0, model$C0, model$beta, model$gamma, model$lambda)
  png(paste(out_dir, "I(t).png", sep="/"))
  plot(1:length(expected_I), expected_I, type="l")
  lines(1:length(observed_I), observed_I, col="red")
  dev.off()
}