#' km
#'
#' k-means via Lloyd's Algorithm.
#'
#' Note that the function does not respect \code{set.seed()} or
#' \code{comm.set.seed()}.  For managing random seeds, use the \code{seed}
#' parameter.
#'
#' @details
#' The iterations stop either when the maximum number of iterations have been
#' achieved, or when the centers in the current iteration are basically the same
#' (within \code{1e-8} for double precision and \code{1e-4} single precision)
#' as the centers from the previous iteration.
#'
#' For best performance, the data should be as balanced as possible across all
#' MPI ranks.
#'
#' @section Communication:
#' Most of the computation is local. However, at each iteration there is a
#' length \code{n*k} and a length \code{k} allreduce call to update the centers.
#' There is also a check at the beginning of the call to find out how many
#' observations come before the current process's data, which is an allgather
#' operation.
#'
#' @param x
#' A shaq.
#' @param k
#' The 'k' in k-means.
#' @param maxiter
#' The maximum number of iterations possible.
#' @param seed
#' A seed for determining the (random) initial centroids.  Each process has to
#' use the same seed or very strange things may happen.  If you do not provide
#' a seed, a good initial seed will be chosen.
#'
#' @return
#' A list containing the cluster centers (global), the observation labels i.e.
#' the assignments to clusters (distributed shaq), and the total number of
#' iterations (global).
#'
#' @references
#' Phillips, J.. Data Mining: Algorithms, Geometry, and Probability.
#' https://www.cs.utah.edu/~jeffp/DMBook/DM-AGP.html
#'
#' @useDynLib clustrgame R_kmeans
#' @export
km_game = function(x, k=2, maxiter=100, seed=1234)
{
  kazaam:::check.is.shaq(x)
  kazaam:::check.is.posint(k)
  kazaam:::check.is.posint(maxiter)
  kazaam:::check.is.posint(seed)

  k = as.integer(k)
  maxiter = as.integer(maxiter)
  comm_ptr = pbdMPI::get.mpi.comm.ptr(.pbd_env$SPMD.CT$comm)

  pbdMPI::comm.set.seed(seed, diff=FALSE)

  if (is.float(DATA(x)))
    data = DATA(x)@Data
  else
  {
    data = DATA(x)
    if (!is.double(data))
      storage.mode(data) = "double"
  }

  m = nrow(x)
  m = as.integer(m) # FIXME

  out = .Call(R_kmeans, data, m, k, maxiter, comm_ptr)

  if (is.float(DATA(x)))
    out$centers = float32(out$centers)

  labels.shaq = shaq(matrix(out$labels), ncols=1L, checks=FALSE)
  list(centers=out$centers, labels=labels.shaq, iterations=out$niters)
}


# 4. faza: Analiza podatkov

#NAPOVED ŠTEVILA POROK

tabela1$Vrsta <- "Podatki"
model <- lm(Stevilo_sklenjenih_zakonskih_zvez~Leto, data=tabela1)
pr <- predict(model, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_porok <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Stevilo_sklenjenih_zakonskih_zvez = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_porok$Vrsta <- "Napoved"
skupna_tabela <- rbind(tabela1, napoved_porok)

graf_napoved <- ggplot(skupna_tabela, aes(x=Leto, y=Stevilo_sklenjenih_zakonskih_zvez, shape=Vrsta)) +
  geom_smooth(method=lm, se=FALSE, fullrange = TRUE) +
  geom_point(data=tabela1, aes(x=Leto, y=Stevilo_sklenjenih_zakonskih_zvez), color="red", size=3) +
  labs(title="Napoved števila porok", y="Število porok", x="Leto") + geom_point(color="red")

#NAPOVED POVPREČNE STAROSTI PRI VSTOPU V ZAKONSKO ZVEZO

#za ženske

slovenija = c('Slovenija')
sprememba_starosti_a <- tabela7 %>% filter(Drzava=="Slovenija", Meritev=="povprečna starost žensk" )
sprememba_starosti_a$Vrsta <- "Podatki"
sprememba_starosti_a$Meritev <- NULL
model_a <- lm(Povprecna_starost~Leto, data=sprememba_starosti_a)
pr <- predict(model_a, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_spremembe_starosti_a <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Povprecna_starost = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_spremembe_starosti_a$Drzava <- slovenija
napoved_spremembe_starosti_a$Vrsta <- "Napoved"
skupna_tabela_a <- rbind(sprememba_starosti_a, napoved_spremembe_starosti_a)

Španija = c('Španija')
sprememba_starosti_b <- tabela7 %>% filter(Drzava=="Španija", Meritev=="povprečna starost žensk" )
sprememba_starosti_b$Vrsta <- "Podatki"
sprememba_starosti_b$Meritev <- NULL
model_b <- lm(Povprecna_starost~Leto, data=sprememba_starosti_b)
pr <- predict(model_b, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_spremembe_starosti_b <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Povprecna_starost = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_spremembe_starosti_b$Drzava <- Španija
napoved_spremembe_starosti_b$Vrsta <- "Napoved"
skupna_tabela_b <- rbind(sprememba_starosti_b, napoved_spremembe_starosti_b)

Danska = c('Danska')
sprememba_starosti_c <- tabela7 %>% filter(Drzava=="Danska", Meritev=="povprečna starost žensk" )
sprememba_starosti_c$Vrsta <- "Podatki"
sprememba_starosti_c$Meritev <- NULL
model_c <- lm(Povprecna_starost~Leto, data=sprememba_starosti_c)
pr <- predict(model_c, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_spremembe_starosti_c <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Povprecna_starost = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_spremembe_starosti_c$Drzava <- Danska
napoved_spremembe_starosti_c$Vrsta <- "Napoved"
skupna_tabela_c <- rbind(sprememba_starosti_c, napoved_spremembe_starosti_c)

napoved_starosti <- rbind(skupna_tabela_a, skupna_tabela_b, skupna_tabela_c)

graf_napoved2 <- ggplot(napoved_starosti , aes(x=Leto, y=Povprecna_starost, col=Drzava, shape=Vrsta)) +
  geom_point() + ggtitle("Napoved povprečne starosti žensk ob vstopu v zakonsko zvezo") +
  xlab("Leto") + ylab("Povprečna starost") + facet_grid(. ~ Drzava) + stat_smooth(method="lm") + theme(axis.text.x=element_text(angle=90))

#za moške

slovenija = c('Slovenija')
sprememba_starosti_s <- tabela7 %>% filter(Drzava=="Slovenija", Meritev== "povprečna starost moških" )
sprememba_starosti_s$Vrsta <- "Podatki"
sprememba_starosti_s$Meritev <- NULL
model_s <- lm(Povprecna_starost~Leto, data=sprememba_starosti_s)
pr <- predict(model_s, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_spremembe_starosti_s <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Povprecna_starost = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_spremembe_starosti_s$Drzava <- slovenija
napoved_spremembe_starosti_s$Vrsta <- "Napoved"
skupna_tabela_s <- rbind(sprememba_starosti_s, napoved_spremembe_starosti_s)

Danska = c('Danska')
sprememba_starosti_d <- tabela7 %>% filter(Drzava=="Danska", Meritev== "povprečna starost moških" )
sprememba_starosti_d$Vrsta <- "Podatki"
sprememba_starosti_d$Meritev <- NULL
model_d <- lm(Povprecna_starost~Leto, data=sprememba_starosti_d)
pr <- predict(model_d, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_spremembe_starosti_d <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Povprecna_starost = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_spremembe_starosti_d$Drzava <- Danska
napoved_spremembe_starosti_d$Vrsta <- "Napoved"
skupna_tabela_d <- rbind(sprememba_starosti_d, napoved_spremembe_starosti_d)

Španija = c('Španija')
sprememba_starosti_sp <- tabela7 %>% filter(Drzava=="Španija", Meritev== "povprečna starost moških" )
sprememba_starosti_sp$Vrsta <- "Podatki"
sprememba_starosti_sp$Meritev <- NULL
model_sp <- lm(Povprecna_starost~Leto, data=sprememba_starosti_sp)
pr <- predict(model_sp, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_spremembe_starosti_sp <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Povprecna_starost = c(pr[1], pr[2], pr[3], pr[4], pr[5], pr[6]))
napoved_spremembe_starosti_sp$Drzava <- Španija
napoved_spremembe_starosti_sp$Vrsta <- "Napoved"
skupna_tabela_sp <- rbind(sprememba_starosti_sp, napoved_spremembe_starosti_sp)

napoved_starosti_moski <- rbind(skupna_tabela_s, skupna_tabela_d, skupna_tabela_sp)

graf_napoved3 <- ggplot(napoved_starosti_moski , aes(x=Leto, y=Povprecna_starost, col=Drzava, shape=Vrsta)) +
  geom_point() + ggtitle("Napoved povprečne starosti moških ob vstopu v zakonsko zvezo") +
  xlab("Leto") + ylab("Povprečna starost") + facet_grid(. ~ Drzava) + stat_smooth(method="lm") +
  theme(axis.text.x=element_text(angle=90))


#simple occupancy model with data in long format (one line per visit)
#this version has List Length as a covariate and Site effect
occDetBUGScode <- function(){
  # Priors
  # state model priors
  for(t in 1:nyear){
    a[t] ~ dunif(-10,10)
  }

  # RANDOM EFFECT for SITE
  for (i in 1:nsite) {
    eta[i] ~ dnorm(0, tau2)       # extra random site-effect op occ. (previously it was mu2: changed Jan 2014)
  }

  tau2 <- 1/(sigma2 * sigma2) # NOT NEEDED IN SIMPLE MODEL
  sigma2 ~ dunif(0, 5)

  # observation model priors
  for (t in 1:nyear) {
    alpha.p[t] ~ dnorm(mu.lp, tau.lp)            # p random year
  }

  mu.lp ~ dnorm(0, 0.01)
  tau.lp <- 1 / (sd.lp * sd.lp)
  sd.lp ~ dunif(0, 5)

  LL.p ~ dunif(dtype2p_min, dtype2p_max)

  # State model
  for (i in 1:nsite){
    for (t in 1:nyear){
      z[i,t] ~ dbern(muZ[i,t]) # True occupancy z at site i
      logit(muZ[i,t])<- a[t] + eta[i] # plus random site effect
    }}

  # Observation model
  # go through the visits and find the matching year and site identity
  for(j in 1:nvisit) {
    #for each visit, find the matching site and year identities
    Py[j]<- z[Site[j],Year[j]]*p[j]
    logit(p[j]) <- alpha.p[Year[j]] + LL.p*logL[j]
    y[j] ~ dbern(Py[j])
    Presi[j] <- abs(y[j]-p[j])
    y.new[j] ~ dbern(Py[j])
    Presi.new[j] <- abs(y.new[j]-p[j])
  }

  # Bayesian Goodness-of-Fit
  fit <- sum(Presi[])
  fit.new <- sum(Presi.new[])

  # Derived parameters state model

  # Finite sample occupancy
  for (t in 1:nyear) {
    psi.fs[t] <- sum(z[1:nsite,t])/nsite
  }

  # Overall trend in occpuancy
  sumY <- sum(psi.fs[1:nyear])
  for (t in 1:nyear) {
    sumxy[t] <- psi.fs[t]*t
  }
  sumXY <- sum(sumxy[1:nyear])
  regres.psi <- (sumXY - ((sumX*sumY)/nyear))/(sumX2 - ((sumX*sumX)/nyear))

  # Derived parameters observation model
  for (t in 1:nyear) {
    pdet.alpha[t] <- exp(alpha.p[t])/(1 + exp(alpha.p[t]))
  }

  # overall trend in pdet.alpha
  sumYpdet <- sum(pdet.alpha[1:nyear])
  for (t in 1:nyear) {
    sumxypdet[t] <- pdet.alpha[t]*t
  }
  sumXYpdet <- sum(sumxypdet[1:nyear])
  regres.pdet <- (sumXYpdet - ((sumX*sumYpdet)/nyear))/(sumX2 - ((sumX*sumX)/nyear))

  # end of model formulation
}


#' Pairwise SERE for two samples
#'
#' Compute the SERE coefficient for two samples
#'
#' @param observed \code{matrix} with two columns containing observed counts of two samples
#' @return The SERE coefficient for the two samples
#' @references Schulze, Kanwar, Golzenleuchter et al, SERE: Single-parameter quality control and sample comparison for RNA-Seq, BMC Genomics, 2012
#' @author See paper published

SERE <- function(observed){
  #calculate lambda and expected values
  observed = as.matrix(observed)
  laneTotals <- colSums(observed)
  total <- sum(laneTotals)
  fullObserved <- observed[rowSums(observed)>0,]
  fullLambda <- rowSums(fullObserved)/total
  fullLhat <- fullLambda > 0
  fullExpected<- outer(fullLambda, laneTotals)

  #keep values
  fullKeep <- which(fullExpected > 0)

  #calculate degrees of freedom (nrow*(ncol -1) >> number of parameters - calculated (just lamda is calculated >> thats why minus 1)
  #calculate pearson and deviance for all values
  oeFull <- (fullObserved[fullKeep] - fullExpected[fullKeep])^2/ fullExpected[fullKeep] # pearson chisq test
  dfFull <- length(fullKeep) - sum(fullLhat!=0)

  sqrt(sum(oeFull)/dfFull)
}


bound <- function(x, p = 0.0001) {
    pmax(pmin(x, 1 - p), p)
}

truncate <- function(x, p = 0.01) {
    pmin(pmax(x, p), 1 - p)
}

gfun <- function(a, w) {
    pscore <- plogis(2 - 1 / (- rowSums(w) / 4 + 1))
    return(a * pscore + (1 - a) * (1 - pscore))
}

gdeltafun <- function(a, w, delta){
    pscore <- delta * gfun(1, w) / (delta * gfun(1, w) + 1 - gfun(1, w))
    return(a * pscore + (1 - a) * (1 - pscore))
}

gdelta1 <- function(g, delta){
    delta * g / (delta * g + 1 - g)
}

pl <- function(l, a, w) {
    prob1 <- plogis(rowMeans(w) - a - log(2) + 0.2)
    return(l * prob1 + (1 - l) * (1 - prob1))
}

pz <- function(z, l, a, w) {
    prob1 <- plogis(rowSums(log(3) * w[, -3]) + a - l)
    return(z * prob1 + (1 - z) * (1 - prob1))
}

my <- function(z, l, a, w) {
    plogis(1 - 3 / (2 + rowSums(w) / 3 - l - 3 * a + z))
    ## plogis((rowSums(w) - l + 3 * a * z))
}

pzaw <- function(z, a, w) pz(z, 1, a, w) * pl(1, a, w) + pz(z, 0, a, w) * pl(0, a, w)
pzw  <- function(z, w) pzaw(z, 1, w) * gfun(1, w) + pzaw(z, 0, w) * gfun(0, w)

u <- function(z, a, w) my(z, 1, a, w) * pl(1, a, w) + my(z, 0, a, w) * pl(0, a, w)
v <- function(l, a, w) my(1, l, a, w) * pzaw(1, a, w) + my(0, l, a, w) * pzaw(0, a, w)
s <- function(l, a, w) my(1, l, a, w) * pzw(1, w) + my(0, l, a, w) * pzw(0, w)

ubar <- function(a, w) u(1, a, w) * pzaw(1, a, w) + u(0, a, w) * pzaw(0, a, w)
sbar <- function(a, w) s(1, a, w) * pl(1, a, w) + s(0, a, w) * pl(0, a, w)
vbar <- ubar
q <- function(a, w) u(1, a, w) * pzw(1, w) + u(0, a, w) * pzw(0, w)

simdata <- function(n_obs = 1000) {
    ## helper functions for nuisance parameter estimation
    ## baseline covariate -- simple, binary
    w_1 <- rbinom(n_obs, 1, prob = 0.6)
    w_2 <- rbinom(n_obs, 1, prob = 0.3)
    w_3 <- rbinom(n_obs, 1, prob = pmin(0.2 + (w_1 + w_2) / 3, 1))
    w <- cbind(w_1, w_2, w_3)
    w_names <- paste("W", seq_len(ncol(w)), sep = "")

    ## exposure/treatment
    a <- as.numeric(rbinom(n_obs, 1, prob = gfun(1, w)))

    ## mediator-outcome confounder affected by treatment
    l <- rbinom(n_obs, 1, pl(1, a, w))

    ## mediator (possibly multivariate)
    z <- rbinom(n_obs, 1, pz(1, l, a, w))
    z_names <- "Z"

    ## outcome
    y <- rbinom(n_obs, 1, my(z, l, a, w))

    colnames(w) <- w_names
    ## construct data for output
    dat <- as.data.frame(cbind(W = w, A = a, Z = z, L = l, Y = y))
    return(dat)
}

truth <- function(n_obs = 1e7, delta = 2){

    w_1 <- rbinom(n_obs, 1, prob = 0.6)
    w_2 <- rbinom(n_obs, 1, prob = 0.3)
    w_3 <- rbinom(n_obs, 1, prob = pmin(0.2 + (w_1 + w_2) / 3, 1))
    w <- cbind(w_1, w_2, w_3)
    w_names <- paste("W", seq_len(ncol(w)), sep = "")

    ## exposure/treatment
    a <- as.numeric(rbinom(n_obs, 1, prob = gfun(1, w)))

    ## mediator-outcome confounder affected by treatment
    l <- rbinom(n_obs, 1, pl(1, a, w))

    ## mediator (possibly multivariate)
    z <- rbinom(n_obs, 1, pz(1, l, a, w))
    z_names <- "Z"

    ## outcome
    y <- rbinom(n_obs, 1, my(z, l, a, w))

    eif10 <- pzaw(z, a, w) / pz(z, l, a, w) * (y - my(z, l, a, w)) +
        v(l, a, w) - vbar(a, w) + u(z, a, w)

    eif1d <- gdeltafun(a, w, delta) / gfun(a, w) * pzaw(z, a, w) / pz(z, l, a, w) *
        (y - my(z, l, a, w)) +
        gdeltafun(a, w, delta) / gfun(a, w) * (v(l, a, w) - vbar(a, w) + u(z, a, w)) +
        (1 - gdeltafun(a, w, delta) / gfun(a, w)) *
        (ubar(1, w) * gdeltafun(1, w, delta) + ubar(0, w) * gdeltafun(0, w, delta))

    eif2d <- gdeltafun(a, w, delta) / gfun(a, w) * pzw(z, w) / pz(z, l, a, w) *
        (y - my(z, l, a, w)) +
        gdeltafun(a, w, delta) / gfun(a, w) * (s(l, a, w) - sbar(a, w)) +
        u(z, 1, w) * gdeltafun(1, w, delta) + u(z, 0, w) * gdeltafun(0, w, delta) +
        gdeltafun(a, w, delta) / gfun(a, w) * (q(a, w) - (q(1, w) * gdeltafun(1, w, delta) +
                                                          q(0, w) * gdeltafun(0, w, delta)))

    efbde <- var(eif10 - eif2d)
    efbie <- var(eif2d - eif1d)

    de <- mean(eif10 - eif2d)
    ie <- mean(eif2d - eif1d)

    return(data.frame(parameter = c('DE', 'IE'),
                      truth = c(de, ie),
                      eff_bound = c(efbde, efbie)))

}


#' Function to convert SST data to d18O
#'
#' Takes a matrix of SST data (in degrees C) against time (in days), information
#' about the d18O value (in permille VSMOW) of the water and how it changes
#' through the year and the transfer function used for of the record (e.g.
#' Kim and O'Neil, 1997 or Grossman and Ku, 1986). Converts the SST data to d18O
#' data using the supplied empirical transfer function.
#'
#' @param SST Matrix with a time column (values in days) and an SST column
#' (values in degrees C)
#' @param d18Ow Either a single value (constant d18Ow) or a vector of length
#' equal to the period in SST data (365 days by default) containing information
#' about seasonality in d18Ow. Defaults to constant d18Ow of 0 permille VSMOW
#' (the modern mean ocean value)
#' @param transfer_function String containing the name of the transfer function
#' (for example: \code{"KimONeil97"} or \code{"GrossmanKu86"}). Defaults to
#' Kim and O'Neil (1997).
#' @return A vector containing d18O values for each SST value in \code{"SST"}
#' @references Grossman, E.L., Ku, T., Oxygen and carbon isotope fractionation in biogenic
#' aragonite: temperature effects, _Chemical Geology_ **1986**, _59.1_, 59-74.
#'     \doi{10.1016/0168-9622(86)90057-6}
#' Kim, S., O'Niel, J.R., Equilibrium and nonequilibrium oxygen
#' isotope effects in synthetic carbonates, _Geochimica et Cosmochimica Acta_
#' **1997**, _61.16_, 3461-3475.
#'     \doi{10.1016/S0016-7037(97)00169-5}
#' Dettman, D.L., Reische, A.K., Lohmann, K.C., Controls on the stable isotope
#' composition of seasonal growth bands in aragonitic fresh-water bivalves
#' (Unionidae), _Geochimica et Cosmochimica Acta_ **1999**, _63.7-8_, 1049-1057.
#'     \doi{10.1016/S0016-7037(99)00020-4}
#' Brand, W.A., Coplen, T.B., Vogl, J., Rosner, M., Prohaska, T., Assessment of
#' international reference materials for isotope-ratio analysis (IUPAC Technical
#' Report), _Pure and Applied Chemistry_ **2014**, _86.3_, 425-467.
#'     \doi{10.1515/pac-2013-1023}
#' Kim, S.-T., Coplen, T. B., and Horita, J.: Normalization of stable
#' isotope data for carbonate minerals: Implementation of IUPAC
#' guidelines, Geochim. Cosmochim. Ac. **2015** 158, 276-289.
#'     \doi{10.1016/j.gca.2015.02.011}
#' @examples
#' # Create dummy SST data
#' t <- seq(1, 40, 1)
#' T <- sin((2 * pi * (seq(1, 40, 1) - 8 + 10 / 4)) / 10)
#' SST <- cbind(t, T)
#' # Run d18O model function
#' d18O <- d18O_model(SST, 0, "KimONeil97")
#' @export
d18O_model <- function(SST, # Function that converts SST values into d18O
    d18Ow = 0, # Information on the d18O of seawater (d18Ow), should be either one single number (constant) or a vector of length 365 (value for every day of the year)
    transfer_function = "KimONeil97" # Supply transfer function. Current options are: Kim and O'Neil (calcite calibration; 1997; "KimONeil97") or Grossman and Ku (aragonite calibration; 1986; "GrossmanKu86")
    ){

    if(!is.numeric(d18Ow)){
        d18Ow <- rep(0, length(SST[,1])) # Set default d18Ow vector to constant at d18Ow = 0
    }else if(length(d18Ow) == 1){
        d18Ow <- rep(d18Ow, length(SST[,1])) # Create constant d18Ow vector if only one number is given
    }else{
        d18Ow <- c(0, rep(d18Ow, length(SST[,1])/length(d18Ow))) # If d18Ow is a vector with more than one value, multiply it to reach the same length of SST (multiply by "years")
    }
    if(transfer_function == "KimONeil97"){
        d18Oc <- cbind(SST[,1], (exp((18.03 * 1000 / (SST[,2] + 273.15) - 32.42) / 1000) - 1) * 1000 + (0.97001 * d18Ow - 29.99)) # Use Kim and O'Neil (1997) with conversion between VSMOW and VPDB by Brand et al. (2014) and Kim et al. (2015)
    }else if(transfer_function == "GrossmanKu86"){
        d18Oc <- cbind(SST[,1], (20.6 - SST[,2]) / 4.34 + d18Ow + 0.2) # Use Grossmann and Ku (1986) modified by Dettmann et al. (1999)
    }else{
        return("ERROR: Supplied transfer function is not recognized")
    }
    return(d18Oc)
}


subroutine master(x,y,rw,npd,ntot,nadj,madj,eps,delsgs,ndel,delsum,
                  dirsgs,ndir,dirsum,collinchk,nerror)

# Master subroutine:
# One subroutine to rule them all,
# One subroutine to find them.
# One subroutine to bring them all in,
# And in the darkness bind them.

implicit double precision(a-h,o-z)
dimension x(-3:ntot), y(-3:ntot)
dimension nadj(-3:ntot,0:madj)
dimension rw(4)
dimension delsgs(6,ndel), dirsgs(10,ndir)
dimension delsum(npd,4), dirsum(npd,3)
integer collinchk

# Define one.
one = 1.d0

# Initialize the adjacency list; counts to 0, other entries to -99.
do i = -3,ntot {
	nadj(i,0) = 0
	do j = 1,madj {
		nadj(i,j) = -99
	}
}

# Put the four ideal points into x and y and the adjacency list.
# The ideal points are given pseudo-coordinates
# (-1,-1), (1,-1), (1,1), and (-1,1).  They are numbered as
#    0       -1      -2         -3
# i.e. the numbers decrease anticlockwise from the
# `bottom left corner'.
x(-3) = -one
y(-3) =  one
x(-2) =  one
y(-2) =  one
x(-1) =  one
y(-1) = -one
x(0)  = -one
y(0)  = -one

do i = 1,4 {
        j = i-4
        k = j+1
        if(k>0) k = -3
	call insrt(j,k,nadj,madj,x,y,ntot,nerror,eps)
        if(nerror>0) return
}

# Put in the first of the point set into the adjacency list.
do i = 1,4 {
        j = i-4
	call insrt(1,j,nadj,madj,x,y,ntot,nerror,eps)
        if(nerror>0) return
}
ntri = 4

# Now add the rest of the point set
do j = 2,npd {
	call addpt(j,nadj,madj,x,y,ntot,eps,ntri,nerror)
        if(collinchk==1) call collincheck(nadj,j,madj,x,y,ntot,eps)
        if(nerror>0) {
            return
        }
        ntri = ntri + 3
}

# Obtain the description of the triangulation.
call delseg(delsgs,ndel,nadj,madj,npd,x,y,ntot,nerror)
if(nerror>0) return

call delout(delsum,nadj,madj,x,y,ntot,npd,nerror)
if(nerror>0) return

call dirseg(dirsgs,ndir,nadj,madj,npd,x,y,ntot,rw,eps,ntri,nerror)
if(nerror>0) return
call dirout(dirsum,nadj,madj,x,y,ntot,npd,rw,eps,nerror)
return
end


# --------------------------------------------
# COMMANDS FOR FITTING CONSTANT MODEL FOR EX1
# SECTION 4.1.1
# --------------------------------------------

library(bivpois)       # load bivpois library
data(ex1.sim)          # load data of example 1
# -------------------------------------------------------------------------------

xtemp<-readline(prompt = "Press Enter to Continue")

# Simple Bivariate Poisson Model
ex1.simple<-simple.bp( ex1.sim$x, ex1.sim$y ) # fit simple model of section 4.1.1

names(ex1.simple)      # monitor output variables
ex1.simple$lambda      # view lambda1
ex1.simple$BIC         # view BIC
ex1.simple             # view all results of the model
xtemp<-readline(prompt = "Press Enter to Continue")
#
# plot of loglikelihood vs. iterations
win.graph()
plot( 1:ex1.simple$iterations, ex1.simple$loglikelihood, xlab='Iterations',ylab='Log-likelihood', type='l' )

# -------------------------------------
# COMMANDS FOR FITTING MODELS FOR EX1
# SECTION 4.1.2
# -------------------------------------
ex1.m2<-lm.bp(x~1 , y~1 , data=ex1.sim, zeroL3=TRUE)			# Model 2: DblPoisson(l1, l2)
ex1.m3<-lm.bp(x~1 , y~1 , data=ex1.sim)					# Model 3: BivPoisson(l1, l2, l3)
ex1.m4<-lm.bp(x~. , y~. , data=ex1.sim, zeroL3=TRUE)			# Model 4: DblPoisson (l1=Full, l2=Full)
ex1.m5<-lm.bp(x~. , y~. , data=ex1.sim)					# Model 5: BivPoisson(l1=full, l2=full, l3=constant)
ex1.m6<-lm.bp(x~z1 , y~z1+z5 , l1l2=~z3, data=ex1.sim, zeroL3=TRUE)	# Model 6: DblPois(l1,l2)
ex1.m7<-lm.bp(x~z1 , y~z1+z5 , l1l2=~z3, data=ex1.sim)			# Model 7: BivPois(l1,l2,l3=constant)
ex1.m8<-lm.bp(x~. , y~. , l3=~., data=ex1.sim)				# Model 8: BivPoisson(l1=full, l2=full, l3=full)
ex1.m9<-lm.bp(x~. , y~. , l3=~.-z5, data=ex1.sim)			# Model 9: BivPoisson(l1=full, l2=full, l3=z1+z2+z3+z4)
ex1.m10<-lm.bp(x~z1 , y~z1+z5 , l1l2=~z3, l3=~., data=ex1.sim)		# Model 10: BivPoisson(l1, l2, l3=full)
ex1.m11<-lm.bp(x~z1 , y~z1+z5 , l1l2=~z3, l3=~.-z5, data=ex1.sim)	# Model 11: BivPoisson(l1, l2, l3= z1+z2+z3+z4)
ex1.m11$coef  # monitor all beta parameters of model 11
#
ex1.m11$beta1 # monitor all beta parameters of lambda1 of model 11
ex1.m11$beta2 # monitor all beta parameters of lambda2 of model 11
ex1.m11$beta3 # monitor all beta parameters of lambda3 of model 11


library(binom)
library(bbmle)

compute.gamma.stat = function (x, p.lower, p.upper) {
  gamma.mean.variance.loglik = function (mu, sigma.2) {
    alpha = mu * mu / sigma.2;
    beta = mu / sigma.2;
    loglik = -sum(dgamma(x, alpha, beta, log=TRUE));
    return(loglik);
  }

  if (sum(!is.na(x)) > 1 && sd(x) > 1e-5) {
    stat = mle2(gamma.mean.variance.loglik,
                start = list(mu = mean(x), sigma.2 = var(x)),
                method = 'L-BFGS-B',
                lower = p.lower, upper = p.upper);

    ci = confint(stat, 'mu', quitely=T)
    while (is(ci, 'mle2')) {
      stat = mle2(gamma.mean.variance.loglik,
                  start = list(
                    mu = unname(coef(ci)['mu.mu']),
                    sigma.2 = unname(coef(ci)['sigma.2'])
                  ),
                  method = 'L-BFGS-B',
                  lower = p.lower, upper = p.upper);

      ci = confint(stat, 'mu', quitely=T)
    }

    return(list(
      mean = unname(coef(stat)['mu']),
      sigma.2 = unname(coef(stat)['sigma.2']),
      lower = unname(ci[1]),
      upper = unname(ci[2])
    ));
  } else {
    return(compute.normal.stat(x, p.lower, p.upper, sigma.2 = var(x)));
  }
}

compute.beta.stat = function (x, p.lower, p.upper) {
  x = pmin(0.5 - 1e-16, pmax(1e-16, x))

  beta.mean.v.loglik = function (mu, v) {
    mu.2x = mu * 2;
    alpha = mu.2x * v;
    beta = (1 - mu.2x) * v;
    loglik = -sum(pmin(1e6, pmax(-1e6, dbeta(x, alpha, beta, log=TRUE))));
    return(loglik);
  }

  v.init = (mean(x) * (0.5 - mean(x))) / var(x) - 1

  if (sum(!is.na(x)) > 1 && sd(x) > 1e-10) {
    stat = mle2(beta.mean.v.loglik,
                start = list(
                  mu = mean(x),
                  v = v.init
                ),
                method = 'L-BFGS-B',
                lower = c(mu=0, v=1e-5), upper = c(mu=0.5, v=Inf));

    if (any(is.na(unname(summary(stat)@coef[, "Std. Error"])) &
        is.na(sqrt(1/diag(stat@details$hessian))))) {
      return(compute.normal.stat(x, p.lower, p.upper, v = v.init));
    }
    ci = confint(stat, 'mu', quitely=T)

    while (is(ci, 'mle2')) {
      stat = mle2(beta.mean.v.loglik,
                  start = list(
                    mu = unname(coef(ci)['mu.mu']),
                    v = unname(coef(ci)['v'])
                  ),
                  method = 'L-BFGS-B',
                  lower = c(mu=0, v=1e-5), upper = c(mu=0.5, v=Inf));

      if (any(is.na(unname(summary(stat)@coef[, "Std. Error"])) &
          is.na(sqrt(1/diag(stat@details$hessian))))) {
        return(compute.normal.stat(x, p.lower, p.upper, v = v.init));
      }
      ci = confint(stat, 'mu', quitely=T)
    }

    return(list(
      mean = unname(coef(stat)['mu']),
      v = unname(coef(stat)['v']),
      lower = unname(ci[1]),
      upper = unname(ci[2])
    ));
  } else {
    return(compute.normal.stat(x, p.lower, p.upper, v = v.init));
  }
}

compute.normal.stat = function (x, p.lower, p.upper, ...) {
  mu = mean(x);
  alpha = 0.95;

  if (length(x) > 1) {
    ci = abs(qt((1 - alpha) / 2, length(x) - 1)) * (sd(x) / sqrt(length(x)));

    return(list(
      mean = mu,
      lower = min(p.upper['mu'], max(p.lower['mu'], mu - ci)),
      upper = min(p.upper['mu'], max(p.lower['mu'], mu + ci)),
      ...
    ));
  } else {
    return(list(mean = mu, lower = NA, upper = NA, ...));
  }
}

compute.summary = function (df.group, vars, assume.normal = FALSE) {
  solved = with(df.group, solved);
  converged.at = with(df.group, extrapolation.step.solved[solved]);
  sparse.error = with(df.group, sparse.error.max[solved]);

  success.rate.stat = binom.confint(
    sum(solved), length(solved),
    conf.level = 0.95, method = 'wilson'
  );

  if (assume.normal) {
    sparse.error.stat = compute.normal.stat(
      sparse.error,
      c(mu=0, sigma.2=1e-5),
      c(mu=0.5, sigma.2=Inf),
      v = (mean(x) * (0.5 - mean(x))) / var(x) - 1
    );
  } else {
    sparse.error.stat = compute.beta.stat(
      sparse.error,
      c(mu=0, sigma.2=1e-5),
      c(mu=0.5, sigma.2=Inf)
    );
  }

  if (assume.normal) {
    converged.at.stat = compute.normal.stat(
      converged.at,
      c(mu=0, sigma.2=1e-5),
      c(mu=Inf, sigma.2=Inf),
      sigma.2 = var(x)
    );
  } else {
    converged.at.stat = compute.gamma.stat(
      converged.at,
      c(mu=0, sigma.2=1e-5),
      c(mu=Inf, sigma.2=Inf)
    );
  }

  return(data.frame(
    success.rate.mean = success.rate.stat$mean,
    success.rate.lower = success.rate.stat$lower,
    success.rate.upper = success.rate.stat$upper,
    success.rate.max = NA,

    converged.at.median = ifelse(length(converged.at) > 0, median(converged.at), NA),
    converged.at.sigma.2 = converged.at.stat$sigma.2,
    converged.at.mean = converged.at.stat$mean,
    converged.at.lower = converged.at.stat$lower,
    converged.at.upper = converged.at.stat$upper,
    converged.at.max = ifelse(length(converged.at) > 0, max(converged.at), NA),

    sparse.error.v = sparse.error.stat$v,
    sparse.error.mean = sparse.error.stat$mean,
    sparse.error.lower = sparse.error.stat$lower,
    sparse.error.upper = sparse.error.stat$upper,
    sparse.error.max = NA
  ));
}


## Functions for scaling the bootstrapped matrices

## These functions a helper functions that feed into scale_factor_R
## scale_factor_R takes the matrices elements of two contact matrices
## from the social mixR package, the matrices need to be bootstrapped ones
## as social mixr calls the element matrices when bootstrapped and matrix
## when just one value is created.

## mat1 and mat2 are the matrices objects

# Split contact matrix to only the required rows and columns
## This is because we do not have data for participants <18
split_cm <- function(mat, i = 1, row = 3:8, col = 3:8, ...){
  mat[[i]]$matrix[row,col]
}
split_cm_single <- function(mat, i = 1, row = 3:8, col = 3:8, ...){
  mat[row,col]
}

## Make the matrix symmetric
symm_mat <- function(x){
  (x + t(x))/ 2
}

## Calculate ratio of max eigen values
# This gives a sense of different magnitudes of the matrices
max_eigen_ratio <- function(x, y){
  max(eigen(x, only.values = TRUE)$values)/max(eigen(y, only.values = TRUE)$values)

}

# Take two matrices and calculate the scale factor for them
## Repeats the above functions to get the ratio of the max eigen values
## Of each matrix
scale_factor <- function(mat1, mat2, ... ){
  #x1 <- split_cm(mat1, ...)
  x2 <- split_cm(mat2, ...)
  x1 <- symm_mat(mat1)
  x2 <- symm_mat(x2)
  max_eigen_ratio(x1, x2)
}

# Take a full matrix and scale it based on ratio of mat1 and mat2
scale_matrix <- function(fullmat, mat1, mat2, i = 1, ...){
  mat1 <- mat1[[i]]$matrix

  fullmat[[i]]$matrix * scale_factor(mat1, mat2, i = i, ...)

}

## Specific for this analysis get imputed values for H2020 matrix
impute_values <- function(i = 1, school = FALSE, ...){
  imputed_values <- scale_matrix(
    polymod_boot_cm_home,
    h2020_boot_cm_home,
    polymod_boot_cm_home,
    i = i,
    ...
  ) +
    scale_matrix(
      polymod_boot_cm_work,
      h2020_boot_cm_work,
      polymod_boot_cm_work,
      i = i,
      ...
    ) +
    scale_matrix(
      polymod_boot_cm_other,
      h2020_boot_cm_other,
      polymod_boot_cm_other,
      i = i,
      ...
    )

  if(school){
    ## Schools were closed so can not scale by this so just add
    imputed_values <- imputed_values +
      polymod_boot_cm_school[[i]]$matrix
  }
  return(imputed_values)

}

update_cm <- function(
  mat1, mat2,
  impute_rows = 1:2, impute_cols = 1:8,
  i = 1,
  observed_rows = 2:8,
  observed_col = 1:8,
  school = FALSE,
  ...
){

  mat1 <- mat1[[i]]$matrix
  mat2 <- mat2[[i]]$matrix

  update_mat <- matrix(0, nrow = nrow(mat1), ncol = ncol(mat1))
  row.names(update_mat) <- colnames(mat1)
  colnames(update_mat)  <- colnames(mat1)

  #non_imputed_values <- mat1[row, col]
  update_mat <- mat1

  imputed_values <- impute_values(i = i, school = school, ... )[impute_rows, impute_cols]
  update_mat[impute_rows, impute_cols] <- imputed_values

  symm_mat(update_mat)
}

### Final function which calcualte the scaling factors for R.

scale_factor_R <- function(mat1, mat2, i = 1, ...){
  x1 <- update_cm(mat1, mat2, i = i, ...)
  x2 <- mat2[[i]]$matrix
  x1 <- symm_mat(x1)
  x2 <- symm_mat(x2)
  max_eigen_ratio(x1, x2)
}

### Repeat these for a single matrix objects.

##################

scale_factor_single <- function(mat1, mat2, ... ){
  x1 <- split_cm_single(mat1, ...)
  x2 <- split_cm_single(mat2, ...)
  x1 <- symm_mat(x1)
  x2 <- symm_mat(x2)
  max_eigen_ratio(x1, x2)
}

# Take a full matrix and scale it based on ratio of mat1 and mat2
scale_matrix_single <- function(fullmat, mat1, mat2, i = 1, ...){
  mat1 <- mat1

  fullmat * scale_factor_single(mat1, mat2, i = i, ...)

}

## Specific for this analysis get imputed values for H2020 matrix
impute_values_single <- function(i = 1, school = FALSE, ...){
  imputed_values <- scale_matrix_single(
    polymod_cm_home,
    h2020_cm_home,
    polymod_cm_home,
    i = i,
    ...
  ) +
    scale_matrix_single(
      polymod_cm_work,
      h2020_cm_work,
      polymod_cm_work,
      i = i,
      ...
    ) +
    scale_matrix_single(
      polymod_cm_other,
      h2020_cm_other,
      polymod_cm_other,
      i = i,
      ...
    )

  if(school){
    ## Schools were closed so can not scale by this so just add
    imputed_values <- imputed_values +
      polymod_cm_schoolmatrix
  }
  return(imputed_values)

}


# Winnings/losses each game per day
poker <- c(-11, 42, -49, 63, 74)
roulette <- c(-19, -8, -75, 92, 39)
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")

# Days assigned to each numerical value
names(poker) <- days
names(roulette) <- days

# Calculating total winnings/losses each day
total <- poker + roulette

# Show only profitable days
profit_check <- total > 0
profitable_days <- total[profit_check]

# Calculating profit/loss at end of week
final_amount <- sum(total)

# Comparing performance between games
total_poker <- sum(poker)
total_roulette <- sum(roulette)
if (total_poker > total_roulette) {
    print("Poker is better.")
} else {
    print("Roulette is better.")
}


#' Create object of class 'cover'.
#'
#' @param rho_1 A single numerical value between 0 and 1. Total vegetation cover, i.e. the probability of a cell to be vegetated.
#' @param rho_11 A single numerical value between 0 and 1. Vegetation cover of pairs of vegetated cells. It defaults to \code{rho_11 = rho_1^2}, which leads to an assumption of random clustering.
#' @param q_11 A single numerical value between 0 and 1. Average vegetation cover of vegetated cells, i.e.  the conditional probability of finding vegetated cells in the 4-cell neighborhood given that the focal cell is vegetated. It defaults to NULL which leads to an assumption of neutral clustering, i.e. \code{q_11 == rho_1}.
#' @param cc A single numerical value greater than 0. Clustering coefficient,
#'   describing the ratio between average local cover \code{rho_11}, and global cover \code{rho_1}. A value greater 1 denotes strong clustering. It defaults to NULL which leads to an assumption of neutral clustering, i.e. \code{rho_11 == rho_1}.
#'
#' @return A named vector of two numerical values for the global cover of vegetation (rho_1) and the cover of vegetated pairs (rho_11).
#' @export
#'
#' @examples
#'
#' ini_rho(0.9, cc = 1.2)
#'
#'

ini_rho <- function(rho_1, rho_11 = NULL, q_11 = NULL, cc = NULL) {
  if(is.null(rho_11[1]) & is.null(cc) & is.null(q_11)) {rho_11 <- rho_1^2}
  if(is.null(rho_11[1]) & !is.null(cc) & is.null(q_11)) {rho_11 <- cc*rho_1^2}
  if(is.null(rho_11[1]) & is.null(cc) & !is.null(q_11)) {rho_11 <- q_11*rho_1}
  rho_11[rho_11 > rho_1] <- rho_1[rho_11 > rho_1]

    out <- list(
      rho_1 = rho_1,
      rho_11 = rho_11#,
      #rho_10 = rho_1-rho_11,
      #rho_00 = 1-2*rho_1+rho_11,
      #rho_0 = 1-rho_1
    )

    if(any(unlist(out) < 0)) {
      out <- list(
      rho_1 = NA,
      rho_11 = NA#,
      #rho_10 = NA,
      #rho_00 = NA,
      #rho_0 = NA
    )
    warning("The value of requested clustering is not compatible with the requested total vegetation cover: please provide a lower local cover or clustering value.")
      }

  class(out) <- "cover"
  return(out)
}

#' @export
check_rho <- ini_rho


################################################################################
#
# Script that presents and tests a function that performs an LRT to two
# models that are fitted to datasets generated by multiple imputation
# and provides a test statistic per Meng and Rubin (1992)
#
# The parallel version will not work on Windows machines because of the use of
# Unix-alike specific parallel code.
#
# Andrew Robinson 15-April-2015
#
################################################################################

library(lme4)    # For mixed-effects models

library(Amelia)  # For imputation

library(equivalence) # For data that I know

library(parallel) # Speed things up

library(compiler) # A different speed-up

data(ufc)

str(ufc)

ufc <- subset(ufc, !is.na(Dbh))
ufc$Species[ufc$Species %in% c("F","FG")] <- "GF"
ufc$Species <- factor(ufc$Species)
ufc$ht.measured <- is.na(ufc$Height)

ufc <- subset(ufc,
              Species %in% names(sort(-table(Species)))[1:6],
              select = c("ht.measured","Plot","Dbh.in","Species"))

## Fit a pair of models to compare using LRT

ufc.whole <- ufc

isna.1.w <- glmer(ht.measured ~ Dbh.in + Species + (1 | Plot),
                  data = ufc.whole, family = binomial)
isna.0.w <- glmer(ht.measured ~ Dbh.in + (1 | Plot),
                  data = ufc.whole, family = binomial)

anova(isna.0.w, isna.1.w)

## Make some data missing - 5% each of Dbh, Species, and the response.

make.missing <- sample(1:nrow(ufc), size = 90, replace = FALSE)

is.na(ufc$Species[make.missing[1:30]]) <- TRUE
is.na(ufc$Dbh.in[make.missing[31:60]]) <- TRUE
is.na(ufc$ht.measured[make.missing[61:90]]) <- TRUE

## Impute datasets using Amelia

ufc.imputes <- amelia(ufc,
                      m = 5,
                      cs = "Plot",
                      noms = "Species")

names(ufc.imputes)

str(ufc.imputes$imputations)

## Fit models to each instance

ufc.models <- mclapply(ufc.imputes$imputations,
                       function (dataset) {
                         glmer(ht.measured ~ Dbh.in + Species + (1 | Plot),
                               data = dataset, family = binomial)
                       },
                       mc.cores = 4)

## Obtain average model parameters

ufc.par.hat <-
  do.call(rbind, lapply(ufc.models,
                        function (fit) {
                          c(getME(fit, "theta"), fixef(fit))
                        }))

ufc.par.hat <- colMeans(ufc.par.hat)

## Create deviance functions for each imputation

ufc.dev.funs <- mclapply(ufc.imputes$imputations,
                         function (dataset) {
                           glmer(ht.measured ~ Dbh.in + Species + (1 | Plot),
                                 data = dataset, family = binomial,
                                 devFunOnly = TRUE)
                         },
                       mc.cores = 4)

## Evaluate the deviance for each refitted model at these estimates

(ufc.dev <- sapply(ufc.dev.funs, function(devfun) devfun(ufc.par.hat)))

################################################################################

###  Ok, now develop an LRT function using these pieces.

imputeLRT.p <- function(h0, h1, imputed.data.list, mc.cores = 4) {
  m <- length(imputed.data.list)
  h0.models <- mclapply(imputed.data.list,
                        function (dataset) {
                          update(h0, data = dataset)
                        },
                        mc.cores = mc.cores)
  h0.dev.funs <- mclapply(imputed.data.list,
                          function (dataset) {
                            update(h0, data = dataset,
                                   devFunOnly = TRUE)
                          },
                          mc.cores = mc.cores)
  Q.bar.0 <-
    colMeans(do.call(rbind,
                     lapply(h0.models,
                            function (fit) {
                              c(getME(fit, "theta"), fixef(fit))
                            })))
  h1.models <- mclapply(imputed.data.list,
                        function (dataset) {
                          update(h1, data = dataset)
                        },
                        mc.cores = mc.cores)
  h1.dev.funs <- mclapply(imputed.data.list,
                          function (dataset) {
                            update(h1, data = dataset,
                                   devFunOnly = TRUE)
                          },
                          mc.cores = mc.cores)
  Q.bar.1 <-
    colMeans(do.call(rbind,
                     lapply(h1.models,
                            function (fit) {
                              c(getME(fit, "theta"), fixef(fit))
                            })))
  d.prime.m.bar <- mean(unlist(mclapply(1:m,
                                        function(i) {
                                          anova(h0.models[[i]],
                                                h1.models[[i]])$Chisq[2]
                                        },
                                        mc.cores = mc.cores)))
  d.L.bar <- mean(unlist(mclapply(1:m,
                                  function(i) {
                                    h0.dev.funs[[i]](Q.bar.0) -
                                      h1.dev.funs[[i]](Q.bar.1)
                                  },
                                  mc.cores = mc.cores)))
  p0 <- length(Q.bar.0)
  p1 <- length(Q.bar.1)
  k <- p1 - p0
  rL <- (m + 1) / ((m - 1) * k) * (d.prime.m.bar - d.L.bar) # 3.8
  D.L <- d.L.bar / ((1 + rL) * k)                           # 3.7
  v <- k * (m - 1)
  w <- ifelse(v > 4,                                        # 2.7
              4 + (v - 4) * (1 + (1 - v/2) / rL)^2,
              v / 2 * (1 + 1/k) * (1 + 1/rL)^2)
  Pval <- 1 - pf(D.L, k, w)
  return(list(
    D.L = D.L,
    Pval = Pval,
    rL = rL,
    d.L.bar = d.L.bar,
    d.prime.m.bar = d.prime.m.bar,
    Q.bar.0 = Q.bar.0,
    Q.bar.1 = Q.bar.1,
    k = k,
    w = w,
    p0 = p0,
    p1 = p1,
    m = m))
}

## Here's a test

isna.1 <- glmer(ht.measured ~ Dbh.in + Species + (1 | Plot),
                data = ufc, family = binomial)
isna.0 <- glmer(ht.measured ~ Dbh.in + (1 | Plot),
                data = ufc, family = binomial)

imputeLRT.p(isna.0, isna.1,
          list(ufc.whole, ufc.whole, ufc.whole, ufc.whole, ufc.whole))

## P-value should be comparable to

anova(isna.0.w, isna.1.w)

## Now apply to missing data cases

ufc.imputes.V <- amelia(ufc,
                        m = 5,
                        cs = "Plot",
                        noms = "Species")

imputeLRT.p(isna.0, isna.1, ufc.imputes.V$imputations)

ufc.imputes.X <- amelia(ufc,
                        m = 10,
                        cs = "Plot",
                        noms = "Species")

imputeLRT.p(isna.0, isna.1, ufc.imputes.X$imputations)

ufc.imputes.XX <- amelia(ufc,
                        m = 20,
                        cs = "Plot",
                        noms = "Species")

imputeLRT.p(isna.0, isna.1, ufc.imputes.XX$imputations)

ufc.imputes.L <- amelia(ufc,
                        m = 50,
                        cs = "Plot",
                        noms = "Species")

imputeLRT.p(isna.0, isna.1, ufc.imputes.L$imputations)

##  A version that should work more broadly

imputeLRT <- function(h0, h1, imputed.data.list) {
  m <- length(imputed.data.list)
  h0.models <- lapply(imputed.data.list,
                      function (dataset) {
                        update(h0, data = dataset)
                      })
  h0.dev.funs <- lapply(imputed.data.list,
                        function (dataset) {
                          update(h0, data = dataset,
                                 devFunOnly = TRUE)
                        })
  Q.bar.0 <-
    colMeans(do.call(rbind,
                     lapply(h0.models,
                            function (fit) {
                              c(getME(fit, "theta"), fixef(fit))
                            })))
  h1.models <- lapply(imputed.data.list,
                      function (dataset) {
                        update(h1, data = dataset)
                      })
  h1.dev.funs <- lapply(imputed.data.list,
                        function (dataset) {
                          update(h1, data = dataset,
                                 devFunOnly = TRUE)
                        })
  Q.bar.1 <-
    colMeans(do.call(rbind,
                     lapply(h1.models,
                            function (fit) {
                              c(getME(fit, "theta"), fixef(fit))
                            })))
  d.prime.m.bar <- mean(unlist(lapply(1:m,
                                      function(i) {
                                        anova(h0.models[[i]],
                                              h1.models[[i]])$Chisq[2]
                                      })))
  d.L.bar <- mean(unlist(lapply(1:m,
                                function(i) {
                                  h0.dev.funs[[i]](Q.bar.0) -
                                    h1.dev.funs[[i]](Q.bar.1)
                                })))
  p0 <- length(Q.bar.0)
  p1 <- length(Q.bar.1)
  k <- p1 - p0
  rL <- (m + 1) / ((m - 1) * k) * (d.prime.m.bar - d.L.bar) # 3.8
  D.L <- d.L.bar / ((1 + rL) * k)                           # 3.7
  v <- k * (m - 1)
  w <- ifelse(v > 4,                                        # 2.7
              4 + (v - 4) * (1 + (1 - v/2) / rL)^2,
              v / 2 * (1 + 1/k) * (1 + 1/rL)^2)
  Pval <- 1 - pf(D.L, k, w)
  return(list(
    D.L = D.L,
    Pval = Pval,
    rL = rL,
    d.L.bar = d.L.bar,
    d.prime.m.bar = d.prime.m.bar,
    Q.bar.0 = Q.bar.0,
    Q.bar.1 = Q.bar.1,
    k = k,
    w = w,
    p0 = p0,
    p1 = p1,
    m = m))
}

imputeLRT(isna.0, isna.1,
          list(ufc.whole, ufc.whole, ufc.whole, ufc.whole, ufc.whole))

imputeLRT(isna.0, isna.1, ufc.imputes.X$imputations)

imputeLRT.pw <- function(h0, h1, imputed.data.list, mc.cores = 4) {
#    browser()
    my.cluster <- makeCluster(mc.cores)
    clusterExport(my.cluster,
                  c("h0","h1","imputed.data.list"),
                  environment())
    m <- length(imputed.data.list)
    h0.models <- clusterApplyLB(my.cluster,
                                imputed.data.list,
                                function (dataset) {
                                    require(lme4)
                                    update(h0, data = dataset)
                                })
    h0.dev.funs <- clusterApplyLB(my.cluster,
                                  imputed.data.list,
                                  function (dataset) {
                                      update(h0, data = dataset,
                                             devFunOnly = TRUE)
                                  })
    Q.bar.0 <-
        colMeans(do.call(rbind,
                         lapply(h0.models,
                                function (fit) {
                                    c(getME(fit, "theta"), fixef(fit))
                                })))
    h1.models <- clusterApplyLB(my.cluster,
                                imputed.data.list,
                                function (dataset) {
                                    update(h1, data = dataset)
                                })
    h1.dev.funs <- clusterApplyLB(my.cluster,
                                  imputed.data.list,
                                  function (dataset) {
                                      update(h1, data = dataset,
                                             devFunOnly = TRUE)
                                  })
    Q.bar.1 <-
        colMeans(do.call(rbind,
                         lapply(h1.models,
                                function (fit) {
                                    c(getME(fit, "theta"), fixef(fit))
                                })))
    d.prime.m.bar <- mean(unlist(clusterApplyLB(my.cluster,
                                                1:m,
                                                function(i) {
                                                    anova(h0.models[[i]],
                                                          h1.models[[i]])$Chisq[2]
                                                })))
    d.L.bar <- mean(unlist(clusterApplyLB(my.cluster,
                                          1:m,
                                          function(i) {
                                              h0.dev.funs[[i]](Q.bar.0) -
                                                  h1.dev.funs[[i]](Q.bar.1)
                                          })))
    stopCluster(my.cluster)
    p0 <- length(Q.bar.0)
    p1 <- length(Q.bar.1)
    k <- p1 - p0
    rL <- (m + 1) / ((m - 1) * k) * (d.prime.m.bar - d.L.bar) # 3.8
    D.L <- d.L.bar / ((1 + rL) * k)                           # 3.7
    v <- k * (m - 1)
    w <- ifelse(v > 4,                                        # 2.7
                4 + (v - 4) * (1 + (1 - v/2) / rL)^2,
                v / 2 * (1 + 1/k) * (1 + 1/rL)^2)
    Pval <- 1 - pf(D.L, k, w)
    return(list(
        D.L = D.L,
        Pval = Pval,
        rL = rL,
        d.L.bar = d.L.bar,
        d.prime.m.bar = d.prime.m.bar,
        Q.bar.0 = Q.bar.0,
        Q.bar.1 = Q.bar.1,
        k = k,
        w = w,
        p0 = p0,
        p1 = p1,
        m = m))
}

imputeLRT.pw(isna.0, isna.1,
             list(ufc.whole, ufc.whole, ufc.whole, ufc.whole, ufc.whole))

imputeLRT.pw(isna.0, isna.1, ufc.imputes.X$imputations)

########

## Timing test

system.time(imputeLRT(isna.0, isna.1, ufc.imputes.XX$imputations))

system.time(imputeLRT.p(isna.0, isna.1, ufc.imputes.XX$imputations))

# system.time(imputeLRT.pw(isna.0, isna.1, ufc.imputes.XX$imputations))

imputeLRT.c <- cmpfun(imputeLRT)

system.time(imputeLRT.c(isna.0, isna.1, ufc.imputes.XX$imputations))


## Berechnung der Bachelor-Gesamtnote
# Setting up virtual environment
print("Starting R virtual environment...")
print("Executing script!")
options(digits=3)

# Input einlesen
current_gpa <- readline(prompt="Derzeitiger gesamt-GPA: ")
current_gpa <-gsub(",", ".", current_gpa)
current_gpa <- as.numeric(current_gpa)
print("Eingabe der Berechnungsgrundlage in CP")
print("ACHTUNG! Wenn die finale Note berechnet werden soll, mit der Standardanzahl der CP bei Beendigung rechnen")
print("Normalerweise 180 CP - Bachelorthesis CP")
base_cp <- readline(prompt="Berechnungsgrundlage in CP: ")
base_cp <- as.numeric(base_cp)
current_ba <- readline(prompt="(Voraussichtliche) Bachelor-Thesis-Note: ")
current_ba <-gsub(",", ".", current_ba)
current_ba <- as.numeric(current_ba)
base_bacp <- readline(prompt="CP für Thesis: ")
base_bacp <- as.numeric(base_bacp)

# Berechnung der Noten
weighted_grade_sum <- current_gpa * base_cp
print(paste0("Summe der gewichteten Noten: ", weighted_grade_sum), quote = FALSE)
print(paste0("Summe der zur Berechnung verwendeten CP: ", base_cp), quote = FALSE)
weighted_grade <- weighted_grade_sum / base_cp
print(paste0("Gewichtete Modulnote: ", weighted_grade_sum), quote = FALSE)

weighted_ba_grade_sum <- current_ba * base_bacp
full_cp <- base_cp + base_bacp
print(paste0("Bachelornote: ", current_ba), quote = FALSE)
print(paste0("Bachelor-CP: ", base_bacp), quote = FALSE)
print(paste0("Gewichtete Bachelornote: ", weighted_ba_grade_sum), quote = FALSE)

weighted_full_grade <- weighted_ba_grade_sum + weighted_grade_sum
final_grade <- weighted_full_grade / full_cp
print(paste0("Gewichtete Gesamtnote: ", weighted_full_grade), quote = FALSE)
print(paste0("Gesamte CP: ", full_cp), quote = FALSE)
print("", quote = FALSE)
print(paste0("Voraussichtliche finale Note: ", round(final_grade, digits = 3)), quote = FALSE)


# S1; S2; X; r; metric = "rec"; method = "supt"; type = "band"; plus = T; alpha = .05; h = NULL; mc.rep = 100000

#' Perform a hypothesis test for the difference between two performance curves.
#'
#' \code{PerfCurveTest} takes score vectors for two scoring algorithms and an activity vector.
#' A performance curve is created for the two scoring algorithms and hypothesis tests are performed
#' at the selected testing fractions.
#'
#' @param S1 a vector of scores for scoring algorithm 1.
#' @param S2 a vector of scores for scoring algorithm 2.
#' @param X a vector of activities.
#' @param r a vector of testing fractions.
#' @param metric the performance curve to use. Options are recall ("rec") and precision ("prec").
#' @param method the method to use. Recall options are
#' c("EmProc", "binomial", "JZ ind", "mcnemar", "binomial ind"). Precision options are
#' c("EmProc", "binomial", "JZ ind", "stouffer", "binomial ind").
#' @param plus should plus correction be used or not?
#' @param alpha the significance level.
#'
#' @export
PerfCurveTest <- function(S1, S2, X, r, metric = "rec", type = "pointwise", method = "EmProc",
                          plus = T, alpha = .05, h = NULL, seed = 111, mc.rep = 100000){

  set.seed(seed)
  m <- length(S1) #total sample size
  r.all <- (1:m)/m
  idx <- which(r.all %in% r)

  # Convert fractions in r to thresholds, for both sets of scores
  # Also accumulate the number of actives, for each score and jointly
  F1cdf <- ecdf(S1); yyobs <- sort(unique(S1))
  F1inv <- stepfun(x = F1cdf(yyobs), y = c(yyobs, max(yyobs)), right=TRUE, f=1)
  F2cdf <- ecdf(S2); yyobs <- sort(unique(S2))
  F2inv <- stepfun(x = F2cdf(yyobs), y = c(yyobs, max(yyobs)), right=TRUE, f=1)
  hits1 <- hits2 <- hits12 <- ntest12 <- vector(length = length(r))
  for(i in 1:length(r)) {
    t1 <- F1inv(1-r[i])
    t2 <- F2inv(1-r[i])
    hits1[i] <- sum(X*(S1 > t1))
    hits2[i] <- sum(X*(S2 > t2))
    hits12[i] <- sum(X*(S1 > t1 & S2 > t2))
    ntest12[i] <- sum(S1 > t1 & S2 > t2)
  }
  nact <- sum(X) #total number of actives
  ntest <- (m*r) #number of compounds tested

  # Uncorrected parameters
  r <- ntest/m
  pi.0 <- nact/m
  k1 <- (hits1)/nact
  k2 <- (hits2)/nact
  k12 <- (hits12)/nact
  pi1 <- pi.0/r*k1
  pi2 <- pi.0/r*k2
  r12 <- ntest12/m
  pi12 <- hits12/ntest12
  pi12 <- ifelse(is.na(pi12), 0, pi12)
  # pooled estimates
  pi <- (pi1 + pi2)/2
  k <- (k1 + k2)/2

  if(plus) {
    hits1.c <- hits1 + 1
    hits2.c <- hits2 + 1
    nact.c <- nact + 2
    ntest.c <- ntest + 1
    m.c <- m + 2
  } else {
    hits1.c <- hits1
    hits2.c <- hits2
    nact.c <- nact
    ntest.c <- ntest
    m.c <- m
  }

  # Corrected parameters
  r.c <- ntest.c/m.c
  pi.0.c <- nact.c/m.c
  k1.c <- (hits1.c)/nact.c
  k2.c <- (hits2.c)/nact.c
  k12.c <- (hits12)/nact.c
  pi1.c <- (hits1.c)/(ntest.c)
  pi2.c <- (hits2.c)/(ntest.c)
  r12.c <- ntest12/m.c
  pi12.c <- hits12/ntest12
  pi12.c <- ifelse(is.na(pi12.c), 0, pi12.c)

  Lam1.vec <- vector(length = length(k1))
  Lam2.vec <- vector(length = length(k1))
  for(j in seq_along(k1)){
    Lam1.vec[j] <- EstLambda(S1, X, t = F1inv(1-r[j]), idx = idx[j], h)
    Lam2.vec[j] <- EstLambda(S2, X, t = F2inv(1-r[j]), idx = idx[j], h)
  }

  CI.int <- matrix(ncol = 2, nrow = length(k1))
  se.p <- se.np <- p.val <- vector(length = length(k1))
  if(metric == "rec") {
    if(type == "pointwise") {
      quant <- qnorm(1-alpha/2)

      if(method %in% c("JZ ind", "EmProc")){
        for(j in seq_along(k1)) {
          Lam1 <- Lam1.vec[j]
          Lam2 <- Lam2.vec[j]

          # unpooled variances
          var1.k.np <- ((k1.c[j]*(1-k1.c[j]))/(m.c*pi.0.c))*(1-2*Lam1) +
            (Lam1^2*(1-r.c[j])*r.c[j])/(m.c*pi.0.c^2)
          # Check to see if var.k.np is negative due to m.cachine precision problem.c
          var1.k.np <- ifelse(var1.k.np < 0, 0, var1.k.np)
          var2.k.np <- ((k2.c[j]*(1-k2.c[j]))/(m.c*pi.0.c))*(1-2*Lam2) +
            (Lam2^2*(1-r.c[j])*r.c[j])/(m.c*pi.0.c^2)
          var2.k.np <- ifelse(var2.k.np < 0, 0, var2.k.np)
          cov.k.np <- (m.c^-1*pi.0.c^-2)*(pi.0.c*(k12.c[j]-k1.c[j]*k2.c[j])*(1-Lam1-Lam2) +
                                            (r12.c[j]-r.c[j]^2)*Lam1*Lam2)

          # pooled variances
          var1.k.p <- ((k[j]*(1-k[j]))/(m*pi.0))*(1-2*Lam1) +
            (Lam1^2*(1-r[j])*r[j])/(m*pi.0^2)
          var1.k.p <- ifelse(var1.k.p < 0, 0, var1.k.p)
          var2.k.p <- ((k[j]*(1-k[j]))/(m*pi.0))*(1-2*Lam2) +
            (Lam2^2*(1-r[j])*r[j])/(m*pi.0^2)
          var2.k.p <- ifelse(var2.k.p < 0, 0, var2.k.p)
          cov.k.p <- (m^-1*pi.0^-2)*(pi.0*(k12[j]-k[j]*k[j])*(1-Lam1-Lam2) +
                                       (r12[j]-r[j]^2)*Lam1*Lam2)

          if(method == "JZ ind"){
            # assuming independence of k
            var.k.p <- var1.k.p + var2.k.p
            var.k.np <- var1.k.np + var2.k.np
          } else if(method == "EmProc") {
            var.k.p <- var1.k.p + var2.k.p - 2*cov.k.p
            var.k.np <- var1.k.np + var2.k.np - 2*cov.k.np
          }
          var.k.p <- ifelse(var.k.p < 0, 0, var.k.p)
          var.k.np <- ifelse(var.k.np < 0, 0, var.k.np)
          se.p[j] <- sqrt(var.k.p)
          se.np[j] <- sqrt(var.k.np)

          # Use pooled variance for tests, and unpooled for CIs
          CI.int[j, ] <- c( (k1[j]-k2[j]) - quant*se.np[j],
                            (k1[j]-k2[j]) + quant*se.np[j])
          zscore <- ( (k1[j]-k2[j]) )/se.p[j]
          p.val[j] <- 2*pnorm(-abs(zscore))
          p.val[j] <- ifelse(is.na(p.val[j]), 1, p.val[j])
        }
      } else if(method %in% c("binomial ind", "binomial")) {
        for(j in seq_along(k1.c)) {

          # unpooled variances
          var1.k.np <- (m.c*pi.0.c)^-1*(k1.c[j])*(1-k1.c[j])
          var2.k.np <- (m.c*pi.0.c)^-1*(k2.c[j])*(1-k2.c[j])
          cov.k.np <- (m.c*pi.0.c)^-1*(k12.c[j] - k1.c[j]*k2.c[j])

          # pooled variances
          var1.k.p <- (m*pi.0)^-1*(k[j])*(1-k[j])
          var2.k.p <- (m*pi.0)^-1*(k[j])*(1-k[j])
          cov.k.p <- (m*pi.0)^-1*(k12[j] - k[j]*k[j])

          if(method == "binomial") {
            var.k.p <- var1.k.p + var2.k.p - 2*cov.k.p
            var.k.np <- var1.k.np + var2.k.np - 2*cov.k.np
          } else if(method == "binomial ind") {
            var.k.p <- var1.k.p + var2.k.p
            var.k.np <- var1.k.np + var2.k.np
          }
          var.k.p <- ifelse(var.k.p < 0, 0, var.k.p)
          var.k.np <- ifelse(var.k.np < 0, 0, var.k.np)
          se.p[j] <- sqrt(var.k.p)
          se.np[j] <- sqrt(var.k.np)
          CI.int[j, ] <- c( (k1[j]-k2[j]) - quant*se.np[j],
                            (k1[j]-k2[j]) + quant*se.np[j])
          zscore <- ( (k1[j]-k2[j]) )/se.p[j]
          p.val[j] <- 2*pnorm(-abs(zscore))
          p.val[j] <- ifelse(is.na(p.val[j]), 1, p.val[j])
        }
      } else if(method == "mcnemar") {
        for(j in seq_along(k1.c)) {
          BminusC <- hits1[j] - hits2[j]
          BplusC <- hits1[j] + hits2[j] - 2*hits12[j]
          BminusC.c <- hits1.c[j] - hits2.c[j]
          BplusC.c <- hits1.c[j] + hits2.c[j] - 2*hits12[j]
          zscore <- ifelse(BplusC>0, BminusC/sqrt(BplusC), 0)
          p.val[j] <- 2*pnorm(-abs(zscore))
          se.np[j] <- sqrt(BplusC.c - BminusC.c^2/nact.c) / nact.c
          # TODO use the actual difference as the center point for the CI?
          # No, use actual BP corrected McNemar for a fair comparison
          CI.int[j,] <- c( BminusC.c/nact.c - quant*se.np[j], BminusC.c/nact.c + quant*se.np[j])
        }
      }
    } else if (type == "band") {
      if(method == "sup-t") {
        cor.C <- matrix(NA, ncol = length(k1), nrow = length(k1))
        for(f in seq_along(k1)) {
          for(e in 1:f) {

            # Alg 1 unpooled variance
            Lam11 <- Lam1.vec[e]; Lam21 <- Lam2.vec[e];
            var1.k.r1 <- ((k1.c[e]*(1-k1.c[e]))/(m.c*pi.0.c))*(1-2*Lam11) +
              (Lam11^2*(1-r.c[e])*r.c[e])/(m.c*pi.0.c^2)
            var1.k.r1 <- ifelse(var1.k.r1 < 0, 0, var1.k.r1)
            var2.k.r1 <- ((k2.c[e]*(1-k2.c[e]))/(m.c*pi.0.c))*(1-2*Lam21) +
              (Lam21^2*(1-r.c[e])*r.c[e])/(m.c*pi.0.c^2)
            var2.k.r1 <- ifelse(var2.k.r1 < 0, 0, var2.k.r1)
            cov.k.r1 <- (m.c^-1*pi.0.c^-2)*(pi.0.c*(k12.c[e]-k1.c[e]*k2.c[e])*(1-Lam11-Lam21) +
                                              (r12.c[e]-r.c[e]^2)*Lam11*Lam21)
            var.difk.r1 <- var1.k.r1 + var2.k.r1 - 2*cov.k.r1

            # Alg 2 unpooled variances
            Lam12 <- Lam1.vec[f]; Lam22 <- Lam2.vec[f];
            var1.k.r2 <- ((k1.c[f]*(1-k1.c[f]))/(m.c*pi.0.c))*(1-2*Lam12) +
              (Lam12^2*(1-r.c[f])*r.c[f])/(m.c*pi.0.c^2)
            var1.k.r2 <- ifelse(var1.k.r2 < 0, 0, var1.k.r2)
            var2.k.r2 <- ((k2.c[f]*(1-k2.c[f]))/(m.c*pi.0.c))*(1-2*Lam22) +
              (Lam22^2*(1-r.c[f])*r.c[f])/(m.c*pi.0.c^2)
            var2.k.r2 <- ifelse(var2.k.r2 < 0, 0, var2.k.r2)
            cov.k.r2 <- (m.c^-1*pi.0.c^-2)*(pi.0.c*(k12.c[f]-k1.c[f]*k2.c[f])*(1-Lam12-Lam22) +
                                              (r12.c[f]-r.c[f]^2)*Lam12*Lam22)
            var.difk.r2 <- var1.k.r2 + var2.k.r2 - 2*cov.k.r2

            # Covariance
            cov.k11.k12 <- ((m.c^-1*pi.0.c^-2)*(pi.0.c*(k1.c[e]-k1.c[e]*k1.c[f])*(1-Lam11-Lam12) +
                                                       (r.c[e]-r.c[e]*r.c[f])*Lam11*Lam12))
            cov.k21.k22 <- ((m.c^-1*pi.0.c^-2)*(pi.0.c*(k2.c[e]-k2.c[e]*k2.c[f])*(1-Lam21-Lam22) +
                                                  (r.c[e]-r.c[e]*r.c[f])*Lam21*Lam22))

            t1 <- F1inv(1-r[e])
            t2 <- F2inv(1-r[f])
            hits11.22 <- sum(X*(S1 > t1 & S2 > t2))
            ntest11.22 <- sum(S1 > t1 & S2 > t2)
            k11.22.c <- (hits11.22)/nact.c
            r11.22.c <- ntest11.22/m.c

            cov.k11.k22 <- (m.c^-1*pi.0.c^-2)*(pi.0.c*(k11.22.c-k1.c[e]*k2.c[f])*(1-Lam11-Lam22) +
                                              (r11.22.c-r.c[e]*r.c[f])*Lam11*Lam22)

            t1 <- F1inv(1-r[f])
            t2 <- F2inv(1-r[e])
            hits12.21 <- sum(X*(S1 > t1 & S2 > t2))
            ntest12.21 <- sum(S1 > t1 & S2 > t2)
            k12.21.c <- (hits12.21)/nact.c
            r12.21.c <- ntest12.21/m.c

            cov.k12.k21 <- (m.c^-1*pi.0.c^-2)*(pi.0.c*(k12.21.c-k1.c[f]*k2.c[e])*(1-Lam21-Lam12) +
                                                 (r12.21.c-r.c[f]*r.c[e])*Lam21*Lam12)

            cov.difk <- cov.k11.k12 + cov.k21.k22 - cov.k11.k22 - cov.k12.k21

            if(e == f) {
              # If the covariance serves as a variance then it cant be non-negative
              # otherwise, it can be negative
              cov.difk <- ifelse(cov.difk < 0, 0, cov.difk)
            }
            cor.difk <- cov.difk/(sqrt(var.difk.r1)*sqrt(var.difk.r2))
            cor.difk <- ifelse(var.difk.r1 == 0 | var.difk.r2 == 0, ifelse(e == f, 1, 0), cor.difk)
            cor.C[e, f] <- cor.difk
            cor.C[f, e] <- cor.difk
          }
        }
        mc.samples <- MASS::mvrnorm(n = mc.rep, rep(0, length = length(k)), cor.C, tol = 1)
        max.q <- vector(length = mc.rep)
        for(j in 1:mc.rep) {
          max.q[j] <- max(abs(mc.samples[j, ]))
        }
        # This should be divided by two because standard normal dist and not the chi-square
        # in Montiel Olea and Plagborg-Møller
        quant <- quantile(max.q, probs = 1-alpha/2)
      } else if(method == "theta-proj") {
        quant <- sqrt(qchisq(1-alpha, length(k1)))
      } else if(method == "bonf") {
        quant <- qnorm(1-alpha/(2*length(k1)))
      }
      for(j in seq_along(k)) {
        Lam1 <- Lam1.vec[j]
        Lam2 <- Lam2.vec[j]

        print(quant)
        # unpooled variances
        var1.k.np <- ((k1.c[j]*(1-k1.c[j]))/(m.c*pi.0.c))*(1-2*Lam1) +
          (Lam1^2*(1-r.c[j])*r.c[j])/(m.c*pi.0.c^2)
        # Check to see if var.k.np is negative due to machine precision problem
        var1.k.np <- ifelse(var1.k.np < 0, 0, var1.k.np)
        var2.k.np <- ((k2.c[j]*(1-k2.c[j]))/(m.c*pi.0.c))*(1-2*Lam2) +
          (Lam2^2*(1-r.c[j])*r.c[j])/(m.c*pi.0.c^2)
        var2.k.np <- ifelse(var2.k.np < 0, 0, var2.k.np)
        cov.k.np <- (m.c^-1*pi.0.c^-2)*(pi.0.c*(k12.c[j]-k1.c[j]*k2.c[j])*(1-Lam1-Lam2) +
                                          (r12.c[j]-r.c[j]^2)*Lam1*Lam2)
        var.k.np <- var1.k.np + var2.k.np - 2*cov.k.np
        var.k.np <- ifelse(var.k.np < 0, 0, var.k.np)
        se.np[j] <- sqrt(var.k.np)
        CI.int[j, ] <- c( (k1[j]-k2[j]) - quant*se.np[j],
                          (k1[j]-k2[j]) + quant*se.np[j])
        p.val[j] <- NA
      }
    }
    # difference estimates
    est <- (k1 - k2)
  } else if(metric == "prec") {
    # TODO implement pooled and unpooled variances here
    if(method %in% c("JZ ind", "EmProc")) {
      for(j in seq_along(pi1.c)) {
        Lam1 <- Lam1.vec[j]
        var1.pi <- (pi1.c[j]*(1-pi1.c[j]))/(m*r[j]) + (1-r[j])*(pi1.c[j]-Lam1)^2/(m*r[j])
        var1.pi <- ifelse(var1.pi < 0, 0, var1.pi)
        Lam2 <- Lam2.vec[j]
        var2.pi <- (pi2.c[j]*(1-pi2.c[j]))/(m*r[j]) + (1-r[j])*(pi2.c[j]-Lam2)^2/(m*r[j])
        var2.pi <- ifelse(var2.pi < 0, 0, var2.pi)
        cov.pi <- ((m^-1*r[j]^-2))*(r12[j]*pi12.c[j]*(1 - pi12.c[j]) +
                                      (pi12.c[j]-Lam1)*(pi12.c[j]-Lam2)*(r12[j] - r[j]^2))

        if(method == "JZ ind") {
          var.pi <- var1.pi + var2.pi
        } else if(method == "EmProc") {
          var.pi <- var1.pi + var2.pi - 2*cov.pi
        }

        # Check to see if var.k is negative due to machine precision problem
        var.pi <- ifelse(var.pi < 0, 0, var.pi)
        se <- sqrt(var.pi)
        CI.int[j, ] <- cbind((pi1[j] - pi2[j]) - quant*se, (pi1[j] - pi2[j]) + quant*se)
        zscore <- (pi1[j] - pi2[j])/se
        p.val[j] <- 2*pnorm(-abs(zscore))
        p.val[j] <- ifelse(is.na(p.val[j]), 1, p.val[j])
      }
    } else if(method %in% c("binomial ind", "binomial")) {
      for(j in seq_along(k1.c)) {
        var1.pi <- (pi.c[j]*(1-pi.c[j]))/(m*r[j])
        var2.pi <- (pi.c[j]*(1-pi.c[j]))/(m*r[j])
        cov.pi <- (r12[j]/(m*r[j]^2))*(pi12.c[j]*(1 - pi12.c[j]))

        if(method == "binomial") {
          var.pi <- var1.pi + var1.pi - 2*cov.pi
        } else if(method == "binomial ind") {
          var.pi <- var1.pi + var1.pi
        }

        # Check to see if var.k is negative due to machine precision problem
        var.pi <- ifelse(var.pi < 0, 0, var.pi)
        se <- sqrt(var.pi)
        CI.int[j, ] <- cbind((pi1.c[j] - pi2.c[j]) - quant*se, (pi1.c[j] - pi2.c[j]) + quant*se)
        zscore <- (pi1.c[j] - pi2.c[j])/se
        p.val[j] <- 2*pnorm(-abs(zscore))
        p.val[j] <- ifelse(is.na(p.val[j]), 1, p.val[j])
      }
    } else if(method == "stouffer") {
      # TODO check that stouffer implemented correctly after changes
      for(j in seq_along(pi1.c)) {
        Sorder1 <-  order(S1,decreasing=TRUE)
        Sorder2 <-  order(S2,decreasing=TRUE)
        overlap.idx <- Sorder1[which(Sorder1[1:idx[j]] %in% Sorder2[1:idx[j]])]
        S1.unique.idx <- Sorder1[which(!Sorder1[1:idx[j]] %in% Sorder2[1:idx[j]])]
        S2.unique.idx <- Sorder2[which(!Sorder2[1:idx[j]] %in% Sorder1[1:idx[j]])]
        n12 <- length(overlap.idx)
        a <- sum(X[overlap.idx])
        d <- n12 - a
        n1 <- m*r[j] - n12
        e <- sum(X[S1.unique.idx])
        g <- sum(X[S2.unique.idx])
        p.1 <- e/n1
        p.2 <- g/n1
        p.bar <- (e + g)/(2*n1)
        if(correction == "plus") {
          n1 <- n1 + 4
          e <- e + 2
          g <- g + 2
        }
        z1 <- (p.1 - p.2)/sqrt((2*p.bar*(1-p.bar))/n1)
        z1 <- ifelse(is.na(z1), 0, z1)
        z3 <- 0
        w <- (n1 + n1)/(2*n12 + n1 + n1)
        z5 <- (w*z1 + (1-w)*z3)/(sqrt(w^2 + (1-w)^2))
        p.val[j] <- 2*(1 - pnorm(abs(z5)))
      }
    }
    # difference estimates
    est <- pi1 - pi2
  }
  list(diff_estimate = est, std_err = se.np, ci_interval = CI.int, p_value = p.val)
}


#' Estimating the isodensity (ISO) environmental contours
#'
#' @description
#' This function estimates the isodensity (ISO) environmental contours for a fitted joint distribution of
#' class \code{ht} or \code{wln}.
#'
#' @param object the fitted joint distribution object as generated by function \code{\link{fit_ht}}
#' or \code{\link{fit_wln}}.  See details.
#'
#' @param output_rp the required return periods (in years) for the estimated contours.
#'
#' @param n_point the number of points to output around each contour. The default value is 100.
#'
#' @details
#' The estimated isodensity contour for is a set of points with constant joint
#' probability density, equal to that at the anchor point.  The anchor point for the \eqn{T}-year contour
#' is the \eqn{K}-year return level on wave height \code{hs}, and the conditional median of
#' the wave period \code{tp}. See the guidance by DNV-GL section 3.7.2.
#'
#' Due to the challenge of the non-parametric estimation of a joint denisty, the current version only
#' supports joint probability density estimation based on
#' the parametric form of the Weibull-log-normal model or the parametric part of the Heffernan-Tawn
#' model.  The residual distribution in the Heffernan-Tawn is approximted by a normal
#' distribution, rather than estimated using the kernel density.
#'
#' For this same reason, this function does not support sample data as the input \code{object}, unlike
#' the other contour estimation functions in this package.  When applied
#' to a fitted \code{ht} object, the quadrant below the dependence thresholds is ignored.
#'
#' @return
#' A set of estimated ISO contours with the specified return periods in the format
#' of a \code{data.table} with \code{rp}, \code{hs}, and \code{tp} as columns.
#'
#' @examples
#' # Estimating ISO contours based on a fitted ht object
#' data(ww3_pk)
#' ht = fit_ht(data = ww3_pk, npy = nrow(ww3_pk)/10, margin_thresh_count = 50, dep_thresh_count = 500)
#' ec_ht = estimate_iso(object = ht, output_rp = c(10,100,1000,10000))
#' plot_ec(ec = ec_ht, raw_data = ww3_pk)
#'
#' # Estimating ISO contours based on a fitted wln object
#' data(ww3_ts)
#' wln = fit_wln(data = ww3_ts, npy = nrow(ww3_ts)/10)
#' ec_wln = estimate_iso(object = wln, output_rp = c(10,100,1000,10000))
#' plot_ec(ec = ec_wln, raw_data = ww3_ts)
#'
#' @seealso \code{\link{fit_ht}}, \code{\link{fit_wln}}, \code{\link{plot_ec}}
#'
#' @references
#' DNVGL-RP-C205, Recommended Practice - Environmental conditions and environmental loads,
#' Edition August 2017, https://rules.dnvgl.com/docs/pdf/DNVGL/RP/2017-08/DNVGL-RP-C205.pdf
#'
#' Heffernan, Janet & A. Tawn, Jonathan. (2004). A Conditional Approach for Multivariate Extreme Values.
#' Journal of the Royal Statistical Society Series B. 66. 497-546. 10.1111/j.1467-9868.2004.02050.x.
#'
#'
#' @export
estimate_iso = function(object, output_rp, n_point=100){

  if("ht" %in% class(object)){

    res = .estimate_iso_from_ht(
      ht = object, output_rp = output_rp,
      n_point = n_point)

  }else if("wln" %in% class(object)){

    res = .estimate_iso_from_wln(
      wln = object, output_rp = output_rp,
      n_point = n_point)

  }else if("data.table" %in% class(object)){

    stop("Estimation of the isodensity contours based on sample data is not currently supported. Please consider
         the alternative types of contours or fitting a Weibull-log-normal or Heffernan-Tawn distribtuion to
         the data set first.")

  }else{

    stop("The input object must be of class ht or wln.")
  }
  return(res)
}

# Estimate the contour based on a formulated wln --------------------------

.estimate_iso_from_wln = function(wln, output_rp, n_point, ...){

  ## Anchor point log-density
  hs_par = wln$hs$par
  tp_par = wln$tp$par
  prob_hs = 1-1/(output_rp*wln$npy)
  hs0 = qweibull(p = prob_hs, shape = hs_par[["shape"]], scale = hs_par[["scale"]])+hs_par[["loc"]]
  tp0 = exp(tp_par[1] + tp_par[2] * hs0^tp_par[3])
  ld0 = .ldwln(hs0, tp0, hs_par, tp_par)

  ## Main loop
  res_list = list()
  for(i in 1:length(output_rp)){

    ### Identify ranges of hs
    this_hs_range = .find_hs_range_given_ldwln(
      target_ldwln = ld0[i], hs_par = hs_par, tp_par = tp_par, hs_rl = hs0[i])
    this_calc = data.table(hs = seq(this_hs_range[1], this_hs_range[2], length.out = round(n_point/2)+1))

    ### Find the corresponding tp
    this_calc[, mean_log:=tp_par[1] + tp_par[2] * (hs^tp_par[3])]
    # this_calc[, sd_log:=tp_par[4] + tp_par[5] * exp(tp_par[6] * hs)]
    this_calc[, sd_log:=pmax(.limit_zero, tp_par[4] + tp_par[5] * exp(tp_par[6] * hs))]
    this_calc[, ld_hs:=log(dweibull(hs-hs_par[["loc"]], shape=hs_par[["shape"]], scale=hs_par[["scale"]]))]
    this_calc[, ld_tp:=ld0[i]-ld_hs]
    for(i_row in 1:this_calc[,.N]){
      i_tp_range = this_calc[i_row, .find_tp_given_ldtp(ld_tp, mean_log, sd_log)]
      this_calc[i_row, tp_lb:=i_tp_range[1]]
      this_calc[i_row, tp_ub:=i_tp_range[2]]
    }

    ### Return
    res_list[[i]] = this_calc[, .(
      rp = output_rp[i],
      hs = hs[c(1:(.N-1), .N:1)],
      tp = c(tp_lb[1:(.N-1)], tp_ub[.N]/2+tp_lb[.N]/2, tp_ub[(.N-1):1]))]
  }

  ## Return
  res = rbindlist(res_list)
  return(res)
}

.ldwln = function(hs, tp, hs_par, tp_par){
  f_hs = dweibull(hs-hs_par[["loc"]], shape=hs_par[["shape"]], scale=hs_par[["scale"]])
  mean_log = tp_par[1] + tp_par[2] * (hs^tp_par[3])
  sd_log = pmax(.limit_zero, tp_par[4] + tp_par[5] * exp(tp_par[6] * hs))
  f_tp_hs = dlnorm(tp, meanlog = mean_log, sdlog = sd_log)
  return(log(f_hs)+log(f_tp_hs))
}

.find_max_ldwln_given_hs = function(hs, hs_par, tp_par){
  mean_log = tp_par[1] + tp_par[2] * (hs^tp_par[3])
  sd_log = pmax(.limit_zero, tp_par[4] + tp_par[5] * exp(tp_par[6] * hs))
  tp_mode = exp(mean_log-sd_log^2)
  return(.ldwln(hs, tp_mode, hs_par, tp_par))
}

.find_hs_range_given_ldwln = function(target_ldwln, hs_par, tp_par, hs_rl){
  op_fn = function(hs, hs_par, tp_par)(.find_max_ldwln_given_hs(hs, hs_par, tp_par)-target_ldwln-.limit_zero)^2
  # op1 = optim(
  #   par = hs_rl/4, fn = op_fn, hs_par=hs_par, tp_par=tp_par, method = "Brent",
  #   lower = hs_par["loc"], upper = hs_rl/1.1)
  op2 = optim(
    par = hs_rl, fn = op_fn, hs_par=hs_par, tp_par=tp_par, method = "Brent",
    lower = hs_rl/2, upper = hs_rl*2)
  # c(op1$par, op2$par)
  c(hs_par["loc"]+.limit_zero, op2$par)
}

.find_tp_given_ldtp = function(target_ldtp, mean_log, sd_log){
  op_fn = function(tp, target_ldtp, mean_log, sd_log)
    (log(dlnorm(tp, meanlog = mean_log, sdlog = sd_log))-target_ldtp)^2

  op1 = optim(
    par = .limit_zero, fn = op_fn,
    mean_log=mean_log, sd_log=sd_log, target_ldtp = target_ldtp,
    method = "Brent", lower = .limit_zero, upper = exp(mean_log-sd_log^2))
  op2 = optim(
    par = exp(mean_log-sd_log^2), fn = op_fn,
    mean_log=mean_log, sd_log=sd_log, target_ldtp = target_ldtp,
    method = "Brent", lower = exp(mean_log-sd_log^2), upper =  exp(mean_log-sd_log^2)*10)
  return(c(op1$par, op2$par))
}

# Esimate the contour based on a formulated ht ----------------------------

.estimate_iso_from_ht = function(ht, output_rp, n_point){

  ## Find anchro points
  ap = .find_ht_iso_anchor_points(ht, output_rp)

  ## Calculate density on lap_grid exceedance area only
  lap_thresh = -log(2-2*ht$dep$p_dep_thresh)
  max_lap = -2*log(2-2*(1-1/max(output_rp)/ht$npy))
  dlap = (max_lap-lap_thresh)/max(100,n_point)
  range_lap = seq(max_lap, by = -dlap, to = -max_lap)
  grid_lap = data.table(
    lap_hs = range_lap, lap_tp = rep(range_lap, each=length(range_lap)))
  setkey(grid_lap, lap_hs, lap_tp)
  ld_calc = .ld_ht(dt_lap = grid_lap[, .(lap_hs, lap_tp)], ht = ht)
  grid_lap = merge(grid_lap, ld_calc, by = c("lap_hs", "lap_tp"))

  ## Draw contours
  mat = as.matrix(dcast.data.table(grid_lap, lap_hs~lap_tp, value.var = "ld")[,-1,with=F])
  cl = contourLines(
    x = sort(range_lap), y = sort(range_lap),
    z = mat, levels = ap$ld)
  res = rbindlist(lapply(cl, function(x)data.table(ld=x$level, lap_hs=x$x, lap_tp=x$y)))

  ## Convert contour to original scale
  res[, u_hs:=.convert_lap_to_unif(lap_hs)]
  res[, hs:=.convert_unif_to_origin(
    unif = u_hs, p_thresh = ht$margin$p_margin_thresh, gpd_par = ht$margin$hs$par, emp = ht$margin$hs$emp)]

  res[, u_tp:=.convert_lap_to_unif(lap_tp)]
  res[, tp:=.convert_unif_to_origin(
    unif = u_tp, p_thresh = ht$margin$p_margin_thresh, gpd_par = ht$margin$tp$par, emp = ht$margin$tp$emp)]

  res[, angle:=atan2(y = lap_tp, x = lap_hs)/pi*180]

  ## Refine output contour
  contour = merge(res[, .(ld, hs, tp, angle)], ap[, .(ld, rp)], by = "ld")[, .(rp, hs, tp, angle)]
  setkey(contour, rp, angle)
  out = contour[, .SD[round(seq(1, .N, length.out = n_point))], .(rp)]

  ## Return
  return(out[, .(rp, hs, tp)])
}

.find_ht_iso_anchor_points = function(ht, output_rp){

  ## Find lap_hs0
  npy = ht$npy
  p_dep_thresh = ht$dep$p_dep_thresh
  p_hs0 = 1-(1/(output_rp*npy))
  if(any(p_hs0<ht$dep$p_dep_thresh)){
    warning("Not all output_rp are above the threshold for the Heffernan-Tawn model.")
    warning("Those below the threshold will be ignored.")
    warning("Increase the argument dep_thresh_count in function fit_ht, or adjust the output RP for the
            contours.")
  }
  keep_idx = which(p_hs0>=p_dep_thresh)
  lap_hs0 = -log(2*(1-p_hs0))[keep_idx]
  p_hs0 = p_hs0[keep_idx]
  output_rp = output_rp[keep_idx]

  ## Find median lap_tp0 given lap_hs0
  dep_par = ht$dep$hs$par
  dep_median_resid = median(ht$dep$hs$resid)
  lap_tp0 = lap_hs0*dep_par[["a"]]+dep_median_resid*lap_hs0^dep_par[["b"]]
  p_tp0 = 1-exp(-abs(lap_tp0))/2

  ## Find hs0
  hs0_calc = data.table(p = p_hs0, p_gpd = (p_hs0-ht$margin$p_margin_thresh)/(1-ht$margin$p_margin_thresh))
  hs_par = ht$margin$hs$par
  hs0_calc[p_gpd<1 & p_gpd>0, hs0:= qgpd(
    p_gpd, loc=hs_par[["loc"]], scale = hs_par[["scale"]], shape = hs_par[["shape"]])]

  ## Find tp0
  tp0_calc = data.table(p = p_tp0, p_gpd = (p_tp0-ht$margin$p_margin_thresh)/(1-ht$margin$p_margin_thresh))
  tp0_calc[, tp0:=quantile(ht$dep$dep_data$tp, p)]
  if(tp0_calc[p_gpd<1 & p_gpd>0, .N]>0){
    tp_par = ht$margin$tp$par
    tp0_calc[p_gpd<1 & p_gpd>0, tp0:= qgpd(
      p_gpd, loc=tp_par[["loc"]], scale = tp_par[["scale"]], shape = tp_par[["shape"]])]
  }

  ## Estimate density
  out = data.table(rp=output_rp, hs0=hs0_calc$hs0, tp0=tp0_calc$tp0, lap_hs0 = lap_hs0, lap_tp0 = lap_tp0)
  setkey(out, lap_hs0, lap_tp0)
  ld_calc = .ld_ht(dt_lap = out[, .(lap_hs=lap_hs0, lap_tp=lap_tp0)], ht = ht)
  out[, ld:=ld_calc$ld]

  ## Return
  return(out[, .(rp, hs0, tp0, ld)])
}

.ld_ht = function(dt_lap, ht){
  calc = copy(dt_lap)
  lap_thresh = -log(2-2*ht$dep$p_dep_thresh)
  out = list()
  for(cond_var in c("hs","tp")){
    dep_var = setdiff(c("hs", "tp"), cond_var)
    this_resid = ht$dep[[cond_var]]$resid
    this_mu = mean(this_resid)
    this_sigma = sd(this_resid)
    setnames(calc, paste0("lap_", cond_var), "cond_var")
    setnames(calc, paste0("lap_", dep_var), "dep_var")

    this_par = ht$dep[[cond_var]]$par
    calc[cond_var>=lap_thresh, resid:=(dep_var-this_par[["a"]]*dep_var)/(cond_var^this_par[["b"]])]
    calc[cond_var>=lap_thresh, ld_resid:=dnorm(resid, mean=this_mu, sd = this_sigma, log = T), .(resid)]
    calc[cond_var>=lap_thresh, ld_dep:=ld_resid-log(cond_var)*this_par[["b"]]]
    calc[cond_var>=lap_thresh, ld_cond:=log(.5)-abs(cond_var)]
    calc[cond_var>=lap_thresh, ld:=ld_cond+ld_dep]
    setnames(calc, "cond_var", paste0("lap_", cond_var))
    setnames(calc, "dep_var", paste0("lap_", dep_var))
    setnames(calc, "ld", paste0("ld_", cond_var))
    out[[cond_var]] = calc[, c("lap_hs", "lap_tp", paste0("ld_", cond_var)), with=F]
  }

  calc = merge(out[[1]], out[[2]], by=c("lap_hs", "lap_tp"))
  calc[lap_hs<lap_thresh & lap_tp<lap_thresh, ld:=NA_real_]
  calc[lap_hs>=lap_thresh & lap_tp>=lap_thresh, w_hs:= (lap_hs-lap_thresh)/(lap_hs+lap_tp-2*lap_thresh)]
  calc[lap_hs>=lap_thresh & lap_tp>=lap_thresh, w_tp:= 1-w_hs]
  calc[lap_hs>=lap_thresh & lap_tp>=lap_thresh, ld:=ld_hs*w_hs+ld_tp*w_tp]
  calc[lap_hs>=lap_thresh & lap_tp<lap_thresh, ld:=ld_hs]
  calc[lap_hs<lap_thresh & lap_tp>=lap_thresh, ld:=ld_tp]
  return(calc[, .(lap_hs, lap_tp, ld)])
}


# Based on the paper by Dos Reis & al, 2004

naive_s = c(0, 0, 0, 0, 0.5, 0.5, 0.75, 0.5)

find_optimal_s = function (codon_usage, expression, lengths, trna) {
    f = function (s)
        cor(tai(codon_usage, w(trna, c(0, 0, 0, 0, exp(s))), lengths),
            expression,
            method = 'spearman')

    # Fix first four values, optimize rest.
    # Optimize `log(s)` to avoid getting negative values into `s`.
    par = log(naive_s[-(1 : 4)])
    within(optim(par, f, control = list(fnscale = -1)),
           {par = c(0, 0, 0, 0, exp(par))})
}

# Reverse complement of the anticodons, in the order of anticodons as given in
# Figure 1 of dos Reis & al.
rc_anticodons = c('TTT', 'TTC', 'TTA', 'TTG',
                  'TCT', 'TCC', 'TCA', 'TCG',
                  'TAT', 'TAC', 'TAA', 'TAG',
                  'TGT', 'TGC', 'TGA', 'TGG',

                  'CTT', 'CTC', 'CTA', 'CTG',
                  'CCT', 'CCC', 'CCA', 'CCG',
                  'CAT', 'CAC', 'CAA', 'CAG',
                  'CGT', 'CGC', 'CGA', 'CGG',

                  'ATT', 'ATC', 'ATA', 'ATG',
                  'ACT', 'ACC', 'ACA', 'ACG',
                  'AAT', 'AAC', 'AAA', 'AAG',
                  'AGT', 'AGC', 'AGA', 'AGG',

                  'GTT', 'GTC', 'GTA', 'GTG',
                  'GCT', 'GCC', 'GCA', 'GCG',
                  'GAT', 'GAC', 'GAA', 'GAG',
                  'GGT', 'GGC', 'GGA', 'GGG')

met_codon = match('ATG', rc_anticodons)
stop_codons = match(c('TAA', 'TAG', 'TGA'), rc_anticodons)

w = function (counts, s = naive_s) {
    counts = counts[rc_anticodons]
    counts[is.na(counts)] = 0
    p = 1 - s
    w = vector('numeric', length(counts))

    for (i in seq(1, length(counts), by = 4)) {
        w[i]     = p[1] * counts[i]     + p[5] * counts[i + 1]
        w[i + 1] = p[2] * counts[i + 1] + p[6] * counts[i]
        w[i + 2] = p[3] * counts[i + 2] + p[7] * counts[i]
        w[i + 3] = p[4] * counts[i + 3] + p[8] * counts[i + 2]
    }

    w[met_codon] = p[4] * counts[met_codon]
    w = w[-stop_codons]
    w_rel = w / max(w)
    nonzero_w = w_rel[w != 0]
    if (length(nonzero_w) != length(w_rel))
        w_rel[w_rel == 0] = exp(sum(log(nonzero_w)) / length(nonzero_w))

    w_rel
}

tai = function (codon_counts, w, lengths) {
    codons = rc_anticodons[-stop_codons]
    exp(colSums(apply(codon_counts[, codons], 1, `*`, log(w))) / lengths)
}


mcmc_performR <- function(data, period_start, init_state_num, init_pars, fix_pars,
                          niter = 1e5, BurnIn = 5e3, model = "Multinomial", trace_num = 100,
                          step_pars = init_pars/1000, ... ){
  Prior_func <- function(pars){
    if(any(pars < 0) || any(pars[(length(pars)/2 + 1):length(pars)] > 1)){
      return (0)
    }else{
      return (1) ## Set a non-informative prior
    }
  }

  ftime <- 1:dim(data)[1]

  Log_Likelihood_calculateR <- function(pars, period_start = parent.frame()$period_start, init_state_num = parent.frame()$init_state_num,
                                        ftime = parent.frame()$ftime, fix_pars = parent.frame()$fix_pars){
    ypred <- model_deterministic_simulateR(init_obs = init_state_num, period_start = parent.frame()$period_start,
                                        times = ftime, pars = pars, fix_pars = parent.frame()$fix_pars)
    p_new_pred_n <- round(ypred[, 11])
    p_new_pred_prob <- ypred[, 12]
    rd_new_pred_n <- round(ypred[, 13])
    r_new_pred_prob <- ypred[, 14]
    d_new_pred_prob <- ypred[, 15]
    p <- c()
    if(model=="Poisson"){
      ypred <-  p_new_pred_n  *  p_new_pred_prob
      p<-try(dpois(data$Confirmed, round(ypred),log=T),silent=T)
      if(any(is.nan(p))||any(p==-Inf)){
        logL <- -Inf
      }
      else{
        logL <- sum(p)
      }
    }
    else if(model=="Binomial"){
      p <- try(dbinom(data$Confirmed,round( p_new_pred_n), p_new_pred_prob, log = T),silent = T)
      if(any(p == -Inf) || any(is.nan(p))){
        logL <- -Inf
      }else{
        logL <- sum(p)
      }
    }
    else if(model=="Multinomial"){
      for(i in 1:dim(ypred)[1]){
      p[i] = try(dbinom(data$Confirmed[i], round(p_new_pred_n[i]) , p_new_pred_prob[i], log = T),silent =T)
      obs_size = data$Confirmed[max(i - 1, 1)]
      pred_size = round(ypred[max(i - 1, 1),"P"])
      if((data$Recovered[i] + data$Deceased[i]) > pred_size){
        p[i] = -Inf
      }
      else{
        p[i] = p[i] + try(dmultinom(c(round(c(data$Recovered[i], data$Deceased[i])), pred_size - (data$Recovered[i] + data$Deceased[i])),
                                pred_size, c(r_new_pred_prob[i], d_new_pred_prob[i], 1-r_new_pred_prob[i]-d_new_pred_prob[i]),
                                log = T),silent=T)
      }
    }
    if(any(p == -Inf) || any(is.nan(p))){
      logL <- -Inf
    }else{
      logL <- sum(p)
    }
   }
    return(logL)
  }

  if(period_start[1] != 1){
    period_start = c(1, period_start)
  }
  if(length(period_start) != length(init_pars)/2)
    stop("Number of period is not equal to number of initial parameters")

  ## build the matrix to store the results

  n_period = length(init_pars)/2 # number of periods
  pmat <- matrix(0, ((niter + BurnIn) / trace_num) + 1, 3*n_period) ## parameters + R0 for n_period periods
  colnames(pmat) <- unlist(lapply(c("b","r","R0") , function(y) lapply(1:n_period, function(x) paste(y,x, sep = ""))))
  pmat[1, 1:(2*n_period)] <- init_pars
  R0_est <- R0_calculateR(estpar = init_pars, fix_pars = fix_pars)
  pmat[1, ((2*n_period)+1):(3*n_period)] <- R0_est

  ## Start MCMC
  pars_now <- init_pars

  cat("MCMC:", fill = T)
  for(i in 2:(niter + BurnIn)){
    pars_new <- rep(0, 2*n_period)
    for(j in 1:(2*n_period)){
      pars_new[j] <- rnorm(1, mean = pars_now[j], sd = step_pars[j])
    }
    A <- 0
    if(Prior_func(pars_new) > 0){
      ll_pars_new <- Log_Likelihood_calculateR(pars = pars_new)
      if(ll_pars_new != -Inf) {
        ll_pars_now <- Log_Likelihood_calculateR(pars = pars_now)
        A <-  exp(1)^(ll_pars_new - ll_pars_now) # Prior_func(pars_new) / Prior_func(pars_now) * 10^(ll_pars_new - ll_pars_now)
      }
    }

    if(runif(1) < A){
      pars_now <- pars_new
    }
    if(i %% trace_num == 0) {
      R0_est <- R0_calculateR(estpar = pars_now, fix_pars = fix_pars)
      pmat[(i / trace_num) + 1, 1:(2*n_period)] <- pars_now
      pmat[(i / trace_num) + 1, ((2*n_period)+1):(3*n_period)] <- R0_est

    }
    if(i%%(niter/10) == 0) cat("Iter", i, " A =", round(A, digits=4), " : ", round(pars_now, digits=4), fill = T)

  }
  mcmc_estimates = pmat[-c(1:(BurnIn / trace_num + 1)), ]
  return(mcmc_estimates)

}


LISP Interpreter Run

[[[[ Omega in the limit from below! ]]]]

define (count-halt prefix bits-left-to-extend)
    if = bits-left-to-extend 0
    if = success car try t 'eval read-exp prefix
       1 0
    + (count-halt append prefix '(0) - bits-left-to-extend 1)
      (count-halt append prefix '(1) - bits-left-to-extend 1)

define      count-halt
value       (lambda (prefix bits-left-to-extend) (if (= bits-l
            eft-to-extend 0) (if (= success (car (try t (' (ev
            al (read-exp))) prefix))) 1 0) (+ (count-halt (app
            end prefix (' (0))) (- bits-left-to-extend 1)) (co
            unt-halt (append prefix (' (1))) (- bits-left-to-e
            xtend 1)))))

define (omega t) cons (count-halt nil t)
                 cons /
                 cons ^ 2 t
                      nil

define      omega
value       (lambda (t) (cons (count-halt nil t) (cons / (cons
             (^ 2 t) nil))))

(omega 0)

expression  (omega 0)
value       (0 / 1)

(omega 1)

expression  (omega 1)
value       (0 / 2)

(omega 2)

expression  (omega 2)
value       (0 / 4)

(omega 3)

expression  (omega 3)
value       (0 / 8)

(omega 8)

expression  (omega 8)
value       (1 / 256)

End of LISP Run

Elapsed time is 1 seconds.