#############################################################################
## Estimate g_AMSE pilot bandwidths for even orders - 2-dim
##
## Parameters
## r - (r1, r2) partial derivative
## n - sample size
## psi1 - psi_(r + (2,0))
## psi2 - psi_(r + (0,2))
##
## Returns
## g_AMSE pilot bandwidths for even orders
#############################################################################
gamse.even.2d <- function(r, n, psi1, psi2)
{
    d <- 2
    num <- -2 * dmvnorm.deriv(x=c(0,0), deriv.order=r, Sigma=diag(2), deriv.vec=FALSE)
    den <- (psi1 + psi2) * n   
    g.amse <- (num/den)^(1/(2 + d + sum(r)))

    return(g.amse)
}

#############################################################################
## Estimate g_AMSE pilot bandwidths for odd orders - 2-dim
##
## Parameters
##
## r - (r1, r2) partial derivative
## n - sample size
## psi1 - psi_(r + (2,0))
## psi2 - psi_(r + (0,2))
## psi00 - psi_(0,0)
## RK - R(K^(r))
## 
## Returns
## g_AMSE pilot bandwidths for odd orders
#############################################################################
gamse.odd.2d <- function(r, n, psi1, psi2, psi00, RK)
{  
    d <- 2
    num <- 2 * psi00 * (2 * sum(r) + d) * RK
    den <- (psi1 + psi2)^2 * n^2
    g.amse <- (num/den)^(1/(2*sum(r) + d + 4))

    return(g.amse)
}

#############################################################################
## Estimate g_SAMSE pilot bandwidth - 2- to 6-dim 
##
## Parameters
## Sigma.star - scaled variance matrix
## n - sample size
##
## Returns
## g_SAMSE pilot bandwidth
#############################################################################
gsamse <- function(Sigma.star, n, modr, nstage=1, psihat=NULL)
{
    d <- ncol(Sigma.star)
    K <- numeric(); psi <- numeric()

    ## 4th order g_SAMSE
    K <- dmvnorm.deriv(x=rep(0,d), deriv.order=modr, Sigma=diag(d), add.index=TRUE, deriv.vec=FALSE)
    K <- K$deriv[apply(K$deriv.ind, 1, is.even)]

    if (modr==4)
    {
        derivt4 <- dmvnorm.deriv(x=rep(0,d), deriv.order=4, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)
        derivt6 <- dmvnorm.deriv(x=rep(0,d), deriv.order=6, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)

        for (i in 1:nrow(derivt4))
        {
            r <- derivt4[i,]
            if (is.even(r))
            {
                A3psi <- 0
                for (j in 1:d)
                {
                    if (nstage==1) A3psi <- A3psi + psins(r=r+2*elem(j,d), Sigma=Sigma.star)
                    else if (nstage==2) A3psi <- A3psi + psihat[which.mat(r=r+2*elem(j,d), mat=derivt6)]
                }
                psi <- c(psi, A3psi)    
            }
        }
    }
    ## 6th order g_SAMSE
    else if (modr==6)
    {
        derivt6 <- dmvnorm.deriv(x=rep(0,d), deriv.order=6, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)

        for (i in 1:nrow(derivt6))
        {
            r <- derivt6[i,]        
            if (is.even(r))
            {
                A3psi <- 0
                for (j in 1:d)
                A3psi <- A3psi + psins(r=r+2*elem(j,d), Sigma=Sigma.star)
                psi <- c(psi, A3psi)
            }
        }
    }

    ## see thesis for formula
    A1 <- sum(K^2)
    A2 <- sum(K * psi)  
    A3 <- sum(psi^2)
    B1 <- (2*modr + 2*d)*A1
    B2 <- (modr + d - 2)*A2
    B3 <- A3
    gamma1 <- (-B2 + sqrt(B2^2 + 4*B1*B3)) / (2*B1)
    g.samse <- (gamma1 * n)^(-1/(modr + d + 2))

    return (g.samse)      
}

############################################################################
## Scalar pilot selector for derivatives r>0 from Chacon & Duong (2011)
## Generalisation of gsamse for r>0
############################################################################
gdscalar <- function(x, d, r, n, verbose, binned=FALSE, nstage=1, scv=FALSE)
{
    if (scv) cf <- c(2^(-d), 2^(-d/2+1), 4)
    else cf <- c(1,1,1)
    S <- var(x)
    Sinv <- chol2inv(chol(S))

    if (nstage==1)
    {
        psi2r6.ns <- psins(r=2*r+6, Sigma=S)
        B3 <- norm(Matrix(psi2r6.ns, nrow=1) %*% (Matrix(vec(diag(d)), ncol=1) %*% Matrix(vec(diag(d)), nrow=1) %x% Diagonal(d^(2*r+4))) %*% Matrix(psi2r6.ns, ncol=1), type="1")
        if (!scv)
        {
            B1 <- 2*(2*pi)^(-d)*OF(2*r+4)*prod(d+2*(0:(r+2)))  
            B2 <- -(d+2*r+2)*2^(-d/2-r)*(2*pi)^(-d)*det(S)^(-1/2)*OF(2*r+4)*nu(r=r+2, A=Sinv)
        }
        else
        {
            B1 <- 2^(-2*r-3)*(4*pi)^(-d)*OF(2*r+6)*prod(d+2*(0:(r+2)))
            B2 <- -(d+2*r+2)*2^(-2*r-4)*(4*pi)^(-d)*det(S)^(-1/2)*OF(2*r+4)*nu(r=r+2, A=Sinv)
            B3 <- 4*B3
        }
        g2r4 <- (2*B1/(-B2 + sqrt(B2^2 + 4*B1*B3)))^(1/(d+2*r+8))*n^(-1/(d+2*r+8))
    }
    else if (nstage==2)
    {
        psi2r8.ns <- psins(r=2*r+8, Sigma=S)
        B3 <- norm(Matrix(psi2r8.ns, nrow=1) %*% (Matrix(vec(diag(d)), ncol=1) %*% Matrix(vec(diag(d)), nrow=1) %x% Diagonal(d^(2*r+6))) %*% Matrix(psi2r8.ns, ncol=1), type="1")
        if (!scv)
        {
            B1 <- 2*(2*pi)^(-d)*OF(2*r+6)*prod(d+2*(0:(r+3)))  
            B2 <- -(d+2*r+4)*2^(-d/2-r+2)*(2*pi)^(-d)*det(S)^(-1/2)*OF(2*r+6)*nu(r=r+4, A=Sinv)
        }
        else
        {
            B1 <- 2^(-2*r-5)*(4*pi)^(-d)*OF(2*r+6)*prod(d+2*(0:(r+3)))
            B2 <- -(d+2*r+4)*2^(-2*r-6)*(4*pi)^(-d)*det(S)^(-1/2)*OF(2*r+6)*nu(r=r+4, A=Sinv)
            B3 <- 4*B3
        }
        g2r6.ns <- (2*B1/(-B2 + sqrt(B2^2 + 4*B1*B3)))^(1/(d+2*r+8))*n^(-1/(d+2*r+8))
        L0 <- dmvnorm.mixt(x=rep(0,d), mus=rep(0,d), Sigmas=diag(d), props=1)
        if (binned)
        eta2r6 <- drop(kfe(x=x, G=g2r6.ns^2*diag(d), inc=1, binned=binned, deriv.order=2*r+6, add.index=FALSE, verbose=verbose) %*% vec(diag(d^(r+3))))
        else
        eta2r6 <- Qr(x=x, deriv.order=2*r+6, Sigma=g2r6.ns^2*diag(d), inc=1)
        
        A1 <- cf[1]*(2*d+4*r+8)*L0^2*OF(2*r+4)*nu(r=r+2, A=diag(d))
        A2 <- cf[2]*(d+2*r+2)*L0*OF(2*r+4)*eta2r6
        A3 <- cf[3]*eta2r6^2
        
        g2r4 <- (2*A1/((-A2+ sqrt(A2^2 +4*A1*A3))*n))^(1/(d+2*r+6))
    }
    return(g2r4)
}

############################################################################
## Unconstrained pilot selector for derivatives r>0 from Chacon & Duong (2011)
## Generalisation of Gunconstr for r>0
############################################################################
Gdunconstr <- function(x, d, r, n, nstage=1, verbose, binned=FALSE, scv=FALSE, optim.fun="optim")
{
    if (scv) cf <- c(2^(-(d/2+r+2)), 2)
    else cf <- c(1,1)
    S <- var(x) 
    S12 <- matrix.sqrt(S)

    optim.fun <- match.arg(optim.fun, c("nlm", "optim")) 
    if (nstage==2)
    {
        # G2r6.NR <- Gns(r=2*r+6,n=n,Sigma=S, scv=scv)
        # vecPsi2r6 <- kfe(x=x, G=G2r6.NR, binned=binned, deriv.order=2*r+6, deriv.vec=TRUE, add.index=FALSE, verbose=verbose)   
        x.star <- pre.sphere(x)
        G2r6.NR <- Gns(r=2*r+6,n=n,Sigma=var(x.star), scv=scv)
        vecPsi2r6 <- kfe(x=x.star, G=G2r6.NR, binned=binned, deriv.order=2*r+6, deriv.vec=TRUE, add.index=FALSE, verbose=verbose)   

        dls <- (0:(d^2-1))*d^(2*r+4) 

        AB2 <- function(vechG)
        {
            G <- invvech(vechG) %*% invvech(vechG)
            Ginv <- chol2inv(chol(G))
            ## direct computation
            v1 <- n^(-1)*det(G)^(-1/2)*(2*pi)^(-d/2)*(-1)^(r+2)*OF(2*r+4)* Sdrv(d=d,r=2*r+4,v=Kpow(vec(Ginv),r+2))

            v2 <- numeric(d^(2*r+4))
            for(k in 1:d^(2*r+4)) { v2[k] <- sum(vec(G)*vecPsi2r6[dls+k]) }

            AB <- cf[1]*v1 + cf[2]*(1/2)*v2
            AB2.val <- sum(AB^2)
            return(AB2.val)
        }

        Gstart <- Gns(r=2*r+4, n=n, Sigma=var(x.star), scv=scv)
        Gstart <- matrix.sqrt(Gstart)
        
        if (optim.fun=="nlm")
        {
            result <- nlm(p=vech(Gstart), f=AB2, print.level=2*as.logical(verbose))
            G2r4 <- result$estimate
        } 
        else if (optim.fun=="optim")
        { 
            result <- optim(par=vech(Gstart), fn=AB2, method="BFGS", control=list(trace=as.numeric(verbose), REPORT=1))
            G2r4 <- result$par
        }
        G2r4 <- invvech(G2r4) %*% invvech(G2r4)
        G2r4 <- S12 %*% G2r4 %*% S12
    }
    else
    {
        G2r4 <- Gns(r=2*r+4, n=n, Sigma=S, scv=scv)
    }
    
    return(G2r4)
}

############################################################################
## Estimate psi functionals using 1-stage plug-in 
##
## Parameters
## x.star - pre-transformed data points
## pilot - "amse" = different AMSE pilot bandwidths
##       - "samse" = optimal SAMSE pilot bandwidth
##
## Returns
## estimated psi functionals
#############################################################################
psifun1 <- function(x.star, pilot="samse", binned, bin.par, deriv.order=0, verbose=FALSE)
{
    d <- ncol(x.star)
    r <- deriv.order
    S.star <- var(x.star)
    n <- nrow(x.star)

    ## pilots are based on (2r+4)-th order derivatives
    ## compute 1 pilot for SAMSE
    if (pilot=="samse")
    {
        g.star <- gsamse(S.star, n, 4)
        psihat.star <- kfe(x=x.star, G=g.star^2*diag(d), deriv.order=4, deriv.vec=TRUE, binned=binned, add.index=TRUE, verbose=verbose)
    }
    ## compute 5 different pilots for AMSE
    else if ((pilot=="amse") & (d==2))
    {
        derivt4 <- dmvnorm.deriv(x=rep(0,d), deriv.order=4, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)
        derivt4.vec <- dmvnorm.deriv(x=rep(0,d), deriv.order=4, add.index=TRUE, deriv.vec=TRUE, only.index=TRUE)

        RK31 <- 15/(64*pi)
        psi00 <- psins(r=c(0,0), Sigma=S.star) 
        psihat.star <- vector()
        g.star <- vector()

        for (k in 1:nrow(derivt4))
        { 
            r <- derivt4[k,]
            psi1 <- psins(r=r + 2*elem(1, 2), Sigma=S.star)
            psi2 <- psins(r=r + 2*elem(2, 2), Sigma=S.star)

            ## odd order
            if (prod(r) == 3) g.star[k] <- gamse.odd.2d(r=4, n, psi1, psi2, psi00, RK31)
            ## even order
            else g.star[k] <- gamse.even.2d(r=4, n, psi1, psi2)[k]
            psihat.star[k] <- kfe.scalar(x=x.star, deriv.order=r, g=g.star[k], binned=binned, bin.par=bin.par)
        }

        ## create replicated form of psihat
        psihat.star.vec <- rep(0, nrow(derivt4.vec))
        for (k in 1:nrow(derivt4.vec))
        psihat.star.vec[k] <- psihat.star[which.mat(r=derivt4.vec[k,], mat=derivt4)]

        psihat.star <- list(psir=psihat.star.vec, deriv.ind=derivt4.vec)
    }

    return(psihat.star)
}

#############################################################################
## Estimate psi functionals using 2-stage plug-in 
##
## Parameters
## x - pre-transformed data points
## pilot - "amse" - different AMSE pilot
##       - "samse" - SAMSE pilot
## Returns
## estimated psi functionals
#############################################################################
psifun2 <- function(x.star, pilot="samse", binned, bin.par, deriv.order=0, verbose=FALSE)
{ 
    d <- ncol(x.star)
    r <- deriv.order
    S.star <- var(x.star)
    n <- nrow(x.star)

    ## pilots are based on (2r+4)-th order derivatives
    ## compute 1 pilot for SAMSE    
    if (pilot=="samse")
    {
        g6.star <- gsamse(S.star, n=n, modr=6)
        psihat6.star <- kfe(x=x.star, G=g6.star^2*diag(d), deriv.order=6, deriv.vec=TRUE, binned=binned, add.index=FALSE, verbose=verbose)
        g.star <- gsamse(S.star, n=n, modr=4, nstage=2, psihat=psihat6.star)
        psihat.star <- kfe(x=x.star, G=g.star^2*diag(d), deriv.order=4, deriv.vec=TRUE, binned=binned, add.index=TRUE, verbose=verbose)
    }
    ## compute different pilots for AMSE
    else if ((pilot=="amse") & (d==2))
    {
        derivt4 <- dmvnorm.deriv(x=rep(0,d), deriv.order=4, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)
        derivt4.vec <- dmvnorm.deriv(x=rep(0,d), deriv.order=4, add.index=TRUE, deriv.vec=TRUE, only.index=TRUE)
        derivt6 <- dmvnorm.deriv(x=rep(0,d), deriv.order=6, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)

        RK31 <- 15/(64*pi)
        RK51 <- 945/(256*pi)
        RK33 <- 225/(256*pi)
        psi00 <- psins(r=rep(0,d), Sigma=S.star) 

        psihat6.star <- vector()
        g6.star <- vector()
        psihat.star <- vector()
        g.star <- vector()

        for (k in 1:nrow(derivt6))
        {
            r <- derivt6[k,]
            psi1 <- psins(r=r + 2*elem(1, 2), Sigma=S.star)
            psi2 <- psins(r=r + 2*elem(2, 2), Sigma=S.star)
            if (prod(r) == 5)
            g6.star[k] <- gamse.odd.2d(r=6, n, psi1, psi2, psi00, RK51)
            else if (prod(r) == 9)
            g6.star[k] <- gamse.odd.2d(r=6, n, psi1, psi2, psi00, RK33) 
            else  
            g6.star[k] <- gamse.even.2d(r=6, n, psi1, psi2)[k]

            psihat6.star[k] <- kfe.scalar(x=x.star, deriv.order=r, g=g6.star[k], binned=binned, bin.par=bin.par) 
        }

        ## pilots are based on 4th order derivatives using 6th order psi functionals
        ## computed above 'psihat6.star'
        for (k in 1:nrow(derivt4))
        {
            r <- derivt4[k,]
            psi1 <- psihat6.star[7 - (r + 2*elem(1,2))[1]]
            psi2 <- psihat6.star[7 - (r + 2*elem(2,2))[1]]

            if (prod(r) == 3)
            g.star[k] <- gamse.odd.2d(r=4, n, psi1, psi2, psi00, RK31)
            else
            g.star[k] <- gamse.even.2d(r=4, n, psi1, psi2)[k]

            psihat.star[k] <- kfe.scalar(x=x.star, deriv.order=r, g=g.star[k],  binned=binned, bin.par=bin.par) 
        }

        ## create replicated form of psihat
        psihat.star.vec <- rep(0, nrow(derivt4.vec))
        for (k in 1:nrow(derivt4.vec))
        psihat.star.vec[k] <- psihat.star[which.mat(r=derivt4.vec[k,], mat=derivt4)]

        psihat.star <- list(psir=psihat.star.vec, deriv.ind=derivt4.vec)
    }

    return(psihat.star)
}

#############################################################################
## Estimate psi functionals for 6-variate data using 1-stage plug-in 
## with unconstrained pilot
##
## Parameters
## x - data points
## Sd4, Sd6 - symmetrizer matrices of order 4 and 6
##
## Returns
## estimated psi functionals
#############################################################################

psifun1.unconstr <- function(x, binned, bgridsize, deriv.order=0, verbose=FALSE)
{
    n <- nrow(x)
    r <- deriv.order
    S <- var(x)

    ## stage 1 of plug-in
    G2r4 <- Gns(r=2*r+4,n=n,Sigma=S) 
    vecPsi2r4 <- kfe(x=x, G=G2r4, deriv.order=2*r+4, binned=binned, bgridsize=bgridsize, deriv.vec=TRUE, add.index=FALSE, verbose=verbose) 
    return (vecPsi2r4)
}

#############################################################################
## Estimate psi functionals for 6-variate data using 2-stage plug-in 
## with unconstrained pilot
##
## Parameters
## x - data points
## Sd4, Sd6 - symmetrizer matrices of order 4 and 6
##
## Returns
## estimated psi functionals
############################################################################

psifun2.unconstr <- function(x, binned, bgridsize, deriv.order=0, verbose=FALSE, optim.fun="optim")
{
    d <- ncol(x)
    n <- nrow(x)
    S <- var(x)
    S12 <- matrix.sqrt(S)
    r <- deriv.order
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    ## stage 1 of plug-in
    x.star <- pre.sphere(x)
    G2r6 <- Gns(r=2*r+6,n=n,Sigma=var(x.star)) 
    vecPsi2r6 <- kfe(x=x.star, G=G2r6, binned=binned, bgridsize=bgridsize, deriv.order=2*r+6, deriv.vec=TRUE, add.index=FALSE, verbose=verbose)

    ## asymptotic squared bias for r = 4 for MSE-optimal G
    D2r4phi0 <- DrL0(d=d, r=2*r+4)
    Id2r4 <- diag(d^(2*r+4))

    AB2 <- function(vechG)
    {
        rr <- 2*r+4
        G <- invvech(vechG)%*%invvech(vechG)
        G12 <- matrix.sqrt(G)
        Ginv12 <- chol2inv(chol(G12))
        AB <- n^(-1)*det(Ginv12)*(Kpow(A=Ginv12,pow=rr)%*%D2r4phi0)+(1/2)*(t(vec(G))%x%Id2r4) %*% vecPsi2r6
        AB2.val <- sum(AB^2)
        return (AB2.val)
    }

    Gstart <- Gns(r=2*r+4,n=n,Sigma=var(x.star))
    Gstart <- matrix.sqrt(Gstart)

    if (optim.fun=="nlm")
    {
        res <- nlm(p=vech(Gstart), f=AB2, print.level=2*as.logical(verbose))    
        G2r4 <- res$estimate
    }
    else if (optim.fun=="optim")
    {
        res <- optim(vech(Gstart), AB2, method="BFGS", control=list(trace=as.numeric(verbose), REPORT=1))
        G2r4 <- res$par
    }
    G2r4 <- invvech(G2r4)%*%invvech(G2r4)
    G2r4 <- S12 %*% G2r4 %*% S12

    ## stage 2 of plug-in
    vecPsi2r4 <- kfe(x=x, G=G2r4, binned=binned, bgridsize=bgridsize, deriv.order=2*r+4, deriv.vec=TRUE, add.index=FALSE, verbose=verbose)

    return (vecPsi2r4)
}

#############################################################################
## Plug-in bandwidth selectors
#############################################################################

############################################################################
## Computes plug-in full bandwidth matrix - 2 to 6 dim
##
## Parameters
## x - data points
## Hstart - initial value for minimisation
## nstage - number of plug-in stages (1 or 2)
## pilot - "amse" - different AMSE pilot
##       - "samse" - SAMSE pilot
##       - "unconstr" - unconstrained pilot
## pre - "scale" - pre-scaled data
##     - "sphere"- pre-sphered data 
##
## Returns
## Plug-in full bandwidth matrix
#############################################################################
hpi <- function(x, nstage=2, binned=TRUE, bgridsize, deriv.order=0)
{
    ## 1-d selector is taken from KernSmooth's dpik
    d <- 1 
    if (missing(bgridsize)) bgridsize <- default.gridsize(d)
    if (deriv.order==0) h <- dpik(x=x, level=nstage, gridsize=bgridsize)
    else
    {
        n <- length(x)
        d <- 1
        r <- deriv.order  
        K2r4 <- dnorm.deriv(x=0, mu=0, sigma=1, deriv.order=2*r+4)
        K2r6 <- dnorm.deriv(x=0, mu=0, sigma=1, deriv.order=2*r+6) 
        m2 <- 1  
        mr <- psins.1d(r=2*r, sigma=1)

        ## formula for bias annihilating bandwidths from Wand & Jones (1995, p.70)
        if (nstage==2)
        {
            psi2r8.hat <- psins.1d(r=2*r+8, sigma=sd(x))
            gamse2r6 <- (2*K2r6/(-m2*psi2r8.hat*n))^(1/(2*r+9)) 
            psi2r6.hat <- kfe.1d(x=x, g=gamse2r6, deriv.order=2*r+6, inc=1, binned=binned)
            gamse2r4 <- (2*K2r4/(-m2*psi2r6.hat*n))^(1/(2*r+7))
            psi2r4.hat <- kfe.1d(x=x, g=gamse2r4, deriv.order=2*r+4, inc=1, binned=binned)
        }
        else 
        {
            psi2r6.hat <- psins.1d(r=2*r+6, sigma=sd(x))
            gamse2r4 <- (2*K2r4/(-m2*psi2r6.hat*n))^(1/(2*r+7))
            psi2r4.hat <- kfe.1d(x=x, g=gamse2r4, deriv.order=2*r+4, inc=1, binned=binned)
        }

        ## formula form Wand & Jones (1995, p.49)
        h <- ((2*r+1)*mr/(m2^2*psi2r4.hat*n))^(1/(2*r+5))
    }

    return(h)
}

Hpi <- function(x, nstage=2, pilot, pre="sphere", Hstart, binned, bgridsize, amise=FALSE, deriv.order=0, verbose=FALSE, optim.fun="optim")
{
    n <- nrow(x)
    d <- ncol(x)
    r <- deriv.order
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (d > 4) binned <- FALSE
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)
    if (!is.matrix(x)) x <- as.matrix(x)

    if (missing(pilot)) { if (d==2 & r==0) pilot <- "samse" else pilot <- "dscalar" }
    pilot1 <- match.arg(pilot, c("amse", "samse", "unconstr", "dunconstr", "dscalar"))
    pre1 <- match.arg(pre, c("scale", "sphere"))
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    if (pilot1=="amse" & (d>2 | r>0)) stop("amse pilot selectors not defined for d>2 and/or r>0")
    if ((pilot1=="samse" | pilot1=="unconstr") & r>0) stop("dscalar or dunconstr pilot selectors are better for derivatives r>0")
    if (pilot1=="unconstr" & d>=6) stop("Unconstrained pilots are not implemented for d>6")

    if (pre1=="scale")
    {
        x.star <- pre.scale(x)
        S12 <- diag(sqrt(diag(var(x))))
        Sinv12 <- chol2inv(chol(S12))
    }
    else if (pre1=="sphere")
    {
        x.star <- pre.sphere(x)
        S12 <- matrix.sqrt(var(x))
        Sinv12 <- chol2inv(chol(S12))
    }

    Idr <- diag(d^r)
    RKr <- nu(r=r, diag(d))*2^(-d-r)*pi^(-d/2)

    if (binned)
    {
        H.max <- (((d+8)^((d+6)/2)*pi^(d/2)*RKr)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x)
        bin.par <- binning(x=x, bgridsize=bgridsize, H=H.max)
        H.max.star <- (((d+8)^((d+6)/2)*pi^(d/2)*RKr)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x.star)
        bin.par.star <- binning(x=x.star, bgridsize=bgridsize, H=H.max.star)    
    }

    if (pilot1=="unconstr")
    {
        ## psi4.mat is on data scale
        if (nstage==1)
        psi.fun <- (-1)^r*psifun1.unconstr(x=x, binned=binned, bgridsize=bgridsize, deriv.order=r, verbose=verbose)
        else if (nstage==2)
        psi.fun <- psifun2.unconstr(x=x, binned=binned, bgridsize=bgridsize, deriv.order=r, verbose=verbose)
        psi2r4.mat <- (-1)^r*invvec(psi.fun)   

        ## use normal reference bandwidth as initial condition 
        if (missing(Hstart)) Hstart <- Hns(x=x, deriv.order=r)
    }
    else if (pilot1=="dunconstr")
    {
        ## G2r4 is on data scale
        G2r4 <- Gdunconstr(x=x, d=d, r=r, n=n, nstage=nstage, verbose=verbose, binned=binned, optim.fun=optim.fun)
        vecPsi2r4 <- kfe(x=x, G=G2r4, binned=binned, deriv.order=2*r+4, deriv.vec=TRUE, add.index=FALSE, verbose=verbose, bin.par=bin.par)
        if (missing(Hstart)) Hstart <-  Hns(x=x, deriv.order=r)
    }
    else if (pilot1=="dscalar")
    {
        ## g2r4 is on pre-transformed data scale
        g2r4 <- gdscalar(x=x.star, r=r, n=n, d=d, verbose=verbose, nstage=nstage, binned=binned)

        G2r4 <- g2r4^2 * diag(d)
        vecPsi2r4 <- kfe(x=x.star, G=G2r4, binned=binned, deriv.order=2*r+4, deriv.vec=TRUE, add.index=FALSE, verbose=verbose, bin.par=bin.par.star)
        if (missing(Hstart)) Hstart <-  Hns(x=x.star, deriv.order=r)
    }
    else
    {      
        ## psi4.mat is on pre-transformed data scale
        if (nstage==1)
        psi.fun <- psifun1(x.star, pilot=pilot, binned=binned, bin.par=bin.par.star, deriv.order=r, verbose=verbose)$psir
        else if (nstage==2)
        psi.fun <- psifun2(x.star, pilot=pilot, binned=binned, bin.par=bin.par.star, deriv.order=r, verbose=verbose)$psir
        psi2r4.mat <- invvec(psi.fun)

        ## use normal reference bandwidth as initial condition 
        if (missing(Hstart)) Hstart <- Hns(x=x.star, deriv.order=r)
        else Hstart <- Sinv12 %*% Hstart %*% Sinv12
    }

    ## PI is estimate of AMISE
    pi.temp <- function(vechH)
    { 
        H <- invvech(vechH) %*% invvech(vechH)
        Hinv <- chol2inv(chol(H))
        IdrvH <- Idr%x%vec(H)
        int.var <- 1/(det(H)^(1/2)*n)*nur(r=r, A=Hinv, mu=rep(0,d), Sigma=diag(d))*2^(-d-r)*pi^(-d/2)

        if (pilot1=="dunconstr" | pilot1=="dscalar") 
        pi.val <- int.var + (-1)^r*1/4*vecPsi2r4 %*% (vec(diag(d^r) %x% vec(H) %x% vec(H)))
        else
        pi.val <- int.var + (-1)^r*1/4* sum(diag(t(IdrvH) %*% psi2r4.mat %*% IdrvH))
        pi.val <- drop(pi.val)
        return(pi.val)
    }

    Hstart <- matrix.sqrt(Hstart)
    if (optim.fun=="nlm")
    {
        result <- nlm(p=vech(Hstart), f=pi.temp, print.level=2*as.numeric(verbose))    
        H <- invvech(result$estimate) %*% invvech(result$estimate)
        amise.star <- result$minimum
    }
    else if (optim.fun=="optim")
    {
        result <- optim(vech(Hstart), pi.temp, method="BFGS", control=list(trace=as.numeric(verbose), REPORT=1))
        H <- invvech(result$par) %*% invvech(result$par)
        amise.star <- result$value
    }
    if (!(pilot1 %in% c("dunconstr","unconstr")))  H <- S12 %*% H %*% S12   ## back-transform

    if (!amise) return(H)
    else return(list(H = H, PI.star=amise.star))
}     

#############################################################################
## Computes plug-in diagonal bandwidth matrix for 2 to 6-dim
##
## Parameters
## x - data points
## nstage - number of plug-in stages (1 or 2)
## pre - "scale" - pre-scaled data
##     - "sphere"- pre-sphered data 
##
## Returns
## Plug-in diagonal bandwidth matrix
#############################################################################
Hpi.diag <- function(x, nstage=2, pilot, pre="scale", Hstart, binned, bgridsize, amise=FALSE, deriv.order=0, verbose=FALSE, optim.fun="optim")
{
    n <- nrow(x)
    d <- ncol(x)
    r <- deriv.order
    RK <- (4*pi)^(-d/2)
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (d > 4) binned <- FALSE
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)
    if (!is.matrix(x)) x <- as.matrix(x)

    if (missing(pilot)) { if (d==2 & r==0) pilot <- "samse" else pilot <- "dscalar" }
    pilot1 <- match.arg(pilot, c("amse", "samse", "unconstr", "dunconstr", "dscalar"))
    pre1 <- match.arg(pre, c("scale", "sphere"))
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    if (pre1=="sphere") stop("Using pre-sphering won't give diagonal bandwidth matrix")
    if (pilot1=="amse" & (d>2 | r>0)) stop("samse pilot selectors are better for higher dimensions and/or deriv.order>0")
    if (pilot1=="samse" & r>0) stop("dscalar or dunconstr pilot selectors are better for derivatives r>0")
    if (pilot1=="unconstr" | pilot1=="dunconstr") stop("Unconstrained pilot selectors are not suitable for Hpi.diag")

    if (pre1=="scale")
    {
        x.star <- pre.scale(x)
        S12 <- diag(sqrt(diag(var(x))))
        Sinv12 <- chol2inv(chol(S12))
    }
    else if (pre1=="sphere")
    {
        x.star <- pre.sphere(x)
        S12 <- matrix.sqrt(var(x))
        Sinv12 <- chol2inv(chol(S12))
    }

    if (binned)
    {
        H.max <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x.star)
        bin.par <- binning(x=x.star, bgridsize=bgridsize, H=H.max) 
    }

    Idr <- diag(d^r)  
    if (pilot1=="amse" | pilot1=="samse")
    {
        if (nstage==1)
        psi.fun <- psifun1(x.star, pilot=pilot, binned=binned, bin.par=bin.par, deriv.order=r, verbose=verbose)$psir
        else if (nstage==2)
        psi.fun <- psifun2(x.star, pilot=pilot, binned=binned, bin.par=bin.par, deriv.order=r, verbose=verbose)$psir
        psi2r4.mat <- invvec(psi.fun)
    }
    else if (pilot1=="dscalar")
    {
        g2r4 <- gdscalar(x=x.star, r=r, n=n, d=d, verbose=verbose, nstage=nstage, binned=binned)
        G2r4 <- g2r4^2 * diag(d)
        vecPsi2r4 <- kfe(x=x.star, G=G2r4, binned=binned, deriv.order=2*r+4, deriv.vec=TRUE, add.index=FALSE, verbose=verbose)
    }

    if (d==2 & r==0 & (pilot1=="amse" | pilot1=="samse"))
    {
        ## diagonal bandwidth matrix for 2-dim has exact formula 
        psi40 <- psi.fun[1]
        psi22 <- psi.fun[6]
        psi04 <- psi.fun[16]
        s1 <- sd(x[,1])
        s2 <- sd(x[,2])
        h1 <- (psi04^(3/4)*RK/(psi40^(3/4)*(sqrt(psi40*psi04)+psi22)*n))^(1/6)
        h2 <- (psi40/psi04)^(1/4) * h1
        H <- diag(c(s1^2*h1^2, s2^2*h2^2))
        psimat4.D <- invvech(c(psi40, psi22, psi04))
        amise.star <- drop(n^(-1)*RK*(h1*h2)^(-1) + 1/4*c(h1,h2)^2 %*% psimat4.D %*% c(h1,h2)^2)
    }
    else
    {  
        ## PI is estimate of AMISE
        pi.temp <- function(diagH)
        { 
            H <- diag(diagH) %*% diag(diagH)
            Hinv <- chol2inv(chol(H))
            IdrvH <- Idr%x%vec(H)
            int.var <- 1/(det(H)^(1/2)*n)*nu(r=r, Hinv)*2^(-d-r)*pi^(-d/2)

            if (pilot1=="dscalar")
            pi.val <- int.var + (-1)^r*1/4*vecPsi2r4 %*% (vec(diag(d^r) %x% vec(H) %x% vec(H)))
            else
            pi.val <- int.var + (-1)^r*1/4* sum(diag(t(IdrvH) %*% psi2r4.mat %*% IdrvH))

            return(drop(pi.val))
        }

        ## use normal reference bandwidth as initial condition
        if (missing(Hstart)) Hstart <- Hns(x=x.star, deriv.order=r)
        else Hstart <- Sinv12 %*% Hstart %*% Sinv12
        Hstart <- matrix.sqrt(Hstart)
        
        if (optim.fun=="nlm")
        {
            result <- nlm(p=diag(Hstart), f=pi.temp, print.level=2*as.numeric(verbose))    
            H <- diag(result$estimate^2)
            amise.star <- result$minimum
        }
        else if (optim.fun=="optim")
        {  
            result <- optim(diag(Hstart), pi.temp, method="BFGS", control=list(trace=as.numeric(verbose), REPORT=1))
            H <- diag(result$par) %*% diag(result$par)
            amise.star <- result$value
        }
        H <- S12 %*% H %*% S12  ## back-transform
    }

    if (!amise) return(H)
    else return(list(H = H, PI.star=amise.star))
}

#############################################################################
## Cross-validation bandwidth selectors
#############################################################################

#############################################################################
## Computes the least squares cross validation LSCV function for 2 to 6 dim
## 
## Parameters
## x - data values
## H - bandwidth matrix
##
## Returns
## LSCV(H)
#############################################################################
lscv.1d <- function(x, h, binned, bin.par, deriv.order=0)
{
    r <- deriv.order
    lscv1 <- kfe.1d(x=x, g=sqrt(2)*h, inc=1, binned=binned, bin.par=bin.par, deriv.order=2*r) 
    lscv2 <- kfe.1d(x=x, g=h, inc=0, binned=binned, bin.par=bin.par, deriv.order=2*r) 
    return((-1)^r*(lscv1 - 2*lscv2))     
}

lscv.mat <- function(x, H, binned=FALSE, bin.par, bgridsize, deriv.order=0)
{
    r <- deriv.order
    d <- ncol(x)
    n <- nrow(x)

    if (!binned)
    {
        lscv1 <- Qr(x=x, deriv.order=2*r, Sigma=2*H, inc=1)
        lscv2 <- Qr(x=x, deriv.order=2*r, Sigma=H, inc=0)
        lscv <- drop(lscv1 - 2*lscv2)
    }
    else
    {
        lscv1 <- kfe(x=x, G=2*H, inc=1, binned=binned, bin.par=bin.par, bgridsize=bgridsize, deriv.order=2*r, add.index=FALSE)
        lscv2 <- kfe(x=x, G=H, inc=0, binned=binned, bin.par=bin.par, bgridsize=bgridsize, deriv.order=2*r, add.index=FALSE)
        lscv <- (-1)^2*drop((lscv1 - 2*lscv2) %*% vec(diag(d^r)))
    }

    return(lscv)  
}

#############################################################################
## Finds the bandwidth matrix that minimises LSCV for 2 to 6 dim
## 
## Parameters
## x - data values
## Hstart - initial bandwidth matrix
##
## Returns
## H_LSCV
#############################################################################
hlscv <- function(x, binned=TRUE, bgridsize, amise=FALSE, deriv.order=0, bw.ucv=TRUE)
{
    ## adapted from versions supplied by J.E. Chacon 19/02/2021
    if (any(duplicated(x)))
    warning("Data contain duplicated values: LSCV is not well-behaved in this case")
    n <- length(x)
    d <- 1
    r <- deriv.order
    hnorm <- sqrt((4/(n*(d + 2)))^(2/(d + 4)) * var(x))
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (missing(bgridsize)) if (bw.ucv) bgridsize <- 10000 else bgridsize <- 10001 

    ## use stats::bw.ucv function
    if (bw.ucv)
    {
        difs <- dist(x)
        lower <- min(difs[difs>0])
        opt <- list()
        opt$minimum <- stats::bw.ucv(x=x, nb=bgridsize, lower=lower)
        opt$objective <- NA
    }
    else 
    {
        difs <- x%*%t(rep(1,n))-rep(1,n)%*%t(x)
        difs <- difs[lower.tri(difs)]  

        if (binned)
        {
            bin.par <- binning(x, bgridsize=bgridsize, h=hnorm)
            lscv.1d.temp <- function(h) { return(lscv.1d(x=x, h=h, binned=binned, bin.par=bin.par, deriv.order=r)) }
        }
        else
        {
            if (r>0) stop("Unbinned hlscv not yet implemented for deriv.order>0") 
            edifs <- exp(-difs^2/2)
            RK <- 1/(2*sqrt(pi))

            lscv.1d.temp <- function(h)
            {
                lscv1 <- (1-1/n)*sum(edifs^(1/(2*h^2)))/(h*sqrt(2)*sqrt(2*pi))
                lscv2 <- 2*sum(edifs^(1/h^2))/(h*sqrt(2*pi))
                return(RK/(n*h)+2*(lscv1-lscv2)/(n^2-n))
            }   
        }
        lower <- min(difs[difs>0]) 
        opt <- optimise(lscv.1d.temp, interval=c(lower, 2*hnorm), tol=.Machine$double.eps)
    }

    if (!amise) return(opt$minimum)
    else return(list(h=opt$minimum, LSCV=opt$objective))
}

Hlscv <- function(x, Hstart, binned=FALSE, bgridsize, amise=FALSE, deriv.order=0, verbose=FALSE, optim.fun="optim", trunc)
{
    if (any(duplicated(x))) warning("Data contain duplicated values: LSCV is not well-behaved in this case")
    if (!is.matrix(x)) x <- as.matrix(x)
    n <- nrow(x)
    d <- ncol(x)
    r <- deriv.order 
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (d>4) binned <- FALSE
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)

    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    ## use normal reference selector as initial condn
    Hnorm <- Hns(x=x, deriv.order=r)
    if (missing(Hstart)) Hstart <- Hnorm
    Hstart <- matrix.sqrt(Hstart)
    if (binned) bin.par <- binning(x=x, H=Hnorm, bgridsize=bgridsize)
    if (missing(trunc)) { if (deriv.order==0) trunc <- 1e10 else trunc <- 4 }

    lscv.init <- lscv.mat(x=x, H=Hnorm, binned=binned, bin.par=bin.par, deriv.order=r)
    lscv.mat.temp <- function(vechH)
    {
        H <- invvech(vechH) %*% invvech(vechH)
        lscv <- lscv.mat(x=x, H=H, binned=binned, bin.par=bin.par, deriv.order=r)
        if (det(H) < 1/trunc*det(Hnorm) | det(H) > trunc*det(Hnorm) | abs(lscv) > trunc*abs(lscv.init)) lscv <- lscv.init 
        return(lscv)  
    }

    if (optim.fun=="nlm")
    {
        result <- nlm(p=vech(Hstart), f=lscv.mat.temp, print.level=2*as.numeric(verbose))   
        H <- invvech(result$estimate) %*% invvech(result$estimate)
        amise.opt <- result$minimum
    }
    else if (optim.fun=="optim")
    {
        result <- optim(vech(Hstart), lscv.mat.temp, method="Nelder-Mead", control=list(trace=as.numeric(verbose), REPORT=1))     
        H <- invvech(result$par) %*% invvech(result$par)
        amise.opt <- result$value
    }

    if (!amise) return(H)
    else return(list(H=H, LSCV=amise.opt))
}

#############################################################################
## Finds the diagonal bandwidth matrix that minimises LSCV for 2 to 6 dim
## 
## Parameters
## x - data values
## Hstart - initial bandwidth matrix
##
## Returns
## H_LSCV,diag
#############################################################################
Hlscv.diag <- function(x, Hstart, binned=FALSE, bgridsize, amise=FALSE, deriv.order=0, verbose=FALSE, optim.fun="optim", trunc)
{
    if (any(duplicated(x))) warning("Data contain duplicated values: LSCV is not well-behaved in this case")
    if (!is.matrix(x)) x <- as.matrix(x)
    n <- nrow(x)
    d <- ncol(x)
    r <- deriv.order
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (d>4) binned <- FALSE
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    Hnorm <- Hns(x=x, deriv.order=r)
    if (missing(Hstart)) Hstart <- Hnorm

    ## don't truncate optimisation for deriv.order==0
    if (missing(trunc)) { if (deriv.order==0) trunc <- 1e10 else trunc <- 4 }

    ## linear binning
    if (binned)
    {
        bin.par <- binning(x=x, bgridsize=bgridsize, H=Hnorm)
    }

    lscv.init <- lscv.mat(x=x, H=Hnorm, binned=binned, bin.par=bin.par, deriv.order=r)

    lscv.mat.temp <- function(diagH)
    {
        H <- diag(diagH^2)
        lscv <- lscv.mat(x=x, H=H, binned=binned, bin.par=bin.par, deriv.order=r)
        if (det(H) < 1/trunc*det(Hnorm) | det(H) > trunc*det(Hnorm) | abs(lscv) > trunc*abs(lscv.init)) lscv <- lscv.init 
        return(lscv)  
    }

    Hstart <- matrix.sqrt(Hstart)
    if (optim.fun=="nlm")
    {
        result <- nlm(p=diag(Hstart), f=lscv.mat.temp, print.level=2*as.numeric(verbose))
        H <- diag(result$estimate^2)
        amise.opt <- result$minimum
    }
    else if (optim.fun=="optim")
    {
        result <- optim(diag(Hstart), lscv.mat.temp, method="Nelder-Mead", control=list(trace=as.numeric(verbose), REPORT=1))
        H <- diag(result$par^2)
        amise.opt <- result$value
    }

    if (!amise) return(H)
    else return(list(H=H, LSCV=amise.opt))
}

## aliases using "UCV" instead of "LSCV"

hucv <- function(...) { hlscv(...) }
Hucv <- function(...) { Hlscv(...) }
Hucv.diag <- function(...) { Hlscv.diag(...) }

#############################################################################
## Computes the biased cross validation BCV function for 2-dim
## 
## Parameters
## x - data values
## H1, H2 - bandwidth matrices
##
## Returns
## BCV(H)
#############################################################################
bcv.mat <- function(x, H1, H2, binned=FALSE)
{
    n <- nrow(x)
    d <- 2

    psi <- kfe(x, G=H2, deriv.order=4, add.index=TRUE, deriv.vec=TRUE, inc=0, binned=binned)
    psi40 <- psi$psir[1]
    psi31 <- psi$psir[2]
    psi22 <- psi$psir[4]
    psi13 <- psi$psir[8]
    psi04 <- psi$psir[16]

    coeff <- c(1, 2, 1, 2, 4, 2, 1, 2, 1)
    psi.fun <- c(psi40, psi31, psi22, psi31, psi22, psi13, psi22, psi13,psi04)/(n*(n-1))
    psi4.mat <- matrix(coeff * psi.fun, ncol=3, nrow=3)

    RK <- (4*pi)^(-d/2) 
    bcv <- drop(n^(-1)*det(H1)^(-1/2)*RK + 1/4*t(vech(H1)) %*% psi4.mat %*% vech(H1))

    return(list(bcv=bcv, psimat=psi4.mat))
}

#############################################################################
## Find the bandwidth matrix that minimises the BCV for 2-dim
## 
## Parameters
## x - data values
## whichbcv - 1 = BCV1
##          - 2 = BCV2 
## Hstart - initial bandwidth matrix
##
## Returns
## H_BCV
#############################################################################
Hbcv <- function(x, whichbcv=1, Hstart, binned=FALSE, amise=FALSE, verbose=FALSE)
{
    n <- nrow(x)
    d <- ncol(x)
    RK <- (4*pi)^(-d/2)
    if(!is.matrix(x)) x <- as.matrix(x)

    ## use normal reference b/w matrix for bounds
    k <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*n*gamma((d+8)/2)*(d+2)))^(2/(d+4))
    Hmax <- k * abs(var(x))
    up.bound <- Hmax
    if (missing(Hstart)) Hstart <- matrix.sqrt(0.9*Hmax)

    bcv.mat.temp <- function(vechH)
    {
        H <- invvech(vechH) %*% invvech(vechH)
        bcv <- bcv.mat(x=x, H1=H, H2=whichbcv*H, binned=binned)$bcv
        return(bcv)  
    }

    result <- optim(vech(Hstart), bcv.mat.temp, method="L-BFGS-B", upper=vech(matrix.sqrt(up.bound)), lower=-vech(matrix.sqrt(up.bound)), control=list(trace=as.numeric(verbose), REPORT=1))

    H <- invvech(result$par) %*% invvech(result$par)
    amise.opt <- result$value

    if (!amise) return(H)
    else return(list(H = H, BCV=amise.opt))
}

#############################################################################
## Find the diagonal bandwidth matrix that minimises the BCV for 2-dim
## 
## Parameters
## x - data values
## whichbcv - 1 = BCV1
##          - 2 = BCV2
## Hstart - initial bandwidth matrix
##
## Returns
## H_BCV, diag
#############################################################################
Hbcv.diag <- function(x, whichbcv=1, Hstart, binned=FALSE, amise=FALSE, verbose=FALSE)
{
    n <- nrow(x)
    d <- ncol(x)
    RK <- (4*pi)^(-d/2)
    if(!is.matrix(x)) x <- as.matrix(x)

    ## use maximally smoothed b/w matrix for bounds
    k <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*n*gamma((d+8)/2)*(d+2)))^(2/(d+4))
    Hmax <- k * abs(var(x))
    up.bound <- diag(Hmax)

    if (missing(Hstart)) Hstart <- 0.9*matrix.sqrt(Hmax)

    bcv.mat.temp <- function(diagH)
    {
        H <- diag(diagH) %*% diag(diagH) 
        return(bcv.mat(x, H, whichbcv*H, binned=binned)$bcv)
    } 

    result <- optim(diag(Hstart), bcv.mat.temp, method="L-BFGS-B", upper=sqrt(up.bound), control=list(trace=as.numeric(verbose), REPORT=1))
    H <- diag(result$par) %*% diag(result$par)
    amise.opt <- result$value
    
    if (!amise) return(H)
    else return(list(H = H, BCV=amise.opt))
}

#############################################################################
## Estimate scalar g_AMSE pilot bandwidth for SCV for 2 to 6 dim
##
## Parameters
## Sigma.star - scaled/ sphered variance matrix
## Hamise - (estimate) of H_AMISE 
## n - sample size
##
## Returns
## g_AMSE pilot bandwidth
#############################################################################
Theta6.elem <- function(d)
{
    Theta6.mat <- list()
    Theta6.mat[[d]] <- list()
    for (i in 1:d)
    Theta6.mat[[i]] <- list()

    for (i in 1:d)
    for (j in 1:d)
    {  
        temp <- numeric()
        for (k in 1:d)     
        for (ell in 1:d)    
        temp <- rbind(temp, elem(i,d)+2*elem(k,d)+2*elem(ell,d)+elem(j,d))

        Theta6.mat[[i]][[j]] <- temp
    }

    return(Theta6.mat)
}

gamse.scv <- function(x.star, d, Sigma.star, Hamise, n, binned=FALSE, bin.par, bgridsize, verbose=FALSE, nstage=1, Theta6=FALSE)
{
    if (nstage==0)
    {
        psihat6.star <- psins(r=6, Sigma=Sigma.star, deriv.vec=TRUE) 
    }
    else if (nstage==1)
    {  
        g6.star <- gsamse(Sigma.star, n, 6) 
        G6.star <- g6.star^2 * diag(d)
        if (Theta6) psihat6.star <- kfe(x=x.star, bin.par=bin.par, deriv.order=6, G=G6.star, deriv.vec=FALSE, add.index=FALSE, binned=binned, bgridsize=bgridsize, verbose=verbose)
        else psihat6.star <- kfe(x=x.star, bin.par=bin.par, deriv.order=6, G=G6.star, deriv.vec=TRUE, add.index=FALSE, binned=binned, bgridsize=bgridsize, verbose=verbose)
    }

    if (Theta6)
    {
        derivt6 <- dmvnorm.deriv(x=rep(0,d), deriv.order=6, add.index=TRUE, deriv.vec=FALSE, only.index=TRUE)
        Theta6.mat <- matrix(0, ncol=d, nrow=d)
        Theta6.mat.ind <- Theta6.elem(d)
        for (i in 1:d)
        for (j in 1:d)
        {
            temp <- Theta6.mat.ind[[i]][[j]]
            temp.sum <- 0
            for (k in 1:nrow(temp))
            temp.sum <- temp.sum + psihat6.star[which.mat(temp[k,], derivt6)]
            Theta6.mat[i,j] <- temp.sum 
        }

        eye3 <- diag(d)
        D4 <- dupl(d)$d
        trHamise <- tr(Hamise) 

        ## required constants - see thesis
        Cmu1 <- 1/2*t(D4) %*% vec(Theta6.mat %*% Hamise)
        Cmu2 <- 1/8*(4*pi)^(-d/2) * (2*t(D4)%*% vec(Hamise) + trHamise * t(D4) %*% vec(eye3))
        num <- 2 * (d+4) * sum(Cmu2*Cmu2)
        den <- -(d+2) * sum(Cmu1*Cmu2) + sqrt((d+2)^2 * sum(Cmu1*Cmu2)^2 + 8*(d+4)*sum(Cmu1*Cmu1) * sum(Cmu2*Cmu2))
        gamse <- (num/(den*n))^(1/(d+6)) 
    }
    else
    {  
        ## updated constants using Chacon & Duong (2010) notation
        Cmu1Cmu1 <- drop(1/4*psihat6.star %*% (Hamise %x% diag(d^4) %x% Hamise) %*% psihat6.star)
        Cmu1Cmu2 <- 3/4*(4*pi)^(-d/2)*drop(vec(Hamise %x% diag(d) %x% Hamise) %*% psihat6.star)
        Cmu2Cmu2 <- 1/64*(4*pi)^(-d)*(4*tr(Hamise%*%Hamise) + (d+8)*tr(Hamise)^2)
        num <- 2 * (d+4) * Cmu2Cmu2
        den <- -(d+2) * Cmu1Cmu2 + sqrt((d+2)^2 * Cmu1Cmu2^2 + 8*(d+4)*Cmu1Cmu1 * Cmu2Cmu2)
        gamse <- (num/(den*n))^(1/(d+6)) 
    }
    
    return(gamse)
}

#############################################################################
## Estimate unconstrained G_AMSE pilot bandwidth for SCV for 2 to 6 dim
## Code by J.E. Chacon
##
## Returns
## G_AMSE pilot bandwidth
#############################################################################
Gunconstr.scv <- function(x, binned=FALSE, bin.par, bgridsize, verbose=FALSE, nstage=1, optim.fun="optim")
{
    d <- ncol(x)
    n <- nrow(x)
    S <- var(x)
    S12 <- matrix.sqrt(S)
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    ## stage 1 of plug-in
    x.star <- pre.sphere(x)
    if (nstage==1)
    {  
        G6 <- Gns(r=6,n=n,Sigma=var(x.star), scv=TRUE) 
        psihat6 <- kfe(x=x.star, deriv.order=6, G=G6, deriv.vec=TRUE, add.index=FALSE, binned=binned, bgridsize=bgridsize, verbose=verbose)
    }
    else if (nstage==0)
    {
        psihat6 <- psins(r=6, Sigma=var(x.star), deriv.vec=TRUE) 
    }

    ## constants for normal reference
    D4phi0 <- DrL0(d=d,r=4)
    Id4 <- diag(d^4)

    ## asymptotic squared bias for r = 4
    AB2 <- function(vechG)
    {
        G <- invvech(vechG)%*%invvech(vechG)
        G12 <- matrix.sqrt(G)
        Ginv12 <- chol2inv(chol(G12))
        AB <- n^(-1)*det(Ginv12)*(Kpow(A=Ginv12,pow=4)%*%D4phi0)*2^(-(d+4)/2) + (t(vec(G))%x%Id4)%*%psihat6
        AB2.val <- sum(AB^2)
        return (AB2.val)
    }

    Gstart <- Gns(r=4,n=n,Sigma=S,scv=TRUE)
    Gstart <- matrix.sqrt(Gstart)
    if (optim.fun=="nlm")
    {
        result <- nlm(p=vech(Gstart), f=AB2, print.level=2*as.logical(verbose))    
        G4 <- result$estimate
    }
    else if (optim.fun=="optim")    
    { 
        result <- optim(vech(Gstart), AB2, method="BFGS", control=list(trace=as.numeric(verbose), REPORT=1))
        G4 <- result$par
    }   
    G4 <- invvech(G4)%*%invvech(G4)
    G4 <- S12 %*% G4 %*% S12

    return(G4) 
}

#############################################################################
## Computes the smoothed cross validation function for 2 to 6 dim
## 
## Parameters
## x - data values
## H - bandwidth matrix
## G - pilot bandwidth matrix
##
## Returns
## SCV(H)
#############################################################################
scv.1d <- function(x, h, g, binned=TRUE, bin.par, inc=1, deriv.order=0)
{
    r <- deriv.order
    if (!missing(x)) n <- length(x)
    if (!missing(bin.par)) n <- sum(bin.par$counts)
    scv1 <- kfe.1d(x=x, deriv.order=2*r, bin.par=bin.par, g=sqrt(2*h^2+2*g^2), binned=binned, inc=inc)
    scv2 <- kfe.1d(x=x, deriv.order=2*r, bin.par=bin.par, g=sqrt(h^2+2*g^2), binned=binned, inc=inc)
    scv3 <- kfe.1d(x=x, deriv.order=2*r, bin.par=bin.par, g=sqrt(2*g^2), binned=binned, inc=inc)

    bias2 <- (-1)^r*(scv1 - 2*scv2 + scv3)
    if (bias2 < 0) bias2 <- 0
    scv <- (n*h)^(-1)*(4*pi)^(-1/2)*2^(-r)*OF(2*r) + bias2

    return(scv)
}

scv.mat <- function(x, H, G, binned=FALSE, bin.par, bgridsize, verbose=FALSE, deriv.order=0)
{
    d <- ncol(x)
    n <- nrow(x)
    r <- deriv.order
    vId <- vec(diag(d))
    Hinv <- chol2inv(chol(H))

    if (!binned)
    {
        scv1 <- Qr(x=x, deriv.order=2*r, Sigma=2*H+2*G, inc=1)
        scv2 <- Qr(x=x, deriv.order=2*r, Sigma=H+2*G, inc=1)
        scv3 <- Qr(x=x, deriv.order=2*r, Sigma=2*G, inc=1)
        bias2 <- (-1)^r*(scv1 - 2*scv2 + scv3)
        if (bias2 < 0) bias2 <- 0
    }
    else
    {
        scv1 <- kfe(x=x, G=2*H + 2*G, deriv.order=2*r, inc=1, binned=binned, bin.par=bin.par, bgridsize=bgridsize, verbose=verbose, add.index=FALSE)
        scv2 <- kfe(x=x, G=H + 2*G, deriv.order=2*r, inc=1, binned=binned, bin.par=bin.par, bgridsize=bgridsize, verbose=verbose, add.index=FALSE)
        scv3 <- kfe(x=x, G=2*G, deriv.order=2*r, inc=1, binned=binned, bin.par=bin.par, bgridsize=bgridsize, verbose=verbose, add.index=FALSE)

        bias2 <- drop((-1)^r*Kpow(vId,r) %*% (scv1 - 2*scv2 + scv3))
        if (bias2 < 0) bias2 <- 0
    }
    scvmat <- 1/(det(H)^(1/2)*n)*nur(r=r, A=Hinv, mu=rep(0,d), Sigma=diag(d), type="direct")*2^(-d-r)*pi^(-d/2) + bias2
    
    return (scvmat)
}

#############################################################################
## Find the bandwidth that minimises the SCV for 1 to 6 dim
## 
## Parameters
## x - data values
## pre - "scale" - pre-scaled data
##     - "sphere"- pre-sphered data
## Hstart - initial bandwidth matrix
##
## Returns
## H_SCV
#############################################################################
hscv <- function(x, nstage=2, binned=TRUE, bgridsize, plot=FALSE)
{
    sigma <- sd(x)
    n <- length(x)
    d <- 1
    hnorm <- sqrt((4/(n*(d + 2)))^(2/(d + 4)) * var(x))
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)
    hmin <- 0.1*hnorm
    hmax <- 2*hnorm

    bin.par <- binning(x=x, bgridsize=bgridsize, h=hnorm)
    if (nstage==1)
    {
        psihat6 <- psins.1d(r=6, sigma=sigma)
        psihat10 <- psins.1d(r=10, sigma=sigma)
    }
    else if (nstage==2)
    {
        g1 <- (2/(7*n))^(1/9)*2^(1/2)*sigma
        g2 <- (2/(11*n))^(1/13)*2^(1/2)*sigma

        psihat6 <- kfe.1d(x=x, bin.par=bin.par, binned=binned, deriv.order=6, g=g1, inc=1)
        psihat10 <- kfe.1d(x=x, bin.par=bin.par, binned=binned, deriv.order=10, g=g2, inc=1)
    }

    g3 <- (-6/((2*pi)^(1/2)*psihat6*n))^(1/7) 
    g4 <- (-210/((2*pi)^(1/2)*psihat10*n))^(1/11)
    psihat4 <- kfe.1d(x=x, bin.par=bin.par, binned=binned, deriv.order=4, g=g3, inc=1)
    psihat8 <- kfe.1d(x=x, bin.par=bin.par, binned=binned, deriv.order=8, g=g4, inc=1)

    C <- (441/(64*pi))^(1/18) * (4*pi)^(-1/5) * psihat4^(-2/5) * psihat8^(-1/9)

    scv.1d.temp <- function(h)
    {
        return(scv.1d(x=x, bin.par=bin.par, h=h, g=C*n^(-23/45)*h^(-2), binned=binned, inc=1))
    }

    if (plot)
    {  
        hseq <- seq(hmin,hmax, length=400)
        hscv.seq <- rep(0, length=length(hseq))
        for (i in 1:length(hseq))
        hscv.seq[i] <- scv.1d.temp(hseq[i])
        plot(hseq, hscv.seq, type="l", xlab="h", ylab="SCV(h)")
    }

    opt <- optimise(f=scv.1d.temp, interval=c(hmin, hmax))$minimum
    if (n >= 1e5) warning("hscv is not always stable for large samples")

    return(opt)
}

Hscv <- function(x, nstage=2, pre="sphere", pilot, Hstart, binned, bgridsize, amise=FALSE, deriv.order=0, verbose=FALSE, optim.fun="optim")
{
    n <- nrow(x)
    d <- ncol(x)
    r <- deriv.order
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (d > 4) binned <- FALSE
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)
 
    if(!is.matrix(x)) x <- as.matrix(x)
    if (missing(pilot)) { if (d==2 & r==0) pilot <- "samse" else pilot <- "dscalar" }
    pilot1 <- match.arg(pilot, c("amse", "samse", "unconstr", "dunconstr", "dscalar"))
    pre1 <- match.arg(pre, c("scale", "sphere"))
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    if (pilot1=="amse" & (d>2 | r>0)) stop("amse pilot selectors not defined for d>2 and/or r>0")
    if ((pilot1=="samse" | pilot1=="unconstr") & r>0) stop("dscalar or dunconstr pilot selectors are better for deriv.order>0")

    if (pre1=="scale")
    {
        x.star <- pre.scale(x)
        S12 <- diag(sqrt(diag(var(x))))
        Sinv12 <- chol2inv(chol(S12))
    }
    else if (pre1=="sphere")
    {
        x.star <- pre.sphere(x)
        S12 <- matrix.sqrt(var(x))
        Sinv12 <- chol2inv(chol(S12))
    }

    RK <- (4*pi)^(-d/2)
    if (binned)
    {
        if (pilot1=="unconstr" | pilot1=="dunconstr")
        H.max <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x)
        else
        H.max <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x.star)
        if (default.bflag(d=d, n=n))
        bin.par <- binning(x=x.star, bgridsize=bgridsize, H=matrix.sqrt(H.max))
    }
    if (pilot1=="unconstr")
    {
        ## Gu pilot matrix is on data scale
        Gu <- Gunconstr.scv(x=x, binned=binned, bgridsize=bgridsize, verbose=verbose, nstage=nstage-1, optim.fun=optim.fun)
        if (missing(Hstart)) Hstart <- Hns(x=x, deriv.order=r)
    }
    else if (pilot1=="dunconstr")
    {
        ## Gu pilot matrix is on data scale
        Gu <- Gdunconstr(x=x, d=d, r=r, n=n, nstage=nstage, verbose=verbose, binned=binned, scv=TRUE, optim.fun=optim.fun)
        if (missing(Hstart)) Hstart <- Hns(x=x, deriv.order=r)
    }
    else if (pilot1=="dscalar")
    {
        ## Gs is on pre-transformed data scale
        g2r4 <- gdscalar(x=x.star, d=d, r=r, n=n, nstage=nstage, verbose=verbose, scv=TRUE, binned=binned)
        Gs <- g2r4^2*diag(d)
        if (missing(Hstart)) Hstart <- Hns(x=x.star, deriv.order=r)
    }
    else
    {
        ## Gs is on transformed data scale    
        Hamise <- Hpi(x=x.star, nstage=1, deriv.order=r, pilot=pilot, pre="sphere", binned=TRUE, bgridsize=bgridsize, verbose=verbose, optim.fun=optim.fun) 
        if (any(is.na(Hamise)))
        {
            warning("Pilot bandwidth matrix is NA - replaced with maximally smoothed")
            Hamise <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x.star)
        }
        gs <- gamse.scv(x.star=x.star, d=d, Sigma.star=var(x.star), Hamise=Hamise, n=n, binned=binned, bgridsize=bgridsize, verbose=verbose, nstage=nstage-1)
        Gs <- gs^2*diag(d)

        ## use normal reference bandwidth as initial condition 
        if (missing(Hstart)) Hstart <- Hns(x=x.star, deriv.order=r) 
        else Hstart <- Sinv12 %*% Hstart %*% Sinv12
    }

    ## SCV is estimate of AMISE
    scv.mat.temp <- function(vechH)
    {
        H <- invvech(vechH) %*% invvech(vechH)
        if (pilot1=="samse" | pilot1=="amse" | pilot1=="dscalar") { Gpilot <- Gs; xx <- x.star }
        else if (pilot1=="unconstr" | pilot1=="dunconstr") { Gpilot <- Gu; xx <- x }

        if (default.bflag(d=d, n=n))
        scvm <- scv.mat(x=xx, H=H, G=Gpilot, binned=binned, bin.par=bin.par, verbose=FALSE, deriv.order=r)
        else
        scvm <- scv.mat(x=xx, H=H, G=Gpilot, binned=binned, verbose=FALSE, deriv.order=r)
        return(scvm)
    }

    Hstart <- matrix.sqrt(Hstart)
    if (optim.fun=="nlm")
    {
        result <- nlm(p=vech(Hstart), f=scv.mat.temp, print.level=2*as.numeric(verbose))    
        H <- invvech(result$estimate) %*% invvech(result$estimate)
        amise.star <- result$minimum
    }
    else if (optim.fun=="optim")
    {
        result <- optim(vech(Hstart), scv.mat.temp, method="BFGS", control=list(trace=as.numeric(verbose), REPORT=1))
        H <- invvech(result$par) %*% invvech(result$par)
        amise.star <- result$value
    }
    if (!(pilot1 %in% c("dunconstr","unconstr")))  H <- S12 %*% H %*% S12   ## back-transform

    if (!amise) return(H)
    else return(list(H = H, SCV.star=amise.star))
}

Hscv.diag <- function(x, nstage=2, pre="scale", pilot, Hstart, binned, bgridsize, amise=FALSE, deriv.order=0, verbose=FALSE, optim.fun="optim")
{
    n <- nrow(x)
    d <- ncol(x)
    r <- deriv.order
    RK <- (4*pi)^(-d/2)
    if (missing(binned)) binned <- default.bflag(d=d,n=n)
    if (d > 4) binned <- FALSE
    if (missing(bgridsize)) bgridsize <- default.bgridsize(d)
    if(!is.matrix(x)) x <- as.matrix(x)

    if (missing(pilot)) { if (d==2 & r==0) pilot <- "samse" else pilot <- "dscalar" }
    pilot1 <- match.arg(pilot, c("amse", "samse", "unconstr", "dunconstr", "dscalar"))
    pre1 <- match.arg(pre, c("scale", "sphere"))
    optim.fun <- match.arg(optim.fun, c("nlm", "optim"))

    if (pilot1=="amse" & (d>2 | r>0)) stop("samse pilot selectors are better for higher dimensions and/or deriv.order>0")
    if (pilot1=="samse" & r>0) stop("dscalar pilot selectors are better for deriv.order>0")
    if (pilot1=="unconstr" | pilot1=="dunconstr") stop("Unconstrained pilot selectors are not suitable for Hscv.diag")

    if (pre1=="sphere") stop("Using pre-sphering doesn't give a diagonal bandwidth matrix")

    if (pre1=="scale")
    {
        x.star <- pre.scale(x)
        S12 <- diag(sqrt(diag(var(x))))
        Sinv12 <- chol2inv(chol(S12))
    }
    else if (pre1=="sphere")
    {
        x.star <- pre.sphere(x)
        S12 <- matrix.sqrt(var(x))
        Sinv12 <- chol2inv(chol(S12))
    }

    if (binned)
    {
        H.max <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x.star)
        bin.par.star <- binning(x=x.star, bgridsize=bgridsize, H=H.max) 
    }

    if (pilot1=="dscalar")
    {
        ## Gs is on pre-transformed data scale
        g2r4 <- gdscalar(x=x.star, r=r, n=n, d=d, verbose=verbose, nstage=nstage, scv=TRUE, binned=binned)
        Gs <- g2r4^2*diag(d)
        if (missing(Hstart)) Hstart <- Hns(x=x.star, deriv.order=r)
    }
    else
    {
          ## Gs is on transformed data scale
          Hamise <- Hpi(x=x.star, nstage=1, pilot=pilot, pre="sphere", binned=binned, bgridsize=bgridsize, verbose=verbose, optim.fun=optim.fun) 
          if (any(is.na(Hamise)))
          {
            warning("Pilot bandwidth matrix is NA - replaced with maximally smoothed")
            Hamise <- (((d+8)^((d+6)/2)*pi^(d/2)*RK)/(16*(d+2)*n*gamma(d/2+4)))^(2/(d+4))* var(x.star)
          }
          gs <- gamse.scv(x.star=x.star, d=d, Sigma.star=var(x.star), Hamise=Hamise, n=n, binned=binned, bgridsize=bgridsize, verbose=verbose, nstage=nstage-1)

          Gs <- gs^2*diag(d)

        ## use normal reference bandwidth as initial condition 
        if (missing(Hstart)) Hstart <- Hns(x=x.star, deriv.order=r)
        else Hstart <- Sinv12 %*% Hstart %*% Sinv12
    }

    scv.mat.temp <- function(diagH)
    {
        H <- diag(diagH) %*% diag(diagH)
        if (default.bflag(d=d, n=n)) scvm <- scv.mat(x.star, H, Gs, binned=binned, verbose=FALSE, bin.par=bin.par.star, deriv.order=r)
        else scvm <- scv.mat(x.star, H, Gs, binned=binned, verbose=FALSE, deriv.order=r)
        return(scvm)
    }

    Hstart <- matrix.sqrt(Hstart)
    if (optim.fun=="nlm")
    {
        result <- nlm(p=diag(Hstart), f=scv.mat.temp, print.level=2*as.numeric(verbose))    
        H <- diag(result$estimate) %*% diag(result$estimate)
        amise.star <- result$minimum
    }
    else if (optim.fun=="optim")
    {  
        result <- optim(diag(Hstart), scv.mat.temp, method="Nelder-Mead", control=list(trace=as.numeric(verbose), REPORT=1))
        H <- diag(result$par) %*% diag(result$par)
        amise.star <- result$value
    }
    ## back-transform
    H <- S12 %*% H %*% S12

    if (!amise) return(H)
    else return(list(H = H, SCV.star=amise.star))
}

############################################################################
## Normal scale selector H_ns for kernel density derivate estimators
############################################################################

Hns <- function(x, deriv.order=0)
{
    if (is.vector(x)) { n <- 1; d <- length(x) } 
    else { n <- nrow(x); d <- ncol(x) }
    r <- deriv.order
    H <- (4/(n*(d+2*r+2)))^(2/(d+2*r+4)) * var(x)
    return(H)
}

Hns.diag <- function(x)
{
    if (is.vector(x)) { n <- 1; d <- length(x) } 
    else { n <- nrow(x); d <- ncol(x) }
    S <- var(x)
    Delta <- chol2inv(chol(diag(diag(S))))%*%S
    svD <- svd(Delta)
    Deltainv <- svD$v %*% diag(1/svD$d) %*% t(svD$u)
    H <- ((4*d*det(Delta)^(1/2))/(2*tr(Deltainv%*%Deltainv) + (tr(Deltainv))^2))^(2/(d+4))*diag(diag(S))*n^(-2/(d+4))
    
    return(H)
}

hns <- function(x, deriv.order=0)
{
    n <- length(x)
    d <- 1
    r <- deriv.order
    h <- (4/(n*(d+2*r+2)))^(1/(d+2*r+4))*sd(x)

    return(h)
}

#######################################################################
## Normal scale G_ns for kernel functional estimators
## scv = flag for SCV bias annihiliation constant
#######################################################################

Gns <- function(r,n,Sigma,scv=FALSE)
{
    d <- ncol(Sigma)
    const <- ifelse(scv,1,2)
    G <- (2/((n*(d+r))))^(2/(d+r+2))*const*Sigma

    return(G)
}

Gns.search <- function(G, f, n=10)
{
    Gstart.vec <- numeric(n)
    for (i in 1:length(Gstart.vec)) 
    { 
        Gstart <- matrix.sqrt(i/length(Gstart.vec)*G)
        Gstart.vec[i] <- f(vech(Gstart))
    }
    i <- which.min(Gstart.vec)/length(Gstart.vec)
    Gstart <- i*G

    return(Gstart)
}

############################################################################
## Normal mixture selector 
############################################################################

Hnm <- function(x, deriv.order=0, G=1:9, subset.ind, mise.flag=FALSE, verbose=FALSE, ...)
{
    if (!requireNamespace("mclust", quietly=TRUE)) stop("Install the mclust package as it is required.", call.=FALSE)
    modelNames <- "VVV" 
    ## set local seed
    current.seed <- .Random.seed
    on.exit( { .Random.seed <<- current.seed } )
    set.seed(8192)

    if (!missing(subset.ind)) nmixt.fit <- mclust::Mclust(x[subset.ind,], G=G, verbose=verbose, modelNames=modelNames, ...)
    else nmixt.fit <- mclust::Mclust(x, G=G, verbose=verbose, modelNames=modelNames, ...)

    if (is.vector(x)) { d <- length(x); n <- 1 } else { d <- ncol(x); n <- nrow(x) } 
    mus <- t(nmixt.fit$parameters$mean)
    Sigmas <- matrix(nmixt.fit$parameters$variance$sigma, byrow=TRUE, ncol=d)
    props <- nmixt.fit$parameters$pro
    if (mise.flag) H.nm <- Hmise.mixt(samp=n, mus=mus, Sigmas=Sigmas, props=props, deriv.order=deriv.order)
    else H.nm <- Hamise.mixt(samp=n, mus=mus, Sigmas=Sigmas, props=props, deriv.order=deriv.order)

    return(H.nm)
}

Hnm.diag <- function(x, deriv.order=0, G=1:9, subset.ind, mise.flag=FALSE, verbose=FALSE, ...)
{
    if (!requireNamespace("mclust", quietly=TRUE)) stop("Install the mclust package as it is required.", call.=FALSE)
    modelNames <- "VVI" 
    if (!missing(subset.ind)) nmixt.fit <- mclust::Mclust(x[subset.ind,], G=G, verbose=verbose, modelNames=modelNames, ...)
    else nmixt.fit <- mclust::Mclust(x, G=G, verbose=verbose, modelNames=modelNames, ...)

    if (is.vector(x)) { d <- length(x); n <- 1 } else { d <- ncol(x); n <- nrow(x) } 
    mus <- t(nmixt.fit$parameters$mean)
    Sigmas <- matrix(nmixt.fit$parameters$variance$sigma, byrow=TRUE, ncol=d)
    props <- nmixt.fit$parameters$pro
    if (mise.flag) H.nm <- Hmise.mixt.diag(samp=n, mus=mus, Sigmas=Sigmas, props=props, deriv.order=deriv.order)
    else H.nm <- Hamise.mixt.diag(samp=n, mus=mus, Sigmas=Sigmas, props=props, deriv.order=deriv.order)

    return(H.nm)
}

hnm <- function(x, deriv.order=0, G=1:9, subset.ind, mise.flag=FALSE, verbose=FALSE, ...)
{
    if (!missing(subset.ind)) nmixt.fit <- mclust::Mclust(x[subset.ind], G=G, verbose=verbose, ...)
    else nmixt.fit <- mclust::Mclust(x, G=G, verbose=verbose, ...)

    mus <- nmixt.fit$parameters$mean
    sigmas <- sqrt(nmixt.fit$parameters$variance$sigma)
    props <- nmixt.fit$parameters$pro
    n <- length(x)
    if (mise.flag) h.nm <- hmise.mixt(samp=n, mus=mus, sigmas=sigmas, props=props, deriv.order=deriv.order)
    else h.nm <- hamise.mixt(samp=n, mus=mus, sigmas=sigmas, props=props, deriv.order=deriv.order)

    return(h.nm)
}
