R/acopula.r

Defines functions print.isCopula isCopula print.gCopula gCopulaEmpirical gCopula rCopulaArchimax2D rCopula print.eCopulaGeneric print.eCopulaArchimax eCopulaGeneric eCopulaArchimax eCopula qCopula cCopula dCopula pCopula copula copNormal copPlackett copFGM copGumbel copProduct ldepPartition3D depfun depCC depGCC depHuslerReiss depTawn depGalambos depGumbel depMax dep1 generator genAMH genJoe genFrank genClayton genGumbel genLog pCopulaEmpirical qPareto pPareto vpartition nintegrate nderive

Documented in cCopula copFGM copGumbel copNormal copPlackett copProduct copula dCopula dep1 depCC depfun depGalambos depGCC depGumbel depHuslerReiss depMax depTawn eCopula eCopulaArchimax eCopulaGeneric gCopula gCopulaEmpirical genAMH genClayton generator genFrank genGumbel genJoe genLog isCopula ldepPartition3D nderive nintegrate pCopula pCopulaEmpirical pPareto print.eCopulaArchimax print.eCopulaGeneric print.gCopula print.isCopula qCopula qPareto rCopula rCopulaArchimax2D vpartition

# ------------------------------------------ function repository

# --- utility functions (move to utils.r afterwards)

#numerical (linear) approximation of partial derivatives of a real function
#fun can have arbitrary number of arguments, as well as derivatives can by of arbitrary order
#'order' is a sequence of the same length as is the number of arguments
#'difference' controls accuracy, 'area' allows for one-sided derivatives
#Example: nderive(function(x,y) x^2*y,c(5,11),c(2,0)) #22
#fun arguments can be sequence as well as vector 
nderive <- function(fun,point=c(0),order=c(1),difference=1e-4,area=c(0,1,-1)) {
  diffup <- difference*(1+sign(area[1]))/2; difflo <- difference*(1-sign(area[1]))/2
  ind <- t(expand.grid(lapply(order,function(x) seq(0,x,1))))
  arg <- point + (order-ind)*diffup - ind*difflo 
  if(length(formals(fun)) == length(point)) { #treatment of arguments in sequence e.g. fun(x,y) instead of fun(c(x,y))
    rownames(arg)<-NULL #due to unwanted passing of argument names in do.call
    func <- function(x) do.call(fun,as.list(x)) 
  } 
  else func <- fun
  sum(
    (-1)^apply(ind,2,sum) * 
      apply(choose(order,ind),2,prod) * 
      apply(arg,2,func)
    ) / (diffup+difflo)^sum(order)
}

#numerical integration of arbitrarily dimensional function
#arguments can form either vector or sequence
#possible to reduce integral with some bounds being equal
nintegrate <- function(fun, lower, upper, subdivisions=100, differences=(upper-lower)/subdivisions) {
  argseq <- mapply(seq.int,from=lower,to=upper,by=differences,SIMPLIFY=F,USE.NAMES=F) #make sequence
  #argseq <- mapply(function(x,y) unique(c(x,y)),argseq,upper,SIMPLIFY=F,USE.NAMES=F) #add upper limits to the sequence and remove if double
  differences <- rep(differences,length.out=length(argseq)) #ensure differences has the proper length
  indargseq <- lapply(argseq,seq_along) #indices of the sequence values
  nind <- sapply(indargseq,length) #number of grid nodes in each direction
  argsgrid <- as.matrix(expand.grid(argseq)); dimnames(argsgrid) <- NULL #grid nodes
  indargsgrid <- as.matrix(expand.grid(indargseq)); dimnames(indargsgrid) <- NULL #and their indices
  mult <- apply(indargsgrid,1,function(x) 2^sum(x>1 & x<nind)) #multiple of each node
  if(length(formals(fun)) > 1) func <- function(x) do.call(fun,as.list(x)) #treatment of arguments in sequence e.g. fun(x,y) instead of fun(c(x,y))
  else func <- fun
  fvalues <- apply(argsgrid,1,func) # compute function values in each node
  prod(differences[nind>1])*sum(fvalues*mult)/2^length(argseq[nind>1]) #final magic
}

#splits vector x to subvectors of specified lengths; create list or simplify to matrix if possible (identical lengths)
vpartition <- function(x, lengths, matrixify=TRUE) {
  n <- length(lengths)
  up <- cumsum(lengths)
  lo <- c(0,up[-n])
  sapply(mapply(function(i,j) c(x[1],x)[i:j], lo+1, up+1, SIMPLIFY = F),function(y) y[-1],simplify=matrixify)#treats zero lengths
}

#CDF of 4-parametric Pareto distribution (type IV), pars[1:3] > 0, location pars[4] in R
pPareto <- function(t,pars) ifelse(t>=pars[4], 1-(1+((t-pars[4])/pars[1])^(1/pars[3]))^(-pars[2]), 0) 
qPareto <- function(t,pars) pars[1]*((1-t)^(-1/pars[2])-1)^(pars[3])+pars[4]

# --- copula-related functions

#create function from 'base' and feed with 'data' (both being matrix or data frame)
pCopulaEmpirical <- function(data,base=data) {
  data <- rbind(data, deparse.level=0 ) #ensure data is matrix/data.frame
  fCe <- function(x) sum(apply(t(base)<=x,2,prod))
  apply(data,1,fCe)/nrow(base)
}

genLog <- function(...) {
  output <- list(
    parameters = NULL,
    pcopula = function(t, pars) prod(t),
    dcopula = function(t, pars) 1,
    rcopula = function(dim, pars) runif(dim),
    gen = function(t, pars) -log(t),
    gen.der = function(t, pars) -1/t,
    gen.der2 = function(t, pars) 1/t^2,
    gen.inv = function(t, pars) exp(-t),
    gen.inv.der = function(t, pars) -exp(-t),
    gen.inv.der2 = function(t, pars) exp(-t),
    lower = NULL,   
    upper = NULL,
    id="log"
  )
  output[names(list(...))] <- list(...) #allow user to modify(=replace) definition
  output
}

genGumbel <- function(...) {
  output <- list(
    parameters = c(4),
    pcopula = function(t, pars) exp(-sum((-log(t))^pars[1])^(1/pars[1])),
    gen = function(t, pars) (-log(t))^pars[1],
    gen.der = function(t, pars) -pars[1]*(-log(t))^(pars[1]-1)/t,
    gen.der2 = function(t, pars) pars[1]*(-log(t))^(pars[1]-2)*(pars[1]-1-log(t))/t^2,
    gen.inv = function(t, pars) exp(-t^(1/pars[1])),
    gen.inv.der = function(t, pars) -exp(-t^(1/pars[1]))*t^(1/pars[1]-1)/pars[1],
    gen.inv.der2 = function(t, pars) exp(-t^(1/pars[1]))*t^(1/pars[1]-2)*(pars[1]+t^(1/pars[1])-1)/pars[1]^2,
    kendall = list(coef=function(t) 1-1/t, icoef=function(t) 1/(1-t), bounds=c(0,1)),
    spearman = list(coef=function(t) pPareto(t,c(1.41917,2.14723,1,1)), icoef=function(t) qPareto(t,c(1.41917,2.14723,1,1)), bounds=c(0,1)),
    lower = 1,     #Pi, g(t)=-ln(t)
    upper = Inf,		#M
    id="Gumbel"
  )
  output[names(list(...))] <- list(...)
  output
}

#to fix: g(u)/(g(u)+g(v)) sends NaN  #edit: still true?
genClayton <- function(...) {
  output <- list(
    parameters = c(2),
    gen = function(t, pars) t^(-pars[1]) - 1,
    gen.der = function(t,pars) -pars[1]*t^(-pars[1] - 1),
    gen.der2 = function(t, pars) pars[1]*(1+pars[1])*t^(-2-pars[1]),
    gen.inv = function(t, pars) (1+t)^(-1/pars[1]),
    gen.inv.der = function(t, pars) -(1+t)^(-1-1/pars[1])/pars[1],
    gen.inv.der2 = function(t, pars) (1 + 1/pars[1])*(1 + t)^(-2 - 1/pars[1])/pars[1],
    kendall = list(coef=function(t) t/(t+2), icoef=function(t) 2*t/(1-t), bounds=c(0,1)),
    spearman = list(coef=function(t) pPareto(t,c(0.496534,1.95277,0.986814,0)), icoef=function(t) qPareto(t,c(0.496534,1.95277,0.986814,0)), bounds=c(0,1)),
    lower = 0.01, upper = Inf,
    id="Clayton"
  )
  output[names(list(...))] <- list(...)
  output
}

genFrank <- function(...) {
  output <- list(
    parameters = c(5),
    gen = function(t, pars) if(abs(pars[1]) < 0.00001) return(-log(t)) else -log( (exp(-pars[1]*t)-1)/(exp(-pars[1])-1) ),
    gen.der = function(t, pars) if(abs(pars[1]) < 0.00001) return(-1/t) else pars[1]*exp(-pars[1]*t)/(exp(-pars[1]*t)-1),
    gen.der2 = function(t, pars) if(abs(pars[1]) < 0.00001) return(1/t^2) else pars[1]^2*exp(pars[1]*t)/(1-exp(pars[1]*t))^2,
    gen.inv = function(t, pars) if(abs(pars[1]) < 0.00001) return(exp(-t)) else -log(1-exp(-t)+exp(-t-pars[1]))/pars[1],
    gen.inv.der = function(t, pars) if(abs(pars[1]) < 0.00001) return(-exp(-t)) else -exp(-t)/(-exp(-t)+1/(1-exp(-pars[1])))/pars[1],
    gen.inv.der2 = function(t, pars) if(abs(pars[1]) < 0.00001) return(exp(-t)) else (exp(pars[1]+t)*(-1+exp(pars[1])))/((1-exp(pars[1])+exp(pars[1]+t))^2*pars[1]),
    kendall = list(coef=function(t) pPareto(t,c(7.46846,1.25305,0.854724,0)), icoef=function(t) qPareto(t,c(7.46846,1.25305,0.854724,0)), bounds=c(0,1)), #only positive dep.
    spearman = list(coef=function(t) pPareto(t,c(13.2333,3.67989,0.857562,0)), icoef=function(t) qPareto(t,c(13.2333,3.67989,0.857562,0)), bounds=c(0,1)), #only positive dep.
    lower = -Inf, upper = Inf,
    id="Frank"
  )
  output[names(list(...))] <- list(...)
  output
}

genJoe <- function(...) {
  output <- list(
    parameters = c(3),
    gen = function(t, pars) -log(1-(1-t)^pars[1]),
    gen.der = function(t, pars) -pars[1]*(1-t)^(pars[1]-1)/(1-(1-t)^pars[1]),
    gen.der2 = function(t, pars) pars[1]*(pars[1]-1+(1-t)^pars[1])*(1-t)^(pars[1]-2)/(1-(1-t)^pars[1])^2,
    gen.inv = function(t, pars) 1-(1-exp(-t))^(1/pars[1]),
    gen.inv.der = function(t, pars) (1 - exp(-t))^(1/pars[1])/(pars[1]*(1 - exp(t))),
    gen.inv.der2 = function(t, pars) -(1-exp(-t))^(1/pars[1])*(1-pars[1]*exp(t))/(pars[1]*(1-exp(t)))^2,
    kendall = list(coef=function(t) pPareto(t,c(1.901055,1.015722,1.038055,1)), icoef=function(t) qPareto(t,c(1.901055,1.015722,1.038055,1)), bounds=c(0,1)), #only positive dep.
    spearman = list(coef=function(t) pPareto(t,c(2.42955,1.99806,1.02288,1)), icoef=function(t) qPareto(t,c(2.42955,1.99806,1.02288,1)), bounds=c(0,1)), #only positive dep.
    lower = 1,     #Pi, g(t)=-ln(t)
    upper = Inf,   #M
    id="Joe"
  )
  output[names(list(...))] <- list(...)
  output
}

genAMH <- function(...) {
  output <- list(
    parameters = c(0.1),
    gen = function(t, pars) log((1-(1-t)*pars[1])/t),
    gen.der = function(t, pars) (pars[1]-1)/(t*(1-(1-t)*pars[1])),
    gen.der2 = function(t, pars) (pars[1]-1)*(1+pars[1]*(2*t-1))/(t*(1-(1-t)*pars[1]))^2,
    gen.inv = function(t, pars) (1-pars[1])/(exp(t)-pars[1]),
    gen.inv.der = function(t, pars) -exp(t)*(1-pars[1])/(exp(t)-pars[1])^2,
    gen.inv.der2 = function(t, pars) exp(t)*(exp(t)+pars[1])*(1-pars[1])/(exp(t)-pars[1])^3,
    kendall = list(coef=function(t) (3*t-2)/(3*t)-(2*(1-t)^2*log(1-t))/(3*t^2), icoef=NULL, bounds=c(-0.1817258,1/3)), 
    spearman = list(coef=function(t) 0.641086*(-1 + 1.71131^t), icoef=function(t) log(t/0.641086+1,1.71131), bounds=c(-0.271065,0.478418)),
    lower = -1,   #par=0 => Pi   
    upper = 0.9999,
    id="AMH"
  )
  output[names(list(...))] <- list(...)
  output
}

generator <- function(name,...) {
  switch(name[1],
         "AMH"=genAMH(...),
         "Clayton"=genClayton(...),
         "Frank"=genFrank(...),
         "Gumbel"=genGumbel(...),
         "Joe"=genJoe(...),
         "log"=genLog(...),
         ({warning("Generator not recognized. Defaults to genLog().", immediate.=T);
           genLog(...)})
         )
}

#upper bound of all Pickands' dependence functions
dep1 <- function(...) {
  output <- list(
    parameters = NULL,
    dep = function(t,pars) 1,
    dep.der = function(t,pars) 0,
    dep.der2 = function(t,pars) 0,
    lower = NULL,
    upper = NULL,
    id="1"
  )
  output[names(list(...))] <- list(...)
  output
}

#lower bound of all Pickands' dependence functions
depMax <- function(power=10,...) {
  output <- list(
    parameters = NULL,
    dep = function(t,pars=NULL) (sum(t^power) + (1 - sum(t))^power)^(1/power),
    dep.der = function(t,pars=NULL) (t^(power-1)-(1-t)^(power-1))*(t^power + (1 - t)^power)^(1/power-1),
    dep.der2 = function(t,pars=NULL) (power-1)*((1-t)*t)^(power-2)*(t^power + (1 - t)^power)^(1/power-2),
    lower = NULL,
    upper = NULL,
    id="max"
  )
  output[names(list(...))] <- list(...)
  output
}

#depMax <- list(parameters = NULL,
#  dep = function(t,pars) max(t,1-sum(t)),
#  lower = NULL,
#  upper = NULL
#)

depGumbel <- function(...) {
  output <- list(
    parameters = c(2),
    pcopula = function(t, pars) exp(-sum((-log(t))^pars[1])^(1/pars)),
    dcopula = NULL,
    rcopula = NULL,
    dep = function(t,pars) (sum(t^pars[1]) + (1 - sum(t))^pars[1])^(1/pars[1]),
    dep.der = function(t,pars) (t^(pars[1]-1)-(1-t)^(pars[1]-1)) * (t^pars[1]+(1-t)^pars[1])^(1/pars[1]-1),
    dep.der2 = function(t,pars) (pars[1]-1)*(-(t-1)*t)^(pars[1]-2)*(t^pars[1] + (1 - t)^pars[1])^(1/pars[1]-2),
    kendall = list(coef=function(t) 1-1/t, icoef=function(t) 1/(1-t), bounds=c(0,1)),
    spearman = list(coef=function(t) pPareto(t,c(1.41917,2.14723,1,1)), icoef=function(t) qPareto(t,c(1.41917,2.14723,1,1)), bounds=c(0,1)),
    lower = c(1), #A=1, upper bound of all Pickands' dependence functions  
    upper = Inf,   #A=max(t1,t2,...,1-t1-t2-...), lower bound of all Pickands' dependence functions
    id="Gumbel"
  )
  output[names(list(...))] <- list(...)
  output
}

#so far only 2D
depGalambos <- function(...) {
  output <- list(
    parameters = c(0.5), 
    dep = function(t,pars) {
      #check for boundary arguments of A (prevents NAN)
      if(any(
        c(sapply(1:(length(t)+1),function(x) sum(c(t,1-sum(t))[-x]) > 1 )),
        pars[1] < 1e-5
      )
      ) return(1)
      1 - (sum(t^-pars[1]) + (1 - sum(t))^-pars[1])^(-1/pars[1])
    },
    dep.der = function(t,pars) if(pars[1] < 1e-5) return(0) else ((1-t)^(-1-pars[1])-t^(-1-pars[1]))/((1-t)^-pars[1]+t^-pars[1])^(1+1/pars[1]),
    dep.der2 = function(t,pars) {
      if(pars[1] < 1e-5) return(0) 
      if(abs(pars[1]-1) < 1e-5) return(2)
      ((1+pars[1])*(-(-1+t)*t)^(-2+pars[1])) * ((1-t)^-pars[1]+t^-pars[1])^(-1/pars[1])/((1-t)^pars[1]+t^pars[1])^2
    },
    kendall = list(coef=function(t) pPareto(t,c(0.579021,0.382682,0.48448,0)), icoef=function(t) qPareto(t,c(0.579021,0.382682,0.48448,0)), bounds=c(0,1)), 
    spearman = list(coef=function(t) pPareto(t,c(0.876443,0.484209,0.307277,0)), icoef=function(t) qPareto(t,c(0.876443,0.484209,0.307277,0)), bounds=c(0,1)),
    lower = c(0),    #A=1
    upper = c(10),		#A=max(t1,t2,...,1-t1-t2-...) 
    id="Galambos"
  )
  output[names(list(...))] <- list(...)
  output
}


#asymmetric logistic Picands' dependence function (Tawn, J. A. (1988). Bivariate extreme value theory: models and estimation)
#the first is the dependence parameter, all other are the asymmetry parameters (which when equal, results in symmetric logistic depfu)
depTawn <- function(dim=2,...) {
  parameters <- c(2,rep.int(0.5,dim))
  dep <- function(t,pars) {
    1 - pars[-1]%*%c(t,1-sum(t)) + sum((pars[-1]*c(t,1-sum(t)))^pars[1])^(1/pars[1])
  }  
  dep.der <- function(t,pars) {
    pars[3] - pars[2] + (1/pars[1])*((pars[2]*t)^pars[1]+(pars[3]*(1-t))^pars[1])^(1/pars[1]-1)*
      (pars[2]^pars[1]*pars[1]*t^(pars[1]-1) - pars[3]^pars[1]*pars[1]*(1-t)^(pars[1]-1))
  }
  dep.der2 <- function(t,pars) {
    (pars[2]*pars[3])^pars[1] * (pars[1]-1) * ((1-t)*t)^(pars[1]-2) * ((pars[2]*t)^pars[1]+(pars[3]*(1-t))^pars[1])^(1/pars[1]-2)
  }
  lower <- c(1,rep.int(0,dim))  #A=1, indep.
  upper <- c(Inf,rep.int(1,dim))	#A=max(t1,t2,...,1-t1-t2-...), perf.pos.dep
  output <- list(
    parameters=parameters,dep=dep,
    dep.der=if(dim==2) dep.der else NULL, dep.der2=if(dim==2) dep.der2 else NULL,
    lower=lower,upper=upper,
    id="Tawn"
  )
  output[names(list(...))] <- list(...)
  output
}


#2D only
depHuslerReiss <- function(...) {
  output <- list(
    parameters = c(0.5),
    dep = function(t,pars) {
      if(pars[1]==0) return(1)
      t*pnorm(1/pars[1] + pars[1]/2*log(t/(1-t))) + (1-t)*pnorm(1/pars[1] - pars[1]/2*log(t/(1-t)))
    },
    dep.der = function(t,pars) {
      if(pars[1]==0) return(0)
      pnorm(1/pars[1]+pars[1]/2*log(t/(1-t))) + pnorm(-1/pars[1]+pars[1]/2*log(t/(1-t))) - 1
    },
    dep.der2 = function(t,pars) {
      if(is.finite(t) && (t <= 0 || t >= 1)) return(0)
      exp(-(4+pars[1]^4*log(t/(1-t))^2)/(8*pars[1]^2)) * pars[1] * sqrt(t/(2*pi*(1-t))) / (2*(1-t)*t^2)
    },
    kendall = list(coef=function(t) pPareto(t,c(0.799473,0.25111,0.298499,0)), icoef=function(t) qPareto(t,c(0.799473,0.25111,0.298499,0)), bounds=c(0,1)), 
    spearman = list(coef=function(t) pPareto(t,c(0.877543,0.485643,0.307806,0)), icoef=function(t) qPareto(t,c(0.876443,0.484209,0.307277,0)), bounds=c(0,1)),
    lower = c(0),  #A=1, indep.
    upper = Inf,  	#A=max(t,1-t), perf.pos.dep
    id="Husler-Reiss"
  )
  output[names(list(...))] <- list(...)
  output
}

#general convex combination of dependence functions
#this function creates object (list) to be used in construction of archimax copula
#allows to include also parameters of constituting dependence functions (lined up before the combination parameters)
#the dimension of random vector need to be specified
#combination parameters are ordered variable-wise (i.e. first dim parameters are related to the first dependence function)
#with symmetry=TRUE the function depCC is called.
#requires: vpartition()
depGCC <- function(depfun=list(dep1(),depGumbel()),
                   dparameters=lapply(depfun,function(x) rep(list(NULL),max(1,length(x$parameters)))),
                   dim=2,symmetry=FALSE) {
  if(symmetry) return(depCC(depfun=depfun,dparameters=dparameters,dim=dim))
  dparameters <- lapply(dparameters,unlist)
  nd <- length(depfun)
  A <- lapply(depfun,function(x) x$dep) #extract dependence functions
  Ad <- lapply(depfun,function(x) x$dep.der) 
  Add <- lapply(depfun,function(x) x$dep.der2)
  np <- mapply(function(dep,par) {if(is.null(par)) length(dep$parameters) else 0}, depfun, dparameters) #count depfus' parameters that are to be estimated
  iD <- vpartition(1:sum(np),np); iC <- 1:(nd*dim)+sum(np) #indices of the depfus'(as list) and the combination parameters (as vector)
  dep <- function(t, pars) {
    pD <- mapply(function(static,idynamic) c(static,pars[idynamic]), dparameters, iD, SIMPLIFY=F,USE.NAMES=F) #extract depfus' pars
    pC <- combpars(pars); i0 <- pC[["i0"]]; pC <- pC[["par"]] #treat zero rows/cols, normalize (colsums=1), extract indices of depfus kept
    tpars <- matrix(c(t,1-sum(t)),byrow=T,nrow=nrow(pC[i0, ,drop=F]),ncol=dim) * pC[i0, ,drop=F] #t * pars (normalized and trimed)
    i00 <- as.logical(apply(tpars,1,sum)) # treat the zero rows occurence
    sum(mapply(
      function(PiDF,par,arg) sum(arg)*PiDF(arg[-dim]/sum(arg),par), 
      A[i0][i00], #remove those depfus and their parameters, for which the combination parameters (and also the multiplication with arguments) were zero (zero rows)
      pD[i0][i00], 
      lapply(apply(tpars[i00, ,drop=F],1,list),unlist) #remove zero rows, isolate the remaining rows
      )) 
  }
  dep.der <- function(t, pars) { 
    pD <- mapply(function(static,idynamic) c(static,pars[idynamic]), dparameters, iD, SIMPLIFY=F,USE.NAMES=F) #extract depfus' pars
    pC <- combpars(pars); i0 <- pC[["i0"]]; pC <- pC[["par"]][i0, ,drop=F] #treat zero rows/cols, normalize (colsums=1), extract indices of depfus kept
    tpars <- matrix(c(t,1-sum(t)),byrow=T,nrow=nrow(pC),ncol=dim)*pC
    i00 <- as.logical(apply(tpars,1,sum)) # treat the zero rows occurence
    sum(mapply(
      function(PiDF,PiDFd,par,arg,pc) {(pc[1]-pc[2])*PiDF(arg[1]/sum(arg),par) + pc[1]*pc[2]/sum(arg)*PiDFd(arg[1]/sum(arg),par)}, 
      A[i0][i00], #remove those depfus and their parameters, for which the combination parameters (and also the multiplication with arguments) were zero (zero rows)
      Ad[i0][i00],
      pD[i0][i00], 
      lapply(apply(tpars[i00, ,drop=F],1,list),unlist), #remove zero rows, isolate the remaining rows
      lapply(apply(pC[i00, ,drop=F],1,list),unlist)
    ))    
  }
  dep.der2 <- function(t, pars) { 
    pD <- mapply(function(static,idynamic) c(static,pars[idynamic]), dparameters, iD, SIMPLIFY=F,USE.NAMES=F) #extract depfus' pars
    pC <- combpars(pars); i0 <- pC[["i0"]]; pC <- pC[["par"]][i0, ,drop=F] #treat zero rows/cols, normalize (colsums=1), extract indices of depfus kept
    tpars <- matrix(c(t,1-sum(t)),byrow=T,nrow=nrow(pC),ncol=dim)*pC
    i00 <- as.logical(apply(tpars,1,sum)) # treat the zero rows occurence
    sum(mapply(
      function(PiDFdd,par,arg,pc) (pc[1]*pc[2])^2/sum(arg)^3*PiDFdd(arg[1]/sum(arg),par), 
      Add[i0][i00],#remove those depfus and their parameters, for which the combination parameters (and also the multiplication with arguments) were zero (zero rows)
      pD[i0][i00], 
      lapply(apply(tpars[i00, ,drop=F],1,list),unlist), #remove zero rows, isolate the remaining rows
      lapply(apply(pC[i00, ,drop=F],1,list),unlist)
    ))    
  }
  combpars <- function(pars) { #true combination parameters piled to matrix (#rows = #dep.functions) and indicators of nonzero rows(i0)/columns(j0)
    pC <- matrix(pars[iC],ncol=dim,nrow=nd,byrow=T) #extract combination parameters, pile them to matrix {p_ji} i=1...dim j=1...length(dep)
    if(all(pC==0)) pC <- pC + 1 # if all comb.parameters are 0, change them to 1  
    i0 <- as.logical(apply(pC,1,sum));  # find non-zero rows
    j0 <- as.logical(apply(pC,2,sum)); pC[i0,!j0] <- 1 # find and fill zero columns with a non-zero constant (e.g. 1) = treat zeros as equal weights
    pCn <- apply(pC,2,function(x) x/sum(x)); dim(pCn) <- dim(pC) #normalize the parameters so that column sums = 1, prevent reducing dimension
    list(par=pCn, i0=i0, j0=j0)
  }
  rescalepars <- function(pars,names=TRUE) {
    idep <- 0:(min(iC)-1)
    result <- c(dep=pars[idep],comb=t(combpars(pars)$par))
    if(!isTRUE(names)) names(result) <- NULL
    result
  }
  stpar <- function(lo,up) {
    lo[is.infinite(lo)] <- pmin(up-1,-11)[is.infinite(lo)]
    up[is.infinite(up)] <- pmax(lo+1, 11)[is.infinite(up)]
    (lo+up)/2
  }
  lower <- c(unlist(mapply(function(x,y) x$lower[0:y], depfun, np)),rep.int(0,nd*dim)) 
  upper <- c(unlist(mapply(function(x,y) x$upper[0:y], depfun, np)),rep.int(1,nd*dim))
  parameters <- stpar(lower,upper)
  list(
    parameters=parameters,dep=dep,
    dep.der=if(dim==2) dep.der else NULL, dep.der2=if(dim==2) dep.der2 else NULL,
    lower=lower,upper=upper,combpars=combpars,rescalepars=rescalepars,id="gcc"
  )  
}

#convex sum of dependence functions
#this function creates object (list) to be used in construction of archimax copula
#allows to include also parameters of constituting dependence functions (lined up before the combination parameters)
#the dimension of random vector need to be specified
#requires: vpartition()
depCC <- function(depfun=list(dep1(),depGumbel()),dparameters=lapply(depfun,function(x) rep(list(NULL),max(1,length(x$parameters)))),dim=2) {
  dparameters <- lapply(dparameters,unlist)
  nd <- length(depfun)
  A <- lapply(depfun,function(x) x$dep) #extract dependence functions
  Ad <- lapply(depfun,function(x) x$dep.der) 
  Add <- lapply(depfun,function(x) x$dep.der2)
  np <- mapply(function(dep,par) {if(is.null(par)) length(dep$parameters) else 0}, depfun, dparameters) #count depfus' parameters that are to be estimated
  iD <- vpartition(1:sum(np),np); iC <- 1:(nd)+sum(np) #indices of the depfus'(as list) and the combination parameters (as vector)
  dep <- function(t, pars) {
    pD <- mapply(function(static,idynamic) c(static,pars[idynamic]), dparameters, iD, SIMPLIFY=F,USE.NAMES=F) #extract depfus' pars
    pC <- combpars(pars); i0 <- pC[["i0"]]; pC <- pC[["par"]] #extract, treat zero vector, normalize (sum=1), get indices of nonzero values
    sum(mapply(function(PiDF,par) PiDF(t,par), A[i0], pD[i0]) * pC[i0])
  }
  dep.der <- function(t, pars) { 
    pD <- mapply(function(static,idynamic) c(static,pars[idynamic]), dparameters, iD, SIMPLIFY=F,USE.NAMES=F) #extract depfus' pars
    pC <- combpars(pars); i0 <- pC[["i0"]]; pC <- pC[["par"]] #extract, treat zero vector, normalize (sum=1), get indices of nonzero values
    sum(mapply(function(PiDFd,par) PiDFd(t,par), Ad[i0], pD[i0]) * pC[i0])
  }
  dep.der2 <- function(t, pars) { 
    pD <- mapply(function(static,idynamic) c(static,pars[idynamic]), dparameters, iD, SIMPLIFY=F,USE.NAMES=F) #extract depfus' pars
    pC <- combpars(pars); i0 <- pC[["i0"]]; pC <- pC[["par"]] #extract, treat zero vector, normalize (sum=1), get indices of nonzero values
    sum(mapply(function(PiDFdd,par) PiDFdd(t,par), Add[i0], pD[i0]) * pC[i0])
  }
  combpars <- function(pars) { #true combination parameters
    pC <- pars[iC] #extract combination parameters
    if(all(pC==0)) pC <- pC + 1 # if all comb.parameters are 0, change them to 1
    i0 <- (pC > 0) # find non-zero (and non-negative) values
    list(par=pC/sum(pC),i0=i0)
  }
  rescalepars <- function(pars,names=TRUE) {
    idep <- 0:(min(iC)-1)
    result <- c(dep=pars[idep],comb=t(combpars(pars)$par))
    if(!isTRUE(names)) names(result) <- NULL
    result
  }
  stpar <- function(lo,up) {
    lo[is.infinite(lo)] <- pmin(up-1,-11)[is.infinite(lo)]
    up[is.infinite(up)] <- pmax(lo+1, 11)[is.infinite(up)]
    (lo+up)/2
  }
  lower <- c(unlist(mapply(function(x,y) x$lower[0:y], depfun, np)),rep.int(0,nd)) 
  upper <- c(unlist(mapply(function(x,y) x$upper[0:y], depfun, np)),rep.int(1,nd))
  parameters <- stpar(lower,upper)
  list(
    parameters=parameters,dep=dep,
    dep.der=if(dim==2) dep.der else NULL, dep.der2=if(dim==2) dep.der2 else NULL,
    lower=lower,upper=upper,combpars=combpars,rescalepars=rescalepars,id="cc"
  )  
}

#return dependece function list or (unnamed) list of dependence functions list
depfun <- function(name,...) {
  ldep <- lapply(
    name,
    function(x) switch(x,
                       "1"=dep1(...),
                       "Galambos"=depGalambos(...),
                       "Gumbel"=depGumbel(...),
                       "Husler-Reiss"=depHuslerReiss(...),
                       "max"=depMax(...),
                       "Tawn"=depTawn(...),
                       "gcc"=depGCC(...),
                       "cc"=depCC(...),
                       ({warning("Dependence function not recognized. Defaults to dep1().", immediate.=T); 
                        dep1()})
                       )
    )
  if(length(ldep)==1) ldep[[1]] else ldep
}

#is there a bug when apply(somematrix,1,function(x) x/sum(x)) ??

#lapply/mapply are not able to return correct list of functions (contains only copies of the last function)
#e.g. mapply(function(a,b) c(function(c) a*b+c), list(1,2,3),list(10,100,1000))

#list of 5 dependence functions corresponding to all possible partitions of vector {1,2,3}, where 3 is number of dimensions
ldepPartition3D <- function(power=8) {
  dmax <- if(is.infinite(power)) {function(t) max(t)} else {function(t) sum(t^power)^(1/power)}
  mapply(
    function(x,y) list(parameters=NULL,dep=x,lower=NULL,upper=NULL,id=y), 
    list(
      function(t,pars) 1, #P={{1},{2},{3}}
      function(t,pars) dmax(c(t,1-sum(t))), #P={{1,2,3}}
      function(t,pars) dmax(c(t[-1],1-sum(t)))+t[1], #P={{1},{2,3}}
      function(t,pars) dmax(c(t[-2],1-sum(t)))+t[2], #P={{2},{1,3}}
      function(t,pars) dmax(t)+1-sum(t) #P={{1,2},{3}}
      ),
    list("1","max","max+1","max+2","max+3"),
    SIMPLIFY=FALSE
    )
}

copProduct <- function(...) {
  output <- list(
    parameters = NULL,
    pcopula = function(t, pars) prod(t),
    dcopula = function(t, pars) 1,
    rcopula = function(dim, pars) runif(dim),
    lower = NULL,   
    upper = NULL,
    id="product"
  )
  output[names(list(...))] <- list(...)
  output
}

copGumbel <- function(...) {
  output <- list(
    parameters = c(4),
    pcopula = function(t, pars) exp(-sum((-log(t))^pars[1])^(1/pars[1])),
    kendall = list(coef=function(t) 1-1/t, icoef=function(t) 1/(1-t), bounds=c(0,1)),
    spearman = list(coef=function(t) pPareto(t,c(1.41917,2.14723,1,1)), icoef=function(t) qPareto(t,c(1.41917,2.14723,1,1)), bounds=c(0,1)),
    lower = 1,     #Pi, g(t)=-ln(t)
    upper = Inf,  	#M
    id="Gumbel"
  )
  output[names(list(...))] <- list(...)
  output
}

copFGM <- function(...) {
  output<- list(
    parameters = c(0.5), #if 0 then product copula Pi
    pcopula = function(t, pars) prod(t)*(pars[1]*prod(1-t)+1),
    dcopula = function(t, pars) 1 + pars[1]*(prod(2*t-1)),
    kendall = list(coef=function(t) 2*t/9, icoef=function(t) 9*t/2, bounds=c(-2/9,2/9)),
    spearman = list(coef=function(t) t/3, icoef=function(t) 3*t, bounds=c(-1/3,1/3)),
    lower = -1,  #
    upper = 1,    #
    id="FGM"
  )
  output[names(list(...))] <- list(...)
  output
}

#only 2D
copPlackett <- function(...) {
  output <- list(
    parameters = c(2), #if 1 then product copula Pi
    pcopula = function(t, pars) {
      if(abs(pars[1]-1) < 0.00001) prod(t) 
      else {
        a <- 1+(pars[1]-1)*sum(t)
        (a-sqrt(a^2-4*prod(t)*pars[1]*(pars[1]-1)))/(2*(pars[1]-1))
      }
    },
    dcopula = function(t, pars) {
      if(abs(pars[1]-1) < 0.00001) 1 
      else pars[1]*(1+(sum(t)-2*prod(t))*(pars[1]-1))/((1+(pars[1]-1)*sum(t))^2-4*prod(t)*pars[1]*(pars[1]-1))^(3/2)
    },
    rcopula = function(n, pars) {
      u <- runif(n); v <- runif(n);
      if(abs(pars[1]-1) < 0.00001) return(cbind(u,v))
      a <- v*(1-v) 
      b <- sqrt(pars[1]*(pars[1]+4*a*u*(1-u)*(1-pars[1])^2))
      cbind(u,(2*a*(u*pars[1]^2+1-u)+pars[1]*(1-2*a)-(1-2*v)*b)/(2*pars[1]+2*a*(pars[1]-1)^2))
    },
    kendall = list(coef=function(t) pPareto(t,c(3.24135,0.538913,1.21742,1)), icoef=function(t) qPareto(t,c(3.24135,0.538913,1.21742,1)), bounds=c(0,1)),
    spearman = list(coef=function(t) (t^2-1-2*t*log(t))/(t-1)^2, icoef=NULL, bounds=c(-1,1)),
    lower = 1e-05,  #
    upper = Inf,    #
    id="Plackett"
  )
  output[names(list(...))] <- list(...)
  output
}

copNormal <- function(dim=2,...) {
  parvec2matrix <- function(parvec) {
    if(dim != (1+sqrt(8*length(parvec)+1))/2) stop("Dimension does not correspond to number of parameters") 
    m <- matrix(0,dim,dim)
    m[lower.tri(m)] <- parvec #fill the lower triangle (without diagonal) with parameters 
    m+t(m)+diag(1,dim) #mirror the lower triangle and add 1's
  }
  if(isTRUE(requireNamespace("mvtnorm"))) {
    pcopula <- function(t,pars) mvtnorm::pmvnorm(lower=-Inf, upper=sapply(t,qnorm), corr=parvec2matrix(pars))[1]
    rcopula <- function(n,pars) pnorm(mvtnorm::rmvnorm(n=n,sigma=parvec2matrix(pars)))
  }
  else {
    pcopula <- NULL
    rcopula <- function(n,pars) t(pnorm(t(chol(parvec2matrix(pars)))%*%replicate(n,rnorm(dim))))
  }
  npar <- factorial(dim)/factorial(dim-2)/2 #variacia bez opakovania / 2
  output <- list(
    parameters = rep.int(0.5,npar), #if 0 then product copula Pi
    pcopula = pcopula,
    dcopula = function(t, pars) {
      sig <- parvec2matrix(pars)
      v <- sapply(t, qnorm)
      exp(-0.5*t(v)%*%(solve(sig)-diag(1,length(t)))%*%v)/sqrt(det(sig))
    },
    rcopula = rcopula,
    kendall = list(coef=function(t) 2*asin(t)/pi, icoef=function(t) sin(t*pi/2), bounds=c(-1,1)),
    spearman = list(coef=function(t) 6*asin(t/2)/pi, icoef=function(t) 2*sin(t*pi/6), bounds=c(-1,1)),
    lower = rep.int(-0.999,npar),  #W (by limit)
    upper = rep.int(0.999,npar),  #M (by limit)
    id="normal"
  )
  output[names(list(...))] <- list(...)
  output
}

copula <- function(name,...) {
  switch(name[1],
         "FGM"=copFGM(...),
         "Gumbel"=copGumbel(...),
         "normal"=copNormal(...),
         "Plackett"=copPlackett(...),
         "product"=copProduct(...),
         ({warning("Copula not recognized. Defaults to genLog().", immediate.=T);
           copProduct(...)})
  )
}

#cumulative distribution function (or probability dis.fun., that's why "p")
pCopula <- function(data, generator=genGumbel(), depfun=dep1(), copula=NULL,
                    gpars=generator$parameters, dpars=depfun$parameters, pars=if(is.null(copula)) list(gpars,dpars) else copula$parameters, 
                    subdivisions=50,
                    quantile=NULL,probability=data[,quantile]) {
  data <- rbind(data, deparse.level=0 ) #ensure data is matrix/data.frame
  data[data>1] <- 1; data[data<0] <- 0 #crop data to [0,1]
  names(data) <- NULL
  dim <- ncol(data)
  eps <- .Machine$double.eps^0.5
  # --- cdf definition ---
  #Archimax copula
  if(is.null(copula)) { 
    if(!is.null(generator$pcopula) && depfun$id=="1") fun <- function(t) generator$pcopula(t,pars[[1]]) 
    else if(!is.null(depfun$pcopula) && generator$id=="log") fun <- function(t) depfun$pcopula(t,pars[[2]])
    else {
      g <- function(t) generator$gen(t, pars[[1]]) #generator
      gi <- function(t) generator$gen.inv(t, pars[[1]])
      A <- function(t) depfun$dep(t, pars[[2]]) #Pickands depedence function
      Aeq1 <- (abs(A(rep.int(1,dim-1)/dim)-1) < eps)
      fun <- function(t) {
        names(t) <- NULL
        #t <- pmin(pmax(t,0),1)
        #if( Aeq1 ) return(gi(sum(sapply(t,g)))) #reduce to archimedean
        if( abs(prod(t)-0) < eps ) return(0) #0 is annihilator
        if( abs(prod(t)-1) < eps ) return(1) #C(1,...,1)=1
        gen <- sapply(t,g)
        #if(any(!is.finite(gen))) browser()
        arg <- gen/(sum(gen)+eps) #prevent an accidental overflow caused by rounding at the last decimal place
        #if(!is.finite(A(arg[-dim]))) browser()
        if(any(arg > 1-eps, Aeq1)) cdf <- gi( sum(gen) )  #reduce to archimedean
        else cdf <- gi( sum(gen) * A(arg[-dim]) )
        #if(any(t > 1)) browser()
        if(!is.finite(cdf)) {
          #print(c(t=t,gpar=pars[[1]],dpar=pars[[2]],generator=gen,A=A(gen[-dim]/sum(gen)),cdf=cdf))
          return(0)
        }
        cdf
      }
    }
  }
  #arbitrary copula
  else { 
    pars <- unlist(pars) #ensure pars is a vector instead of a list    
    if(!is.null(copula$pcopula)) {
      fun <- function(t) {
        if( abs(prod(t)-0) < eps ) return(0) #0 is annihilator
        cdf <- copula$pcopula(t,pars)
        cdf
      }      
    }    
    else {
      pdf <- function(t) copula$dcopula(t,pars)
      if(!is.finite(pdf(lower <- rep.int(0,dim)))) lower <- 1e-4
      fun <- function(t) nintegrate(pdf,lower=lower,upper=t,subdivisions=subdivisions)    
    }
  }
  # --- evaluation ---
  # quantile (if asked for)
  if(!is.null(quantile)) { #to improve: treat explicitely if only copula density is available
    qua <- quantile[1]
    if(quantile > dim) stop("quantile index > copula dimension")
    data[,qua] <- rep(probability,length.out=nrow(data))
    if(any(data[,qua] > apply(data[,-qua,drop=F],1,min))) stop("probability > data")
    qcop <- function(v) { #v contains probability in place of the wanted quantile (qua-th element)
      fun1 <- function(t) {p <- v[qua]; v[qua] <- t; fun(v) - p}
      uniroot(fun1, interval=c(0,1))$root
    }
    apply(data,1,qcop)
  }
  #copula value (by default)
  else apply(data,1,fun)
}

# copula density, the function checks for closed form availability
dCopula <- function(data,generator=genGumbel(),depfun=dep1(),copula=NULL,
                    gpars=generator$parameters, dpars=depfun$parameters,
                    pars=if(is.null(copula)) list(gpars,dpars) else copula$parameters,
                    difference=1e-4,area=c(0),shrinkdiff=FALSE) {
  data <- rbind(data, deparse.level=0 ) #ensure data is matrix/data.frame
  # --- Archimax copula ---
  if(is.null(copula)) {
    if(!is.null(generator$dcopula) && depfun$id=="1") fun <- function(t) generator$dcopula(t,pars[[1]]) 
    else if(!is.null(depfun$dcopula) && generator$id=="log") fun <- function(t) depfun$dcopula(t,pars[[2]])
    else if(ncol(data)==2) {
      g <- function(t) generator$gen(t, pars[[1]])
      gd <- function(t) generator$gen.der(t, pars[[1]])
      gid <- function(t) generator$gen.inv.der(t, pars[[1]])
      gidd <- function(t) generator$gen.inv.der2(t, pars[[1]])
      A <- function(t) depfun$dep(t, pars[[2]])
      Ad <- function(t) depfun$dep.der(t, pars[[2]])
      Add <- function(t) depfun$dep.der2(t, pars[[2]])
      if( abs(A(0.5)-1) < 1e-5 )  fun <- function(t) gd(t[1])*gidd(g(t[1])+g(t[2]))*gd(t[2])
      else 
        fun <- function(t) {
          g1 <- g(t[1]); g2 <- g(t[2]); arg <- g1/(g1+g2)
          gd(t[1]) * (
            gidd((g1+g2)*A(arg))*(A(arg)-arg*Ad(arg))*(A(arg)+(1-arg)*Ad(arg)) - arg*(1-arg)*gid((g1+g2)*A(arg))*Add(arg)/(g1+g2)
            ) * gd(t[2])
        }
    }
    else {
      cdf <- function(t) pCopula(rbind(t),generator=generator,depfun=depfun,pars=pars,gpars=gpars,dpars=dpars)
      if((k=min(1-max(data),min(data))) <= difference/(abs(area)+1) && shrinkdiff) {
        difference=k*9/10 #treat leaking of numeric derivative over [0,1] 
        warning("dCopula: Difference in nderive decreased due to [0,1] overflow.",call.=FALSE)
      }
      fun <- function(t) nderive(cdf,point=t,order=rep.int(1,length(t)),difference=difference,area=area)
    }
  }
  # --- arbitrary copula ---
  else { 
    pars <- unlist(pars)
    if(!is.null(copula$dcopula)) fun <- function(t) copula$dcopula(t,pars) #take explicit formula for density if it exists
    else {
      cdf <- function(t) copula$pcopula(t,pars)
      if((min(1-max(data),min(data))) <= difference/(abs(area)+1)) {
        cdf <- function(t) copula$pcopula(pmin.int(pmax.int(t,0),1),pars) #collapse surroundings to boundary (instead of shrinking difference) #test and use above if good
      }
      fun <- function(t) nderive(cdf,point=t,order=rep.int(1,length(t)),difference=difference,area=area)
    }
  }
  # --- evaluation ---
  apply(data,1,fun)
}

cCopula <- function(data, conditional.on=c(1), generator=genGumbel(), depfun=dep1(), copula=NULL,
                    gpars=generator$parameters, dpars=depfun$parameters, pars=if(is.null(copula)) list(gpars,dpars) else copula$parameters,
                    difference=1e-4,area=c(0),
                    quantile=NULL,probability=data[,quantile]) {
  data <- rbind(data, deparse.level=0 ) #ensure data is matrix/data.frame
  dim <- ncol(data)
  if(max(conditional.on)>dim) stop("conditional.on > ncol(data)")
  con <- conditional.on
  eps <- .Machine$double.eps^0.5
  # --- Archimax copula ---
  if(is.null(copula)) {
    if(dim==2) {
      g <- function(t) generator$gen(t, pars[[1]])
      gd <- function(t) generator$gen.der(t, pars[[1]])
      gid <- function(t) generator$gen.inv.der(t, pars[[1]])
      A <- function(t) depfun$dep(t, pars[[2]])
      Ad <- function(t) depfun$dep.der(t, pars[[2]])
      ccop <- function(v) { #t is unknown quantile, v is the rest of arguments (with probability)
        if( min(abs(v)) < eps ) return(0) #0 is annihilator, prevent unboudedness of strict generator
        g1 <- g(v[1]); g2 <- g(v[2]); arg <- g1/(g1+g2)
        gid((g1+g2)*A(arg)) * gd((2-con)*v[1]+(con-1)*v[2]) * (A(arg)+Ad(arg)*(con-1-arg))          
      }
    }
    else {
      cdf <- function(t) pCopula(t,generator=generator,depfun=depfun,pars=pars,gpars=gpars,dpars=dpars)
      if((min(1-max(data),min(data))) <= difference/(abs(area)+1)) 
        cdf <- function(t) pCopula(pmin.int(pmax.int(t,0),1),generator=generator,depfun=depfun,pars=pars,gpars=gpars,dpars=dpars) #collapse surroundings to boundary (instead of shrinking difference) #test and use above if good
      ord <- rep.int(0,dim); ord[con] <- 1 #1 marks variable w.r.t. which the function will be differentiated
      dcop <- function(v) nderive(cdf,point=v,order=ord,difference=difference,area=area)
      ccop <- function(v) dcop(v)/local({v[setdiff(1:dim,con)] <- 1; dcop(v)})
    }
  }
  # --- arbitrary copula ---
  else { #to tidy up: if arbitrary copula will not contain dim=2 special case, join the 2<dimensional ccop-s with Archimax case
    pars <- unlist(pars)
    cdf <- function(t) copula$pcopula(t,pars)
    if((min(1-max(data),min(data))) <= difference/(abs(area)+1)) {
      cdf <- function(t) copula$pcopula(pmin.int(pmax.int(t,0),1),pars) #collapse surroundings to boundary (instead of shrinking difference) #test and use above if good
    }
    ord <- rep.int(0,dim); ord[con] <- 1 #1 marks variable w.r.t. which the function will be differentiated
    dcop <- function(v) nderive(cdf,point=v,order=ord,difference=difference,area=area)
    ccop <- function(v) dcop(v)/local({v[setdiff(1:dim,con)] <- 1; dcop(v)})
  }
  # --- evaluation ---
  if(!is.null(quantile)) {
    qua <- quantile
    if(qua %in% con) stop("quantile must NOT be an element of conditional.on")
    if(quantile > dim) stop("quantile index > copula dimension")
    data[,qua] <- rep(probability,length.out=nrow(data))
    qcop <- function(v) { #v contains probability in place of the wanted quantile (qua-th element)
      fun <- function(t) {p <- v[qua]; v[qua] <- t; ccop(v) - p}
      uniroot(fun, interval=c(0,1))$root
    }
    apply(data,1,qcop)
  }
  else apply(data,1,ccop)
}

#quantile of a copula (un/conditional) distribution function
qCopula <- function(data, quantile=1, probability=0.95, conditional.on=NULL, 
                    generator=genGumbel(), depfun=dep1(), copula=NULL, 
                    gpars=generator$parameters, dpars=depfun$parameters, pars=if(is.null(copula)) list(gpars,dpars) else copula$parameters,
                    difference=1e-4, area=c(0)) {
  data <- rbind(data, deparse.level=0 ) #ensure data is matrix/data.frame
  dim <- ncol(data) + 1
  qua <- quantile
  data <- cbind(data[,0:(qua-1)],probability,data[,if(qua==dim) 0 else qua:(dim-1)],deparse.level=0)
  if(is.null(conditional.on)) pCopula(data=data,quantile=quantile,generator=generator,depfun=depfun,copula=copula,pars=pars,gpars=gpars,dpars=dpars)
  else cCopula(data=data,conditional.on=conditional.on,quantile=quantile,generator=generator,depfun=depfun,copula=copula,pars=pars,gpars=gpars,dpars=dpars,difference=difference,area=area)
}

# - check the correctness of LS-nls, while LS-optim(nlminb) completely fails; 
# - allow to enter initial values for optimisation routines!!
# - do not use 'fnscale' in 'control' of 'optim'
# - to improve: print summary

eCopula <- function(data, generator=genGumbel(), depfun=dep1(), copula=NULL,
                    glimits=list(generator$lower,generator$upper), 
                    dlimits=list(depfun$lower,depfun$upper),
                    limits=list(copula$lower,copula$upper),
                    ggridparameters=if(!is.null(unlist(glimits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), glimits) else NULL, 
                    dgridparameters=if(!is.null(unlist(dlimits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), dlimits) else NULL, 
                    gridparameters=if(!is.null(unlist(limits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), limits) else NULL,
                    technique=c("ML","LS","icorr"), procedure=c("optim","nlminb","nls","grid"), method="default", corrtype=c("kendall","spearman"), 
                    control=NULL, pgrid=10) {
  if(is.null(copula)) { 
    eCopulaArchimax(data=data,generator=generator,depfun=depfun,glimits=glimits,dlimits=dlimits,ggridparameters=ggridparameters,dgridparameters=dgridparameters,technique=technique,procedure=procedure,method=method,control=control,pgrid=pgrid,corrtype=corrtype)
  }
  else {
    eCopulaGeneric(data=data,copula=copula,limits=limits,gridparameters=gridparameters,technique=technique,procedure=procedure,method=method,control=control,pgrid=pgrid,corrtype=corrtype)
  }
}

## fitting archimax  
eCopulaArchimax <- function(data, generator, depfun=dep1(),
                            glimits=list(generator$lower,generator$upper), 
                            dlimits=list(depfun$lower,depfun$upper),
                            ggridparameters=if(!is.null(unlist(glimits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), glimits) else NULL, 
                            dgridparameters=if(!is.null(unlist(dlimits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), dlimits) else NULL, 
                            technique=c("ML","LS","icorr"), procedure=c("optim","nlminb","nls","grid"), method="default", corrtype=c("kendall","spearman"), 
                            control=NULL, pgrid=10) {
  #initial (local) variables
  npg <- length(generator$parameters); npd <- length(depfun$parameters)  #number of gen and dep parameters
  ig <- min(1,npg):npg; id <- {if(npd < 1) 0 else (1:npd)+npg}   #indices of generator and depfun parameters
  ind <- apply(data==1,1,prod) + apply(data==0,1,prod) # indices for removing unit and zero rows in data
  if(technique[1]=="LS") empC <- pCopulaEmpirical(data)[!ind]
  data <- data[!ind,]  
  splitad <- function(pars) list(gpars=pars[ig],dpars=pars[id])	#separate gen/dep parameters
  #temporarily simple procedure inserted  to provide basic functionality of "icorr" technique
  if(technique[1]=="icorr") { 
    if(npg+npd>1) stop("Technique icorr can currently estimate only 1-parameter copula families.")
    if(depfun$id=="1") copula <- generator
    else copula <- depfun
    if(is.null(corrlist <- copula[[corrtype[1]]])) stop("No relation of correlation coefficient to copula parameter defined.")
    coef <- cor(data,method=corrtype)[1,2]
    if((coef > corrlist$bounds[2]) || (coef < corrlist$bounds[1])) stop("Dependence measure out of bounds")
    if(is.null(corrlist$icoef)) {
      lim <- unlist(c(glimits,dlimits)); lim <- replace(lim,lim==Inf,1000); lim <- replace(lim,lim==-Inf,-1000)
      parestim <- uniroot(function(t) corrlist$coef(t)-coef,interval=lim)$root 
    }
    else parestim <- corrlist$icoef(coef)
    output <- list(parameters=splitad(parestim),approach=c(technique[1]),fvalue=NULL,procedure.output=NULL)
    class(output) <- "eCopulaArchimax"
    return(output)
  }
  #switch to fitting technique
  switch(technique[1],
         ML={
           fun <- function(pars) {
             pars <- comboe(pars)
             #truncate by "almost zero" to prevent negative values occured when numerical derivatives are used
             copuladensity <- pmax(dCopula(data, generator=generator, depfun=depfun, pars=list(pars[ig],pars[id])), exp(-50)) 
             sum(log(copuladensity))
           }
           fscale <- -1
         },
         LS={
           fun <- function(pars) {
             pars <- comboe(pars)
             sum((pCopula(data, generator=generator, depfun=depfun, pars=list(pars[ig],pars[id])) - empC)^2)
           }
           fscale <- +1
           funNLS <- function(u,pars) {
             pars <- comboe(pars)
             pCopula(u, generator=generator, depfun=depfun, pars=list(pars[ig],pars[id]))
           }
         }
  )
  #decision tree of grid estimation option 
  if(procedure[1]=="grid") {	
    #preparing parameters
    makeseq <- function(x) {	#make sequence if any argument for seq() is recognized  
      if(sum(match(names(x),c("from","to","by","length.out","along.with"),nomatch =0)) > 0) {
        x <- replace(x,x==Inf,100); x <- replace(x,x==-Inf,-100)
        return(unique(do.call(seq,as.list(x))))
      }
      else return(x)
    }
    ggridparameters <- lapply(ggridparameters,makeseq); dgridparameters <- lapply(dgridparameters,makeseq)
    ggridparameters <- mapply(function(x,y,z) x[x>=y & x<=z], ggridparameters, generator$lower, generator$upper, SIMPLIFY=FALSE)
    dgridparameters <- mapply(function(x,y,z) x[x>=y & x<=z], dgridparameters, depfun$lower, depfun$upper, SIMPLIFY=FALSE)
    parsgrid <- as.matrix(expand.grid(c(ggridparameters,dgridparameters), KEEP.OUT.ATTRS = FALSE))
    dimnames(parsgrid) <- NULL	#prevent apply from returning infinite values
    comboe <- identity #just for compatibility with (unconditional) optimization procedures
    #evaluation 
    ind <- order(fvalue <- fscale*apply(parsgrid,1,fun))
    res <- list(
      parestim=unlist(parsgrid[ind[1],], use.names = FALSE),
      fvalue=fscale*fvalue[ind[1]],
      complete=list(
        parsgrid=t(parsgrid),
        fvalue=fscale*fvalue,
        relfvalues=(max(fvalue)-fvalue)/(max(fvalue)-min(fvalue))
      )
    )
  }		
  else {
    #immediate exceptions
    if(technique[1]=="ML" && procedure[1]=="nls" ) stop("ML and nls cannot be combined")
    st <- c(generator$parameters, depfun$parameters)     #starting values (to improve: should be optional)
    lo <- c(glimits[[1]], dlimits[[1]]); up <- c(glimits[[2]], dlimits[[2]])
    if( npg+npd != length(lo) || npg+npd != length(up)) stop("differing number of parameters")
    ind <- st < lo;   st[ind] <- lo[ind]
    ipo <- (up - lo) <= 0; ipog <- ipo[ig]; ipod <- ipo[id]  #T/F index of parameters omited in estimation
    npe <- sum(!ipo)			#number of parameters that will be estimated
    ste <- st[!ipo]; loe <- lo[!ipo]; upe <- up[!ipo]   #starting and bounding values of estimated parameters
    comboe <- function(pars) {                               #combine omited and estimated parameters
      parst <- numeric(npg+npd); parst[ipo] <- lo[ipo]; parst[!ipo] <- pars; parst
    }
    #switch to fitting procedure
    switch(procedure[1],
           optim={
             res <- optim( par=st[!ipo], fn=fun, lower=lo[!ipo], upper=up[!ipo], 
                           method=ifelse(method=="default","L-BFGS-B",method), control=c(list(fnscale=fscale,factr=1e12),control) 
             )
             res <- list(parestim=res$par,fvalue=res$value,procedure.output=res)
           },
           nlminb={
             fun1 <- function(pars) fscale*fun(pars)        #check the "port" help to circumvent this line
             res <- nlminb( start=st[!ipo], objective=fun1, lower=lo[!ipo], upper=up[!ipo],control=c(list(),control));
             res <- list(parestim=res$par,fvalue=fscale*res$objective,procedure.output=res)
           },
           nls={
             nlsmethod <- ifelse(method=="default","port",method) 
             if(ncol(data)!=3) stop("nls currently available only for 3D") #tip for improvement: handle formula separately in advance
             emp <- as.data.frame(data); colnames(emp) <- c('u1','u2','u3'); emp <-cbind(empC,emp);
             res <- switch(npe,
                           nls(C ~ funNLS(c(u1, u2,u3), c(par1)),
                               data = data,
                               start = list(par1 = ste[1]),
                               lower = list(par1 = loe[1]),
                               upper = list(par1 = upe[1]),
                               algorithm = nlsmethod
                           ),
                           nls(C ~ funNLS(c(u1, u2,u3), c(par1,par2)),
                               data = emp,
                               start = list(par1 = ste[1],par2 = ste[2]),
                               lower = list(par1 = loe[1],par2 = loe[2]),
                               upper = list(par1 = upe[1],par2 = upe[2]),
                               algorithm = nlsmethod
                           ),
                           nls(C ~ funNLS(c(u1, u2,u3), c(par1,par2,par3)),
                               data = emp,
                               start = list(par1 = ste[1],par2 = ste[2],par3 = ste[3]),
                               lower = list(par1 = loe[1],par2 = loe[2],par3 = loe[3]),
                               upper = list(par1 = upe[1],par2 = upe[2],par3 = upe[3]),
                               algorithm = nlsmethod
                           ),
                           nls(C ~ funNLS(c(u1, u2,u3), c(par1,par2,par3,par4)),
                               data = emp,
                               start = list(par1 = ste[1],par2 = ste[2],par3 = ste[3],par4 = ste[4]),
                               lower = list(par1 = loe[1],par2 = loe[2],par3 = loe[3],par4 = loe[4]),
                               upper = list(par1 = upe[1],par2 = upe[2],par3 = upe[3],par4 = upe[4]),
                               algorithm = nlsmethod
                           ),
                           nls(C ~ funNLS(c(u1, u2,u3), c(par1,par2,par3,par4,par5)),
                               data = emp,
                               start = list(par1 = ste[1],par2 = ste[2],par3 = ste[3],par4 = ste[4],par5 = ste[5]),
                               lower = list(par1 = loe[1],par2 = loe[2],par3 = loe[3],par4 = loe[4],par5 = loe[5]),
                               upper = list(par1 = upe[1],par2 = upe[2],par3 = upe[3],par4 = upe[4],par5 = upe[5]),
                               algorithm = nlsmethod
                           )
             )
             res <- list(parestim=as.vector(coef(res)),fvalue=sum(residuals(res)^2),procedure.output=res)
           }
    )
  }
  parestim <- splitad(comboe(res$parestim))
  if("rescalepars" %in% names(depfun)) parestim$dpars <- depfun$rescalepars(parestim$dpars) # rescale to true parameters of depfun (if appliable)
  output <- list(parameters=parestim,approach=c(technique[1],procedure[1],method[1]),fvalue=res$fvalue,procedure.output=res)
  class(output) <- "eCopulaArchimax"
  output
}

## fitting generic copula
eCopulaGeneric <- function(data, copula=copGumbel(),
                           limits=list(copula$lower,copula$upper),
                           gridparameters=if(!is.null(unlist(limits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), limits) else NULL,
                           technique=c("ML","LS","icorr"), procedure=c("optim","nlminb","grid"), method="default", corrtype=c("kendall","spearman"), 
                           control=NULL, pgrid=10) {
  #initial (local) variables
  np <- length(copula$parameters)  #number copula parameters
  ind <- apply(data==1,1,prod) + apply(data==0,1,prod) # indices for removing unit and zero rows in data
  if(technique[1]=="LS") empC <- pCopulaEmpirical(data)[!ind]
  data <- data[!ind,]
  #temporarily simple procedure inserted  to provide basic functionality of "icorr" technique
  if(technique[1]=="icorr") { 
    if(np>1) stop("Technique icorr can currently estimate only 1-parameter copula families.")
    if(is.null(corrlist <- copula[[corrtype[1]]])) stop("No relation of correlation coefficient to copula parameter defined.")
    coef <- cor(data,method=corrtype)[1,2]
    if((coef > corrlist$bounds[2]) || (coef < corrlist$bounds[1])) stop("Dependence measure out of bounds")
    if(is.null(corrlist$icoef)) {
      lim <- unlist(limits); lim <- replace(lim,lim==Inf,1000); lim <- replace(lim,lim==-Inf,-1000)
      parestim <- uniroot(function(t) corrlist$coef(t)-coef,interval=lim)$root 
    }
    else parestim <- corrlist$icoef(coef)
    output <- list(parameters=parestim,approach=c(technique[1]),fvalue=NULL,procedure.output=NULL)
    class(output) <- "eCopulaGeneric"
    return(output)
  }
  #switch to fitting technique
  switch(technique[1],
         ML={
           fun <- function(pars) {
             pars <- comboe(pars)
             #truncate by "almost zero" to prevent negative values occured when numerical derivatives are used
             copuladensity <- pmax(dCopula(data, copula=copula, pars=pars), exp(-50)) 
             sum(log(copuladensity))
           }
           fscale <- -1
           
         },
         LS={
           fun <- function(pars) {
             pars <- comboe(pars)
             sum((pCopula(data,  copula=copula, pars=pars) - empC)^2)
           }
           fscale <- +1
         }
  )
  #decision tree of grid estimation option 
  if(procedure[1]=="grid") {	
    #preparing parameters
    makeseq <- function(x) {	#make sequence if any argument for seq() is recognized  
      if(sum(match(names(x),c("from","to","by","length.out","along.with"),nomatch =0)) > 0) {
        x <- replace(x,x==Inf,100); x <- replace(x,x==-Inf,-100)
        return(unique(do.call(seq,as.list(x))))
      }
      else return(x)
    }
    gridparameters <- lapply(gridparameters,makeseq)
    gridparameters <- mapply(function(x,y,z) x[x>=y & x<=z], gridparameters, generator$lower, generator$upper, SIMPLIFY=FALSE)
    parsgrid <- as.matrix(expand.grid(gridparameters, KEEP.OUT.ATTRS = FALSE))
    dimnames(parsgrid) <- NULL	#prevent apply from returning infinite values
    comboe <- identity #just for compatibility with (unconditional) optimization procedures
    #evaluation 
    ind <- order(fvalue <- fscale*apply(parsgrid,1,fun))
    res <- list(
      parestim=unlist(parsgrid[ind[1],], use.names = FALSE),
      fvalue=fscale*fvalue[ind[1]],
      complete=list(
        parsgrid=t(parsgrid),
        fvalue=fscale*fvalue,
        relfvalues=(max(fvalue)-fvalue)/(max(fvalue)-min(fvalue))
      )
    )
  }		
  else {
    #immediate exceptions
    st <- copula$parameters     #starting values (to improve: should be optional)
    lo <- limits[[1]]; up <- limits[[2]]
    if( np != length(lo) || np != length(up)) stop("Differing number of parameters.")
    ind <- st < lo;   st[ind] <- lo[ind]
    ipo <- (up - lo) <= 0  #T/F index of parameters omited in estimation
    npe <- sum(!ipo)			#number of parameters that will be estimated
    ste <- st[!ipo]; loe <- lo[!ipo]; upe <- up[!ipo]   #starting and bounding values of estimated parameters
    comboe <- function(pars) {                          #combine omited and estimated parameters
      parst <- numeric(np); parst[ipo] <- lo[ipo]; parst[!ipo] <- pars; parst
    }
    #switch to fitting procedure
    switch(procedure[1],
           optim={
             method <- ifelse(method=="default","L-BFGS-B",method)
             if(method %in% c("Nelder-Mead", "BFGS", "CG", "SANN", "Brent")) { #for these optim methods limit the parameters range implicitely
               fun <- function(pars) {
                 parscut <- pmin.int(pmax.int(pars,loe),upe)
                 if(any(parscut != pars)) hndcp <- (1+sum(abs(pars-parscut)))^fscale else hndcp <- 1 #handicap out-of-bounds parameters
                 fun(parscut)*hndcp                 
               }
               res <- optim( par=ste, fn=fun, method=method, control=c(list(fnscale=fscale,factr=1e12),control) )
             }
             else { #if "L-BFGS-B" method is used, fun arguments will not surpass the limits during optimization
               res <- optim( par=ste, fn=fun, lower=loe, upper=upe, method=method, control=c(list(fnscale=fscale,factr=1e12),control) )
             }
             res <- list(parestim=res$par,fvalue=res$value,procedure.output=res) #compose output
           },
           nlminb={
             fun1 <- function(pars) fscale*fun(pars)        #consult "port" help to circumvent this line
             res <- nlminb( start=ste, objective=fun1, lower=loe, upper=upe,control=c(list(),control));
             res <- list(parestim=res$par,fvalue=fscale*res$objective,procedure.output=res)
           }
    )
  }
  parestim <- comboe(res$parestim)
  if("rescalepars" %in% names(copula)) parestim <- copula$rescalepars(parestim) # rescale to true parameters of copula (if appliable)
  output <- list(parameters=parestim,approach=c(technique[1],procedure[1],method[1]),fvalue=res$fvalue,procedure.output=res)
  class(output) <- "eCopulaGeneric"
  output
}


#methods for printing eCopula results list
print.eCopulaArchimax <- function(x,...) {
  cat("generator parameters: ", x$parameters$gpars, "\n")
  cat("   depfun parameters: ", x$parameters$dpars, "\n")
  cat("  ", x$approach[1], "function value: ", x$fvalue, "\n")
  cat("    convergence code: ", x$procedure.output$procedure.output$convergence, "\n")
}
print.eCopulaGeneric <- function(x,...) {
  cat("   copula parameters: ", x$parameters, "\n")
  cat("  ", x$approach[1], "function value: ", x$fvalue, "\n")
  cat("    convergence code: ", x$procedure.output$procedure.output$convergence, "\n")
}

#simulation of dim-dimensional random vector from archimax copula
#note the thinned runif range due to nderive 'difference' settings
rCopula <- function(n,dim=2,generator=genGumbel(),depfun=dep1(),copula=NULL,
                    gpars=generator$parameters, dpars=depfun$parameters, pars=if(is.null(copula)) list(gpars,dpars) else copula$parameters) {
  if(dim==2 && is.null(copula)) return( rCopulaArchimax2D(n,generator=generator,depfun=depfun,pars=pars) )
  if(!is.null(copula$rcopula)) return( copula$rcopula(n,pars) )
  cdf <- function(u) pCopula(u,generator=generator,depfun=depfun,copula=copula,pars=pars)
  mcop <- function(u) cdf(rbind(c(u,rep.int(1,dim-length(u)))))
  NDdiff <- 1e-4
  ccop <- function(v,u) nderive(fun=mcop,point=c(u,v),order=c(rep.int(1,length(u)),0),difference=NDdiff)/nderive(fun=mcop,point=u,order=rep.int(1,length(u)),difference=NDdiff)
  qcop <- function(p,u) uniroot(function(t) ccop(t,u) - p, interval=c(0,1))$root 
  p <- matrix(runif(dim*n,min=0+1.1*NDdiff,max=1-1.1*NDdiff),ncol=dim) #thin the runif range using NDdiff to avoid overflow in nderive
  result <- NULL
  for(i in 1:n) {
    x <- p[i,1]
    for(j in 2:dim) x <- c(x, qcop(p[i,j],x))
    result <- rbind(result,x,deparse.level=0)
  }
  result
}

# simulation of 2D copula
rCopulaArchimax2D <- function(n, generator=genLog(), depfun=dep1(),
                              gpars=generator$parameters, dpars=depfun$parameters, pars=list(gpars,dpars)) {
  g <- function(t) generator$gen(t, pars[[1]])
  gd <- function(t) generator$gen.der(t, pars[[1]])
  gi <- function(t) generator$gen.inv(t, pars[[1]])
  A <- function(t) depfun$dep(t, pars[[2]])
  Ad <- function(t) depfun$dep.der(t, pars[[2]])
  Add <- function(t) depfun$dep.der2(t, pars[[2]])
  K <- function(t) t - ifelse(t>0 && t<1, g(t)/gd(t), 0)
  Ki <- function(t) uniroot(f = function(x) K(x) - t, interval=c(0,1))$root 
  H <- function(t) t + ifelse(t>0 && t<1, t*(1-t)*Ad(t)/A(t), 0)
  Hi <- function(t) uniroot(f = function(x) H(x) - t, interval=c(0,1))$root 
  rCA <- function(n) {                       #simulation from Archimedean copula
    s <- runif(n); w <- runif(n)
    w <- sapply(w,Ki); gw <- sapply(w,g)
    cbind(u=sapply(s*gw,gi),v=sapply((1-s)*gw,gi))
  }
  Aeq1 <- isTRUE(all.equal(A(0.5),1))
  if( Aeq1 )  return( rCA(n) )
  p <- function(t) {
    A <- A(t); Ad <- Ad(t); Add <- Add(t); D <- Ad/A; Dd <- (Add*A - Ad^2)/A^2
    h <- 1 + (1-2*t)*D + t*(1-t)*Dd
    t*(1-t)*Add/(h*A)
  }
  z <- sapply(runif(n),Hi); u <- runif(n)
  ind <- (u <= sapply(z,p))
  w <- numeric(n); w[ind] <- runif(sum(ind)); w[!ind] <- sapply(runif(n-sum(ind)),Ki)
  gA <- sapply(w,g)/sapply(z,A)
  cbind(u=sapply(z*gA,gi), v=sapply((1-z)*gA,gi))
}


#Blanket goodness-of-fit test 
#require library(parallel) iff ncores > 1 (functionality not appliable on Windows OS)
#Example: gCopula(u123[,1:2],generator=genGumbel,dep=dep1,N=10,nc=1,m=400)
gCopula <- function(data, generator, depfun=dep1(), copula=NULL,
                    glimits=list(generator$lower,generator$upper), 
                    dlimits=list(depfun$lower,depfun$upper),
                    limits=list(copula$lower,copula$upper),
                    etechnique=c("ML","LS","icorr"), eprocedure=c("optim","nlminb","nls"), emethod="default", ecorrtype=c("kendall","spearman"), econtrol=NULL,
                    N=100, m=nrow(data), ncores=1) {  
  if(is.list(data)) return(gCopulaEmpirical(data=data,N=N,ncores=ncores))
  n <- nrow(data)
  dim <- ncol(data)
  Ein <- pCopulaEmpirical(data)
  fKn <- function(x,y) sum(y <= x)/length(y)
  fparest <- function(data) eCopula(data,generator=generator,depfun=depfun,copula=copula,glimits=glimits,dlimits=dlimits,limits=limits,
                                    technique=etechnique,procedure=eprocedure,method=emethod,corrtype=ecorrtype,control=econtrol)    
  fsimcop <- function(parameters) rCopula(m,dim=dim,generator=generator,depfun=depfun,copula=copula, pars=parameters) 
  estim <- fparest(data)
  #---2D Archimax---
  if(ncol(data)==2 && is.null(copula)) {  
    m <- n
    fK <- function(vec,pars) {
      tauA <- 0
      if(depfun$id!="1") {
        integrand <- Vectorize(function(t) t*(1-t)*depfun$dep.der2(t, pars[[2]])/depfun$dep(t, pars[[2]]), vectorize.args="t")
        tauA <- integrate(integrand,lower=0,upper=1)$value
      }
      sapply(vec, function(t) t - ifelse(t>0 && t<1, (1-tauA)*generator$gen(t, pars[[1]])/generator$gen.der(t, pars[[1]]), 0))
    }
    Sn <- sum((rank(Ein)/n - fK(Ein,estim$parameters))^2)
    pb <- txtProgressBar(min=0,max=N,style=3)
    loop <- function(k) {
      simcop <- fsimcop(estim$parameters)
      Ein <- pCopulaEmpirical(simcop)
      simcopRescaled <- apply(simcop,2,rank)/(n+1)
      parest <-  fparest(simcopRescaled)$parameters
      setTxtProgressBar(pb, k)
      #if(k%%10==0) print(k)
      sum((rank(Ein)/n - fK(Ein,parest))^2)
    }    
  }
  #---other copulas---
  else {  
    m <- max(m,n)
    simcop <- fsimcop(estim$parameters)
    Vi <- pCopulaEmpirical(simcop)
    Bm <- rank(Vi)/m
    Kn <- sapply(Vi,fKn,y=Ein)
    Sn <- (n/m)*sum((Kn-Bm)^2)
    pb <- txtProgressBar(min=0,max=N,style=3)
    loop <- function(k) {
      simcop <- fsimcop(estim$parameters)
      Ein <- pCopulaEmpirical(simcop)
      simcopRescaled <- apply(simcop,2,rank)/(m+1)
      parest <-  fparest(simcopRescaled)$parameters
      simcop <- fsimcop(parest)
      Vi <- pCopulaEmpirical(simcop)
      Bm <- rank(Vi)/m
      Kn <- sapply(Vi,fKn,y=Ein)
      setTxtProgressBar(pb, k)
      #if(k%%10==0) print(k)
      (n/m)*sum((Kn-Bm)^2)
    }
  }
  Snk <- if(ncores > 1) do.call(c, parallel::mclapply(1:N,loop,mc.cores=ncores)) else sapply(1:N,loop)
  close(pb)
  #cat("\n")  
  output <- list(
    statistic=Sn,
    q95=quantile(Snk,0.95,names=F),
    p.value=sum(Snk > Sn)/N,
    estimate=c(if(is.null(copula)) c(gpars=estim$parameters$gpars,dpars=estim$parameters$dpars) else pars=estim$parameters, fvalue=estim$fvalue),
    data.name=deparse(substitute(data)),
    method="Blanket GOF test based on Kendall's transform",
    fitting_method=as.vector(c(etechnique,eprocedure,emethod)),
    copula_id=if(is.null(copula)) c(generator=generator$id,depfun=depfun$id) else copula$id
    )
  class(output) <- "gCopula"
  output
}

gCopulaEmpirical <- function(data,N=100,ncores=1) {
  data1 <- data[[1]]; data2 <- data[[2]]
  dim <- ncol(data1); if(dim!=ncol(data2)) stop("Different number of columns.")
  n1 <- nrow(data1); n2 <- nrow(data2)
  #Cramer-von Mises test statistic
  Sn <- local({
    split1 <- split(data1,col(data1)); split2 <- split(data2,col(data2))
    s1 <- sum(Reduce("*",lapply(split1,function(a) outer(a,a,function(...) 1-pmax(...)))))
    s2 <- sum(Reduce("*",mapply(function(a,b) outer(a,b,function(...) 1-pmax(...)),split1,split2,SIMPLIFY=F)))
    s3 <- sum(Reduce("*",lapply(split2,function(a) outer(a,a,function(...) 1-pmax(...)))))
    1/(1/n1+1/n2)*(s1/n1^2-s2*2/n1/n2+s3/n2^2)
  })
  #empirical copulas and their approximate derivatives
  Cn <- function(x) sum(apply(t(data1) <= x,2,prod))/n1
  Dn <- function(x) sum(apply(t(data2) <= x,2,prod))/n2
  dCn <- function(x,i,h) {
    e <- numeric(dim); e[i] <- h
    (Cn(x+e)-Cn(x-e))/2/h
  } 
  dDn <- function(x,i,h) {
    e <- numeric(dim); e[i] <- h
    (Dn(x+e)-Dn(x-e))/2/h
  }
  #gaussian process and multiplier technique functions
  alpha <- function(x) sum(apply(t(data1) <= x,2,prod)*xi)/sqrt(n1)
  gamma <- function(x) sum(apply(t(data2) <= x,2,prod)*zeta)/sqrt(n2)
  beta <- function(x,i) sum((data1[,i]<=x)*xi)/sqrt(n1)
  delta <- function(x,i) sum((data2[,i]<=x)*zeta)/sqrt(n2)
  Ck <- function(x) alpha(x) - sum(sapply(1:dim,function(i) beta(x[i])*dCn(x,i,1/sqrt(n1))))
  Dk <- function(x) gamma(x) - sum(sapply(1:dim,function(i) delta(x[i])*dDn(x,i,1/sqrt(n2))))
  EPSk <- function(x) (sqrt(n2)*Ck(x)-sqrt(n1)*Dk(x)) #divisor sqrt(n1+n2) moved to integral
  #merged data
  data12 <- merge(data1,data2,all=T); n12 <- nrow(data12)
  #iteration
  pb <- txtProgressBar(min=0,max=N,style=3)
  xi <- zeta <- numeric(0)
  loop <- function(k) {
    xii <- rnorm(n1); xi <<- xii - mean(xii)
    zetaa <- rnorm(n2); zeta <<- zetaa - mean(zetaa)
    setTxtProgressBar(pb, k)
    sum(apply(data12,1,EPSk)^2)/(n1+n2)/n12    
  }
  Snk <- if(ncores > 1) do.call(c, parallel::mclapply(1:N,loop,mc.cores=ncores)) else sapply(1:N,loop)
  close(pb)
  #compose output
  output <- list(
    statistic=Sn,
    q95=quantile(Snk,0.95,names=F),
    p.value=sum(Snk > Sn)/N,
    estimate=NULL,
    data.name=if(is.null(names(data))) deparse(substitute(data)) else names(data),
    method="Test of equality between 2 empirical copulas (Remillard & Scaillet 2009)",
    fitting_method=NULL,
    copula_id=NULL
  )
  class(output) <- "gCopula"
  output  
}


#method for printing gCopula results list
print.gCopula <- function(x,...) {
  cat("\n\t\t", x$method, "\n\n")
  print.default(c(statistic=x$statistic,q95=x$q95,p.value=x$p.value))
  cat("-----------------------------\n")
  cat("data: ", x$data.name, "\n")
  cat("copula: ",x$copula_id,"\n")
  cat("estimates:\n")
  print.default(x$estimate)
  invisible(x)
}

#check d-increasingness, 0 as annihilator and 1 as neutral element for every parameters combination
#Example: isCopula(generator=genJoe,dep=depGalambos,dim=3)
isCopula <- function(generator=genLog(),depfun=dep1(),copula=NULL,
                     glimits=list(generator$lower,generator$upper), 
                     dlimits=list(depfun$lower,depfun$upper),
                     limits=list(copula$lower,copula$upper),  
                     ggridparameters=if(!is.null(unlist(glimits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), glimits) else NULL,
                     dgridparameters=if(!is.null(unlist(dlimits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), dlimits) else NULL, 
                     gridparameters=if(!is.null(unlist(limits))) do.call(function(...) mapply(c,...,length.out=pgrid,SIMPLIFY=FALSE), limits) else NULL,
                     dagrid=10, pgrid=10, dim=3, tolerance=1e-15) {
  #initial (local) variables
  npg <- length(generator$parameters); npd <- length(depfun$parameters)  #number of gen and dep parameters
  ig <- min(1,npg):npg; id <- {if(npd < 1) 0 else (1:npd)+npg}   #indices of generator and depfun parameters
  splitad <- function(pars) list(gpars=pars[ig],dpars=pars[id])  #func to separate gen/dep parameters (in the end)
  #preparing parameters
  makeseq <- function(x) {  #make sequence if any argument for seq() is recognized  
    if(sum(match(names(x),c("from","to","by","length.out","along.with"),nomatch =0)) > 0) {
      x <- replace(x,x==Inf,32); x <- replace(x,x==-Inf,-32) #replace infinite boundary
      return(unique(do.call(seq,as.list(x))))
    }
    else return(x)
  }
  gparameters <- lapply(ggridparameters,makeseq) #make sequence 
  dparameters <- lapply(dgridparameters,makeseq) 
  parameters <- lapply(gridparameters,makeseq)  
  gparameters <- mapply(function(x,y,z) x[x>=y & x<=z], gparameters, generator$lower, generator$upper, SIMPLIFY=FALSE) #ensure the parameter sequence is within permitted range
  dparameters <- mapply(function(x,y,z) x[x>=y & x<=z], dparameters, depfun$lower, depfun$upper, SIMPLIFY=FALSE)
  parameters <- mapply(function(x,y,z) x[x>=y & x<=z], parameters, copula$lower, copula$upper, SIMPLIFY=FALSE)
  #preparing data 
  axes <- mapply(seq.int,rep.int(0,dim),rep.int(1,dim),MoreArgs=list(length.out=dagrid)) #expand grid of all (n=m^d) points
  allpoints <- do.call(expand.grid,split(t(axes),1:dim))  
  #distinguish archimax and generic copula
  if(is.null(copula)) {
    parameters <- c(gpar=gparameters,dparameters=dparameters)
    cdfun <- function(pars) pCopula(allpoints, generator=generator, depfun=depfun, pars=list(pars[ig],pars[id]))
  }
  else {
    names(parameters) <- paste("cpar",1:length(parameters),sep="")
    cdfun <- function(pars) pCopula(allpoints, copula=copula, pars=pars)
  }
  #minimal dim-difference (C-volume) in hypercube data grid
  monot <- function(hcdata) {
    out <- numeric(dim)
    for(i in 1:dim) {
      sub1 <- subd <- rep.int(",",dim) #dim equals length(dim(hcdata))
      sub1[i] <- -1; subd[i] <- -dim(hcdata)[i]
      subcop <- function(sub) eval(parse(text = paste(c("hcdata[", sub, ", drop = FALSE]"),collapse="")))
      hcdata <- subcop(sub1) - subcop(subd) #recursive first differences
      out[i] <- min(hcdata) #find the smallest of the current i-th differences
    }
    out
  }
  annih <- function(hcdata) {
    sapply(
      1:dim,
      function(i) { #e.g. i=3,dim=4 yields hcdata[,,1,]
        sub <- rep.int(",",dim) #dim equals length(dim(hcdata))
        sub[i] <- 1
        max(abs(eval(parse(text = paste(c("hcdata[", sub, "]"),collapse=""))))) #find maximal deviation from 0 #max|C(x1,...xn,0,xm,...) - 0|
      }
    )
  }
  neutel <- function(hcdata) {
    sapply(
      1:dim,
      function(i) {
        sub <- as.character(dim(hcdata))
        sub[i] <- ""
        max(abs(eval(parse(text = paste(c("hcdata[", paste(sub,collapse=","), "]"),collapse="")))-axes[,i])) #max|C(1,...1,x,1,...1) - x|
      }
    )    
  }
  fun <- function(pars) {
    coparray <- cdfun(pars) 
    if(length(coparray)!=dagrid^dim || !all(is.finite(coparray))) stop(paste("Copula with parameter(s): ",paste(pars,collapse=" ")," led to errors.\n")) #test for propper length (nrows) to catch errors in cdf
    coparray <- array(coparray,rep.int(dagrid,dim))
    c(
      monot(coparray),
      -annih(coparray), #minus unary operator for evaluation compatibility with one-sided monot criterion
      -neutel(coparray)
    )
  }
  parsgrid <- as.matrix(expand.grid(parameters, KEEP.OUT.ATTRS = FALSE)) #all combinations
  result <- apply(parsgrid,1,fun); dim(result) <- c(dim,3,ncol(result)) #dim1~copuladimension,dim2~(monot,annih,neutel),dim3~parametersgrid
  ind <- which(result < 0 - tolerance, arr.ind=T, useNames=F) 
  output <- data.frame(
    dim=ind[,1], #to which variable the issue is related
    property=c("monot","annih","neutel")[ind[,2]], #which copula property is violated
    value=result[ind]*ifelse(ind[,2]==1,1,-1), #value of difference or deviation from theoretical value, that exceeds tolerance
    parsgrid[ind[,3],,drop=F] #actual parameters of copula related to the issue 
  )
  output <- list(
    is.copula = !as.logical(nrow(output)),
    issues = output
  )
  class(output) <- "isCopula"
  output
}
 
#method for printing isCopula results (does not work with output as data.frame; to fix)
print.isCopula <- function(x,...) {
  n <- nrow(x$issues)
  cat("\n Does the object appears to be a copula(?): ", x$is.copula, "\n")
  if(n>0) {
    cat("\n Showing", min(n,5), "of", n, "issues: \n\n")
    print.data.frame(x$issues[1:min(n,5),])
  }
  #invisible(x)
}

Try the acopula package in your browser

Any scripts or data that you put into this service are public.

acopula documentation built on Sept. 11, 2023, 1:08 a.m.