R/sn-funct.R

Defines functions spread.grouped mqCauchy mmWeibull plot.fitdistr.grouped print.fitdistr.grouped summary.fitdistr.grouped fitted.fitdistr.grouped logLik.fitdistr.grouped vcov.fitdistr.grouped coef.fitdistr.grouped logL.grouped pprodn2 qprodt2 pprodt2 mst.prelimFit st.prelimFit galton2alpha galton_moors2alpha_nu fournum plot2D.SymmModulated rmSymmModulated dmSymmModulated rSymmModulated dSymmModulated confint.selm predict.selm seqLog constrained.logLik discreteSpiral profile.selm vcov.SECdistrMv sd.SECdistrUv mean.SECdistrMv mean.SECdistrUv sd.default sd extractSECdistr coef.mselm coef.selm op2dp dp2op print.summary.mselm print.summary.selm plot.selm plot.SECdistrBv plot.SECdistrMv plot.SECdistrUv vech2mat vech force.symmetry dplist2optpar optpar2dplist msn.pdev msn.mple MPpenalty Qpenalty sn.pdev.hessian sn.pdev.gh sn.pdev sn.mple st.infoMv mst.logL mst.vdp2vcp mst.theta.jacobian mst.pdev.grad mst.pdev param.names st.infoUv st.pdev.hessian st.pdev.gh st.pdev st.mple msn.moment.fit msn.dev.grad msn.dev msn.mle sn.infoMv sn.infoUv weights.mselm fitted.mselm residuals.mselm summary.mselm weights.selm fitted.selm residuals.selm summary.selm selm.fit delta.etc conditionalSECdistr marginalSECdistr affineTransSECdistr mst.cp2dp st.cp2dp st.gamma2 st.gamma1 b st.dp2cp msn.cp2dp cp2dpMv cp2dpUv cp2dp mst.mardia mst.dp2cp msn.dp2cp dp2cpMv dp2cpUv dp2cp summary.SECdistrMv modeSECdistrMv modeSECdistrUv modeSECdistr summary.SECdistrUv makeSECdistr .Owen st.cumulants zeta sn.cumulants rsc qsc psc dsc rmsc pmsc dmsc pmst rmst dmst qst_bounds st_tails rmsn pmsn dmsn qsn psn dsn

Documented in affineTransSECdistr coef.fitdistr.grouped coef.mselm coef.selm conditionalSECdistr confint.selm cp2dp dmsc dmsn dmst dmSymmModulated dp2cp dp2op dsc dsn dSymmModulated extractSECdistr fitted.fitdistr.grouped fitted.mselm fitted.selm fournum galton2alpha galton_moors2alpha_nu logLik.fitdistr.grouped makeSECdistr marginalSECdistr modeSECdistr MPpenalty msn.mle msn.mple mst.prelimFit op2dp plot2D.SymmModulated plot.fitdistr.grouped plot.fitdistr.grouped plot.SECdistrMv plot.SECdistrUv plot.selm pmsc pmsn pmst pprodn2 pprodt2 predict.selm print.fitdistr.grouped profile.selm psc psn Qpenalty qprodt2 qsc qsn residuals.mselm residuals.selm rmsc rmsn rmst rmSymmModulated rsc rSymmModulated sd sd.default selm.fit sn.cumulants sn.infoMv sn.infoUv sn.mple spread.grouped st.cumulants st.infoMv st.infoUv st.mple st.prelimFit summary.fitdistr.grouped summary.mselm summary.SECdistrMv summary.SECdistrUv summary.selm vcov.fitdistr.grouped vech vech2mat zeta

#  file sn/R/sn-funct.R  (various functions)
#  This file is a component of the R package 'sn' 
#  copyright (C) 1997-2020 Adelchi Azzalini
# 
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 2 or 3 of the License
#  (at your option).
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  A copy of the GNU General Public License is available at
#  http://www.r-project.org/Licenses/

#---------
dsn <- function(x, xi=0, omega=1, alpha=0, tau=0, dp=NULL, log=FALSE)
{
  if(!is.null(dp)) {
    if(!missing(alpha)) 
      stop("You cannot set both 'dp' and component parameters") 
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    tau <- if(length(dp) > 3) dp[4] else 0
    }
  za <- cbind((x-xi)/omega, alpha)
  z <- za[,1]
  alpha <- za[,2]
  logN <- (-log(sqrt(2*pi)) -logb(omega) - z^2/2)
  logS <- numeric(length(z))
  ok <- (abs(alpha) < Inf)   
  logS[ok] <- pnorm(tau * sqrt(1+alpha[ok]^2) + (alpha*z)[ok], log.p=TRUE)
  logS[!ok]  <- log(as.numeric((sign(alpha)*z)[!ok] + tau > 0)) 
  logPDF <- as.numeric(logN + logS - pnorm(tau, log.p=TRUE))
  logPDF <- replace(logPDF, abs(x) == Inf, -Inf)
  logPDF <- replace(logPDF, omega <= 0, NaN)
  out <- if(log) logPDF else exp(logPDF)
  names(out) <- names(x)
  return(out)
}

psn <- function(x, xi=0, omega=1, alpha=0, tau=0, dp=NULL, engine, ...)
{
  if(!is.null(dp)) {
    if(!missing(alpha)) 
      stop("You cannot set both 'dp' and component parameters")
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    tau <- if(length(dp)>3) dp[4] else 0
   }
  z <- (x-xi)/omega
  prob <- rep(NA, length(z))
  plain <- is.finite(z) & (omega > 0)
  if(any(!plain)) {
    prob <- replace(prob, z==-Inf, 0)
    prob <- replace(prob, z==Inf, 1) 
    prob <- replace(prob, is.na(z) | (omega <= 0), NA)
    } 
  if(sum(plain) == 0) return(prob)  
  na <- length(alpha)
  za <- matrix(cbind(z, alpha), ncol=2)[plain,,drop=FALSE]
  z <- za[,1]  # z re-defined here
  nz <- length(z)
  if(missing(engine)) engine <- 
    if(na == 1 & nz > 3 & all(z*za[,2] > -5) & (tau == 0)) 
      "T.Owen" else "biv.nt.prob"
  if(engine == "T.Owen") {
    if(tau != 0 | na > 1) 
      stop("engine='T.Owen' not compatible with other arguments")
    p <- pnorm(z) - 2 * T.Owen(z, alpha, ...)
    }
  else{ #  engine="biv.nt.prob"
    p <- numeric(nz)
    alpha <- za[,2]
    delta <- delta.etc(alpha)
    p.tau <- pnorm(tau) 
    for(k in seq_len(nz)) {
      if(abs(z[k])==Inf) p[k] <- (sign(z[k]) + 1)/2
      else {
      if(abs(alpha[k]) == Inf){
        p[k] <- if(alpha[k] > 0) (pnorm(pmax(z[k], -tau)) - pnorm(-tau))/p.tau
                        else {1 - (pnorm(tau) - pnorm(pmin(z[k], tau)))/p.tau}
      }
    else { # SNbook: (2.48), p.40
      R <- matrix(c(1, -delta[k], -delta[k], 1), 2, 2)
      p[k]<- mnormt::biv.nt.prob(0, rep(-Inf,2), c(z[k], tau), c(0, 0), R)/p.tau
      }}
    }}
  p <- pmin(1, pmax(0, as.numeric(p)))
  names(prob) <- names(x)
  replace(prob, plain, p)
}

#
qsn <- function(p, xi = 0, omega = 1, alpha = 0, tau=0, dp=NULL, tol = 1e-08, 
         solver="NR", ...)            
{ if(!is.null(dp)) {
    if(!missing(alpha)) 
        stop("You cannot set both 'dp' and component parameters")
      xi <- dp[1]
      omega <- dp[2]
      alpha <- dp[3]
      tau <- if(length(dp) > 3) dp[4] else 0
      }
  if(omega <= 0) stop("argument 'omega' (or dp[2]) must be positive")    
  # p <- as.vector(p)    
  max.q <- sqrt(qchisq(p, 1)) + tau
  min.q <- -sqrt(qchisq(1-p, 1)) + tau
  if(tau == 0) {
    if(alpha == Inf)  return(xi + omega * max.q)
    if(alpha == -Inf) return(xi + omega * min.q)
    }
  na <- is.na(p) | (p < 0) | (p > 1)
  zero <- (p == 0)
  one <- (p == 1)
  ok <- !(na | zero | one)
  q.all <- numeric(length(p))
  names(q.all) <- names(p)
  q.all <- replace(q.all, na, NA)
  q.all <- replace(q.all, zero, -Inf)
  q.all <- replace(q.all, one, Inf)
  if(sum(ok) == 0) return(q.all)
  p <- p[ok]                                            # can drop cases not-OK
  dp0 <- c(0, 1, alpha, tau)
  if(solver == "NR") {
    dp0 <- c(0, 1, alpha, tau)
    cum <- sn.cumulants(dp=dp0, n=4)
    g1 <- cum[3]/cum[2]^(3/2)
    g2 <- cum[4]/cum[2]^2
    x <- qnorm(p)
    x <- (x + (x^2 - 1) * g1/6 + x * (x^2 - 3) * g2/24 -
          x * (2 * x^2 - 5) * g1^2/36)
    x <- cum[1] + sqrt(cum[2]) * x
    px <- psn(x, dp=dp0, ...)
    max.err <- 1
    while (max.err > tol) { # cat("qsn:", x, "\n")
      # cat('x, px:', format(c(x,px)),"\n")
      x1 <- x - (px - p)/dsn(x, dp=dp0)
      # x1 <- pmin(x1,max.q)
      # x1 <- pmax(x1,min.q)
      x <- x1
      px <- psn(x, dp=dp0, ...)
      max.err <- max(abs(px-p))
      if(is.na(max.err)) stop('failed convergence, try with solver="RFB"')
    }  
    q <- as.numeric(xi + omega * x)
  } else { if(solver == "RFB") {
	  abs.alpha <- abs(alpha)
	  if(alpha < 0) p <- (1-p)
	  x <- xa <- xb <- xc <- fa <- fb <- fc <- rep(NA, length(p))
	  nc <- rep(TRUE, length(p))                        # not converged (yet)
	  # nc[(na| zero| one)] <- FALSE
	  fc[!nc] <- 0
	  xa[nc] <- qnorm(p[nc])
	  xb[nc] <- sqrt(qchisq(p[nc], 1)) + abs(tau) 
	  fa[nc] <- psn(xa[nc], 0, 1, abs.alpha, tau, ...) - p[nc]
	  fb[nc] <- psn(xb[nc], 0, 1, abs.alpha, tau, ...) - p[nc]
	  regula.falsi <- FALSE
	  while (sum(nc) > 0) { # alternate regula falsi/bisection
		xc[nc] <- if(regula.falsi) 
		   xb[nc] - fb[nc] * (xb[nc] - xa[nc])/(fb[nc] - fa[nc])    else
		   (xb[nc] + xa[nc])/2
		fc[nc] <- psn(xc[nc], 0, 1, abs.alpha, tau, ...) - p[nc]
		pos <- (fc[nc] > 0)
		xa[nc][!pos] <- xc[nc][!pos]
		fa[nc][!pos] <- fc[nc][!pos]
		xb[nc][pos] <- xc[nc][pos]
		fb[nc][pos] <- fc[nc][pos]
		x[nc] <- xc[nc]
		nc[(abs(fc) < tol)] <- FALSE
		regula.falsi <- !regula.falsi 
		}	 
    Sign <- function(x) sign(x) + as.numeric(x==0)
    q <- as.numeric(xi + omega * Sign(alpha)* x)		
  } else stop("unknown solver")}
  q.all[ok] <- q
  names(q.all) <- names(q)
  return(q.all)
}
  
rsn <- function (n = 1, xi = 0, omega = 1, alpha = 0, tau = 0, dp = NULL) 
{# since version 1.6-2 (2020): use transformation/additive method throughout
  if (!is.null(dp)) {
    if (!missing(alpha)) 
        stop("You cannot set both 'dp' and the component parameters")
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    tau <- if (length(dp) > 3) dp[4] else 0
  }  
  delta <- alpha/sqrt(1 + alpha^2)
  if(tau == 0) {
    tn <- matrix(rnorm(2*n), 2, n,  byrow = FALSE)
    chi <- c(abs(tn[1,]))
    nrv <- c(tn[2,])
    z <- delta * chi + sqrt(1 - delta^2) * nrv
  } else {
    # rs <<- .Random.seed
    truncN <- qnorm(runif(n, min= pnorm(-tau), max=1))
    # .Random.seed <<- rs
    z <- delta * truncN + sqrt(1-delta^2) * rnorm(n)
  }
  y <- as.vector(xi + omega * z)
  attr(y, "family") <- "SN"
  attr(y, "parameters") <- c(xi, omega, alpha, tau)
  return(y)
}

dmsn <- function(x, xi=rep(0,length(alpha)), Omega, alpha,
                 tau=0, dp=NULL, log=FALSE)
{
    if(!(missing(alpha) & missing(Omega)) && !is.null(dp)) 
       stop("You cannot set both component parameters and dp")
    if(!is.null(dp)){
      if(length(dp) < 3) stop("wrong length of non-null 'dp'")
      xi <- drop(dp[[1]])
      Omega <- dp[[2]]
      alpha <- dp[[3]]
      tau <- if(length(dp) == 4) dp[[4]] else 0
    }
    if(any(abs(alpha) == Inf)) stop("Inf's in alpha are not allowed")
    d <- length(alpha)
    Omega <- matrix(Omega,d,d)
    invOmega <- pd.solve(Omega, silent=TRUE, log.det=TRUE)
    if (is.null(invOmega))  stop("Omega matrix is not positive definite")
    logDet <- attr(invOmega, "log.det")
    x <- if(is.vector(x)) matrix(x, 1, d) else data.matrix(x) 
    if (is.vector(xi)) xi <- outer(rep(1, nrow(x)), as.vector(matrix(xi,1,d)))
    if(tau == 0){
      log.const <- logb(2)
      alpha0 <- 0
      }
    else {
      log.const <- -pnorm(tau, log.p=TRUE)
      O.alpha <- cov2cor(Omega) %*% alpha
      alpha0 <- tau*sqrt(1+sum(alpha* O.alpha))
      }
    X <- t(x - xi)
    # Q <- apply((invOmega %*% X) * X, 2, sum)
    Q <- colSums((invOmega %*% X) * X)
    L <- alpha0 + as.vector(t(X/sqrt(diag(Omega))) %*% as.matrix(alpha))
    logPDF <- (log.const - 0.5 * Q + pnorm(L, log.p = TRUE)
               - 0.5 * (d * logb(2 * pi) + logDet))
    if (log) logPDF
    else exp(logPDF)
}

pmsn <- function(x, xi=rep(0,length(alpha)), Omega, alpha, tau=0, 
                 dp=NULL, ...)
{
  if(!(missing(alpha) & missing(Omega)) && !is.null(dp)) 
       stop("You cannot set both component parameters and dp")
  if(!is.null(dp)){
    xi <- dp$xi
    Omega <- dp$Omega
    alpha <- dp$alpha
    tau <- if(is.null(dp$tau)) 0 else dp$tau
    }
  if(any(abs(alpha) == Inf)) stop("Inf's in alpha are not allowed")
  d <- length(alpha)
  Omega <- matrix(Omega, d, d) 
  omega <- sqrt(diag(Omega))
  if(d == 1) return(psn(x, xi, omega, alpha, tau)) # 2018-05-02
  delta_etc <- delta.etc(alpha, Omega)
  delta <- delta_etc$delta
  Ocor <- delta_etc$Omega.cor
  Obig <- matrix(rbind(c(1,-delta), cbind(-delta,Ocor)), d+1, d+1)
  x <- if (is.vector(x)) matrix(x, 1, d) else data.matrix(x)
  if (is.vector(xi)) xi <- outer(rep(1, nrow(x)), as.vector(matrix(xi,1,d)))
  z0 <- cbind(tau, t(t(x - xi))/omega) 
  mnormt::pmnorm(z0, mean=rep(0,d+1), varcov=Obig, ...)/pnorm(tau) 
}
 
rmsn <- function(n=1, xi=rep(0,length(alpha)), Omega, alpha, tau=0, dp=NULL)
{# generates SN_d(..) variates using the additive (=transformation) method
  # if(!(missing(alpha) & missing(Omega) & !is.null(dp)))
  #     stop("You cannot set both component parameters and dp")
  if(!is.null(dp)) {  
     dp0 <- dp  
     dp0$nu <- NULL
     if(is.null(dp0$tau)) dp0$tau <- 0 
     if(names(dp)[1] == "beta") {
        dp0[[1]] <- as.vector(dp[[1]])
        names(dp0)[1] <- "xi"
        } 
     }
  else dp0 <- list(xi=xi, Omega=Omega, alpha=alpha, tau=tau)
  if(any(is.infinite(dp0$alpha))) stop("Inf's in alpha are not allowed")
  d <- length(dp0$alpha)
  if(d == 1) {
    dp1 <- unlist(dp0)
    dp1[2] <- sqrt(dp1[2])
    y <- matrix(rsn(n, dp=dp1), ncol=1)
  } else { 
    lot <- dp2cpMv(dp=dp0, family="SN", aux=TRUE)
    y <- matrix(rnorm(n*d), n, d) %*% chol(lot$aux$Psi) # N_d(0,Psi)
    if(dp0$tau == 0)    
      truncN <- abs(rnorm(n))  
    else 
      truncN <- qnorm(runif(n, min=pnorm(-dp0$tau), max=1))
    truncN <- matrix(rep(truncN, d), ncol=d)
    delta  <- lot$aux$delta
    z <- delta * t(truncN) + sqrt(1-delta^2) * t(y)
    y <- t(dp0$xi + lot$aux$omega * z)
  }
  attr(y, "family") <- "SN"
  attr(y, "parameters") <- dp0
  return(y)
}

#---

dst <-  function (x, xi=0, omega=1, alpha=0, nu=Inf, dp=NULL, log=FALSE)
{ 
  if(!is.null(dp)) {
     if(!missing(alpha)) 
        stop("You cannot set both the component parameters and dp")
     xi <- dp[1]
     omega <- dp[2]
     alpha <- dp[3]
     nu <- dp[4]
    }
  if (nu == Inf) return(dsn(x, xi, omega, alpha, log=log))
  if (nu == 1) return(dsc(x, xi, omega, alpha, log=log))
  if (nu <= 0) stop("'nu' must be positive")
  za <- cbind((x-xi)/omega, omega, alpha)
  z <- za[,1]
  omega <- za[,2]
  alpha <- za[,3]
  ok <- (omega>0)
  pdf <- ifelse(ok, dt(z, df=nu, log=log), NaN)
  cdf <- ifelse(ok, pt(alpha*z*sqrt((nu+1)/(z^2+nu)), df=nu+1, log.p=log), NaN)
  out <- if(log) logb(2) + pdf + cdf -logb(omega)
           else 2 * pdf * cdf / omega
  names(out) <- names(x)       
  return(out)  
}

rst <- function (n=1, xi = 0, omega = 1, alpha = 0, nu=Inf, dp=NULL)
{ 
    if(!is.null(dp)) {
     if(!missing(alpha)) 
        stop("You cannot set both 'dp' and the component parameters")
     xi <- dp[1]
     omega <- dp[2]
     alpha <- dp[3]
     nu <- dp[4]
    }
  # rs <<- .Random.seed
  z <- rsn(n, 0, omega, alpha)
  if(nu < Inf) {  
    # .Random.seed <<- rs  
    v <- rchisq(n,nu)/nu
    y <- z/sqrt(v) + xi
    }
    else y <- z + xi
  attr(y, "family") <- "ST"
  attr(y, "parameters") <- c(xi, omega, alpha, nu)
  return(y)
}

pst <- function (x, xi=0, omega=1, alpha=0, nu=Inf, dp=NULL, method=0, 
                lower.tail=TRUE, log.p=FALSE, ...) 
{     
  if(!is.null(dp)) {
    if(!missing(alpha)) 
      stop("You cannot set both component parameters and dp")
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    nu <- dp[4]
   }
  if(length(alpha) > 1) stop("'alpha' must be a single value")  
  if(length(nu) > 1) stop("'nu' must be a single value") 
  if(nu <= 0)  stop("'nu' must be positive") 
  dp.std <- c(0, 1, alpha, nu) 
  delta <- alpha/sqrt(1+alpha^2)
  if (nu == Inf) return(psn(x, xi, omega, alpha))
  if (nu == 1) return(psc(x, xi, omega, alpha))
  int.nu <- (round(nu) == nu)
  if(method<0 | method>5 | method != round(method)) stop("invalid 'method' value")
  if((method == 1 | method ==4) & !int.nu) 
    stop("selected 'method' does not work for non-integer nu")
  z <- (x-xi)/omega
  pr <- rep(NA, length(z))
  ok <- !(is.na(z) | (z==Inf) | (z==-Inf) | (omega<=0))
  z <- z[ok]
  nu0 <- (8.2 + 3.55* log(log(length(z)+1)))
  if(alpha == 0) p <- pt(z, df=nu) 
  else if(abs(alpha) == Inf) {
    z0 <- replace(z, alpha*z < 0, 0)
    p <- pf(z0^2, 1, nu)
    if(alpha < 0) p <- (1-p)
    }  
  else {  
  fp <- function(v, alpha, nu, t.value) 
          psn(sqrt(v) * t.value, 0, 1, alpha) * dchisq(v * nu, nu) * nu  
  if(method == 4 || (method==0  && int.nu &&  (nu <= nu0))) {
    # method 4 (recursive formula, for integer nu)
    p. <- pst_int(z, 0, 1, alpha, nu) 
    p <- if(lower.tail) p. else 1-p.
    p <- if(log.p) log(p) else p
    }
  else  {
    p <- numeric(length(z))
    for (i in seq_len(length(z))) {
      if(abs(z[i]) == Inf)  p[i] <- (1 + sign(z[i]))/2
      if(method==5 | method==0 & abs(z[i])> (30+1/sqrt(nu))) {
        lp <- st_tails(z[i], alpha, nu, lower.tail=lower.tail)
        # lp <- if(z[i]<0) lp else log(1-exp(lp))
        # p[i] <- if(log.p) lp else exp(lp) 
        p[i] <- if(log.p) {if(z[i]<0) lp else log(1-exp(lp))} 
                 else {if(z[i]<0) exp(lp) else 1-exp(lp)}
        }
      else {      
        if(method==1 || (method==0  && int.nu &&  (nu > nu0))) { # method 1
        out <- try(pmst(z[i], 0, matrix(1,1,1), alpha, nu, ...), silent=TRUE) 
        p. <- if(inherits(out, "try-error")) NA else out
        ## p[i] <- if(lower.tail) p. else 1-p.
        ## p[i] <- if(log.p) log(p[i]) else max(0, min(1, p[i]))
        }
      else {
      # upper <- if(absalpha> 1) 5/absalpha + 25/(absalpha*nu) else 5+25/nu
      upper <- 10 + 50/nu
      if(method==2 || (method==0 & (z[i] < upper) )) 
        {# method 2         
         p0 <- acos(delta)/pi  # CDF at x=0 
         int <- integrate(dst, min(0,z[i]), max(0,z[i]), dp=dp.std, stop.on.error=FALSE, ...) 
         p. <- p0 + sign(z[i]) * int$value
         ## p[i] <- if(lower.tail) p. else 1-p.
         ## p[i] <- if(log.p) log(p[i]) else max(0, min(1, p[i]))
        } 
        else {# method 3 
          p. <- integrate(fp, 0, Inf, alpha, nu, z[i], stop.on.error=FALSE, ...)$value  
          ## p[i] <- if(lower.tail) p. else 1-p.
          ## p[i] <- if(log.p) log(p[i]) else  max(0, min(1, p[i]))
          }         
        }
       p[i] <- if(lower.tail) p. else 1-p.
       p[i] <- if(log.p) log(p[i]) else max(0, min(1, p[i]))
      }}}}
  pr[ok] <- p
  pr[x == Inf] <- if(log.p) 0 else 1
  pr[x == -Inf] <- if(log.p) -Inf else 0
  pr[omega <= 0] <- NaN
  names(pr) <- names(x)
  return(pr)
}

st_tails <- function(x, alpha, nu, lower.tail=TRUE, threshold=20) 
{
 # log-probabilities of ST tails, using Azzalini & Capitanio (2014, top p.122):
 # (upper prob if x>threshold, lower prob if x< -threshold,  NA otherwise).
  if(length(alpha) > 1) stop("alpha must be a scalar value") 
  if(length(nu) > 1) stop("nu must be a scalar value")    
  pos <- (x > threshold)
  neg <- (x < -threshold)  
  lp <- rep(NA, length(x))  # will collect log-probabilities
  if(alpha >= 0) {
    log.c <- (log(2) + lgamma((nu+1)/2) + (nu/2)*log(nu) + 
              pt(c(-1,1)*alpha*sqrt(nu+1), nu+1, log.p=TRUE)
              -lgamma(nu/2) - 0.5*log(pi)  )  
    lp <- replace(lp, neg, log.c[1] -log(nu)- nu*log(-x[neg])) # lower tail           
    lp <- replace(lp, pos, log.c[2]-log(nu)- nu*log(x[pos])) # upper tail 
    }  
  else lp <- st_tails(-x, -alpha, nu)
  return(lp)
  }
  
pst_int <- function (x, xi=0, omega=1, alpha=0, nu=Inf) 
{# Jamalizadeh, Khosravi and Balakrishnan (2009, CSDA)
  if(nu != round(nu) | nu < 1) stop("'nu' is not a positive integer")
  if(omega <= 0) return(NaN)
  z <- (x-xi)/omega
  if(nu == 1) 
    atan(z)/pi + acos(alpha/sqrt((1+alpha^2)*(1+z^2)))/pi
    else { if(nu==2)
      0.5 - atan(alpha)/pi + (0.5 + atan(z*alpha/sqrt(2+z^2))/pi)*z/sqrt(2+z^2)
    else
      (pst_int(sqrt((nu-2)/nu)*z, 0, 1, alpha, nu-2) + 
        pst_int(sqrt(nu-1)*alpha*z/sqrt(nu+z^2), 0, 1, 0, nu-1) * z *
        exp(lgamma((nu-1)/2) +(nu/2-1)*log(nu)-0.5*log(pi)-lgamma(nu/2)
        -0.5*(nu-1)*log(nu+z^2)))
    } 
}


qst <- function (p, xi = 0, omega = 1, alpha = 0, nu=Inf, tol = 1e-8, 
                 dp = NULL, method=0, ...)
{
  if(!is.null(dp)) {
    if(!missing(alpha)) 
      stop("You cannot set both component parameters and 'dp'")
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    nu <- dp[4]
    }    
  if(length(alpha) > 1) stop("'alpha' must be a single value")  
  if(length(nu) > 1) stop("'nu' must be a single value")  
  if(nu <= 0) stop("'nu' must be non-negative")  
  if(nu > 1e4) return(qsn(p, xi, omega, alpha))
  if(nu == 1) return(qsc(p, xi, omega, alpha))
  if(alpha == Inf) 
    return(xi + omega * sqrt(qf(p, 1, nu)))
  if(alpha == -Inf) 
    return(xi - omega * sqrt(qf(1 - p, 1, nu)))
  # if(some.unknown.rule)  message(
  #  "Running qst with small nu and high/low p can be numerically problematic")    
  na <- is.na(p) | (p < 0) | (p > 1)
  abs.alpha <- abs(alpha)
  if(alpha < 0) p <- (1-p)
  zero <- (p == 0)
  one <- (p == 1)
  x <- xa <- xb <- xc <- fa <- fb <- fc <- rep(NA, length(p))
  nc <- rep(TRUE, length(p)) # not converged (yet)
  nc[(na| zero| one)] <- FALSE
  fc[!nc] <- 0
  bounds <- qst_bounds(p[nc], abs.alpha, nu)
  xa[nc] <- bounds[,"lower"]
  xb[nc] <- bounds[,"upper"]
  fa[nc] <- pst(xa[nc], 0, 1, abs.alpha, nu, method=method, ...) - p[nc]
  fb[nc] <- pst(xb[nc], 0, 1, abs.alpha, nu, method=method, ...) - p[nc]
  regula.falsi <- FALSE 
  while (sum(nc) > 0) { # alternate bisection/regula falsi
    xc[nc] <- if(regula.falsi) 
       xb[nc] - fb[nc] * (xb[nc] - xa[nc])/(fb[nc] - fa[nc])    else
       (xb[nc] + xa[nc])/2      
    fc[nc] <- pst(xc[nc], 0, 1, abs.alpha, nu, method=method) - p[nc]
    pos <- (fc[nc] > 0)
    xa[nc][!pos] <- xc[nc][!pos]
    fa[nc][!pos] <- fc[nc][!pos]
    xb[nc][pos] <- xc[nc][pos]
    fb[nc][pos] <- fc[nc][pos]
    fail <- ((xc[nc]-xa[nc]) * (xc[nc]-xb[nc])) > 0 
    fail[is.na(fail)] <- TRUE
    xc[fail] <- NA
    x[nc] <- xc[nc]  
    # 2018-05-22: swap two adjacent lines to yield either NA or last estimate
    nc[fail] <- FALSE
    nc[(abs(fc) < tol)] <- FALSE
    regula.falsi <- !regula.falsi 
    }
  # x <- replace(x, na, NA)
  x <- replace(x, zero, -Inf)
  x <- replace(x, one, Inf)
  Sign <- function(x) sign(x) + as.numeric(x==0)
  q <- as.numeric(xi + omega * Sign(alpha)* x)
  names(q) <- names(p)
  return(q)
}

qst_bounds <- function(p, alpha, nu)
{# function created 2018-05-03 
  if(length(alpha) > 1) stop("alpha must be of length 1")
  if(length(nu) > 1) stop("nu must be of length 1")
  if(alpha==0) { upper <- lower <- qt(p,nu); return(cbind(lower, upper))}
  s <- sign(alpha)
  if(alpha < 0) { p <- (1-p); alpha <- abs(alpha)} 
  # from now on have alpha>0
  lower <- qt(p, nu)           # quantiles for alpha=0
  upper <- sqrt(qf(p, 1, nu))  # quantiles for alpha=Inf
  wide <- (upper-lower) > 5
  if(any(wide)) { # improves 'lower' when is too low, moving down from 'upper' 
    for(k in 1:sum(wide)) {
      kk <- which(wide)[k]
      step <- 5
      m <- 0
      repeat{ 
        lower[kk] <- upper[kk] - step
        p0 <- pst(lower[kk], 0, 1, alpha, nu, method=2)
        if(p0 < p[kk]) break
        step <- step*2^(2/(m+2))
        m <- m+1
        }
      }}
  if(s>0) cbind(lower, upper) else cbind(lower=-upper, upper=-lower)
} 


dmst <- function(x, xi=rep(0,length(alpha)), Omega, alpha, nu=Inf, dp=NULL,
                  log = FALSE) 
{
    if(!(missing(alpha) & missing(Omega)) && !is.null(dp)) 
       stop("You cannot set both component parameters and dp")
    if(!is.null(dp)) {
      if(length(dp) != 4) stop("wrong length of non-null 'dp'")
      xi <- drop(dp[[1]])
      Omega <- dp[[2]]
      alpha <- dp[[3]]
      nu <- dp[[4]]
      }  
    if(any(abs(alpha) == Inf)) stop("Inf's in alpha are not allowed")
    if (nu == Inf) return(dmsn(x, xi, Omega, alpha, log = log))
    d <- length(alpha)
    Omega <- matrix(Omega, d, d)
    if(!all(Omega - t(Omega) == 0)) return(NA)
      # stop("Omega not a symmetric matrix")
    invOmega <- pd.solve(Omega, silent=TRUE, log.det=TRUE)
    if(is.null(invOmega))  return(NA)
      # stop("Omega matrix is not positive definite")
    logDet <- attr(invOmega, "log.det")
    x <- if(is.vector(x)) matrix(x, 1, d) else data.matrix(x)
    if (is.vector(xi)) xi <- outer(rep(1, nrow(x)), as.vector(matrix(xi,1,d)))
    X <- t(x - xi)
    # Q <- apply((invOmega %*% X) * X, 2, sum)
    Q <- colSums((invOmega %*% X) * X)
    L <- as.vector(t(X/sqrt(diag(Omega))) %*% as.matrix(alpha))
    if(nu < 1e4) {
      log.const <- lgamma((nu + d)/2)- lgamma(nu/2)-0.5*d*logb(nu)
      log1Q <- logb(1+Q/nu) 
      }
    else {
      log.const <- (-0.5*d*logb(2)+ log1p((d/2)*(d/2-1)/nu))
      log1Q <- log1p(Q/nu)
      }
    log.dmt <- log.const - 0.5*(d * logb(pi) + logDet + (nu + d)* log1Q) 
    log.pt <- pt(L * sqrt((nu + d)/(Q + nu)), df = nu + d, log.p = TRUE)
    logPDF <-  logb(2) + log.dmt + log.pt
    if (log) logPDF else exp(logPDF)
}

rmst <- function(n=1, xi=rep(0,length(alpha)), Omega, alpha, nu=Inf, dp=NULL)
{ 
  if(!(missing(alpha) & missing(Omega)) && !is.null(dp)) 
       stop("You cannot set both component parameters and dp")
  if(!is.null(dp)){
      if(!is.null(dp$xi)) xi <- dp$xi
        else
      if(!is.null(dp$beta)) xi <- as.vector(dp$beta)
      Omega <- dp$Omega
      alpha <- dp$alpha
      nu <- dp$nu
     }  
  if(any(is.infinite(alpha))) stop("Inf's in alpha are not allowed")
  d <- length(alpha)
  if(d == 1) 
    y <- matrix(rst(n, xi, sqrt(Omega), alpha, nu), ncol=1)
  else {  
    z <- rmsn(n, rep(0, d), Omega, alpha)
    v <- if(nu==Inf) 1 else  rchisq(n,nu)/nu 
    y <- t(xi+ t(z/sqrt(v)))
    }
  attr(y, "family") <- "ST"
  attr(y, "parameters") <- list(xi=xi, Omega=Omega, alpha=alpha, nu=nu)
  return(y)
}

pmst <- function(x, xi=rep(0,length(alpha)), Omega, alpha, nu=Inf, dp=NULL, ...)
{
  if(!(missing(alpha) & missing(Omega)) && !is.null(dp)) 
       stop("You cannot set both component parameters and dp")
  if(!is.null(dp)){
    if(!is.null(dp$xi)) xi <- dp$xi     else
      if(!is.null(dp$beta)) xi <- as.vector(dp$beta)
      Omega <- dp$Omega
      alpha <- dp$alpha
      nu <- dp$nu
      }  
  if(!is.vector(x)) stop("x must be a vector")
  if(any(abs(alpha) == Inf)) stop("Inf's in alpha are not allowed")
  if(nu == Inf) return(pmsn(x, xi, Omega, alpha))
  d <- length(alpha)
  Omega<- matrix(Omega,d,d) 
  omega<- sqrt(diag(Omega))
  Ocor <- cov2cor(Omega)
  O.alpha <- as.vector(Ocor %*% alpha)
  delta <- O.alpha/sqrt(1 + sum(alpha*O.alpha))
  Obig <- matrix(rbind(c(1, -delta), cbind(-delta, Ocor)), d+1, d+1)
  if(nu == as.integer(nu)) {
    z0 <- c(0,(x-xi)/omega)
    if(nu < .Machine$integer.max)  
      p <- 2 * mnormt::pmt(z0, mean=rep(0,d+1), S=Obig, df=nu, ...)
    else 
      p <- 2 * mnormt::pmnorm(z0, mean=rep(0,d+1), varcov=Obig, ...)    
    }
  else {# for fractional nu, use formula in Azzalini & Capitanio (2003),
        # full-length paper, last paragraph of Section 4.2[Distr.function]) 
    z <- (x-xi)/omega
    fp <- function(v, Ocor, alpha, nu, t.value) {
            pv <-  numeric(length(v))
            for(k in seq_len(length(v))) pv[k] <- (dchisq(v[k] * nu, nu) * nu *
                 pmsn(sqrt(v[k]) * t.value, rep(0,d), Ocor, alpha) )
            pv}
    p <- integrate(fp, 0, Inf, Ocor, alpha, nu, z, ...)$value
    }
  p
}

  
dmsc <- function(x, xi=rep(0,length(alpha)), Omega, alpha, dp=NULL, 
                log = FALSE) 
{
  if(is.null(dp))  
     dp <- list(xi=xi, Omega=Omega, alpha=alpha, nu=1)
  else
     dp$nu <- 1
  dmst(x, dp=dp, log = log) 
}
  
  
pmsc <- function(x, xi=rep(0,length(alpha)), Omega, alpha, dp=NULL, ...)
{
  if(is.null(dp))  
     dp <- list(xi=xi, Omega=Omega, alpha=alpha, nu=1)
  else
     dp$nu <- 1
  pmst(x, dp=dp, ...) 
}
  
  
rmsc <- function(n=1, xi=rep(0,length(alpha)), Omega, alpha, dp=NULL)
{
  if(is.null(dp))  
     dp <- list(xi=xi, Omega=Omega, alpha=alpha, nu=1)
  else
     dp$nu <- 1
  y <- rmst(n, dp=dp) 
  attr(y, "family") <- "SC"
  attr(y, "parameters") <- dp[-4]
  return(y) 
}

dsc <- function(x, xi=0, omega=1, alpha=0, dp=NULL, log = FALSE) {
  # log.pt2 <- function(x) log1p(x/sqrt(2+x^2)) - log(2)
  if(!is.null(dp)){
     if(!missing(alpha)) 
       stop("You cannot set both 'dp' and component parameters")
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    }
  z <- (x-xi)/omega
  logPDF <- (dcauchy(x, xi, omega, log=TRUE)
             + log1p(alpha*z/sqrt(1+z^2*(1+alpha^2))))
  if(log) logPDF else exp(logPDF)
}
 
psc <- function(x, xi=0, omega=1, alpha=0, dp=NULL) 
{# Behboodian et al. / Stat. & Prob. Letters 76 (2006) p.1490, line 2
  if(!is.null(dp)){
    if(!missing(alpha)) 
      stop("You cannot set both 'dp' and component parameters")    
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    }
  z <- (x-xi)/omega
  delta <- if(abs(alpha)==Inf) sign(alpha) else alpha/sqrt(1+alpha^2)
  atan(z)/pi + acos(delta/sqrt(1+z^2))/pi
  }
   
qsc <- function(p, xi=0, omega=1, alpha=0, dp=NULL) 
{# Behboodian et al. / Stat. & Prob. Letters 76 (2006) p.1490, formula (4)
  if(!is.null(dp)){
    if(!missing(alpha)) 
      stop("You cannot set both 'dp' and component parameters")
    xi<- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
    }
  na <- is.na(p) | (p < 0) | (p > 1)
  zero <- (p == 0)
  one <- (p == 1)
  p <- replace(p, (na | zero | one), 0.5)
  u <- (p - 0.5) * pi
  delta <- if(abs(alpha) == Inf) sign(alpha) else alpha/sqrt(1+alpha^2)
  z <- delta/cos(u) + tan(u)
  z <- replace(z, na, NA)
  z <- replace(z, zero, -Inf)
  z <- replace(z, one, Inf)
  q <- (xi + omega*z)
  names(q) <- names(p)
  return(q)
  }
  
rsc <- function(n=1, xi=0, omega=1, alpha=0, dp=NULL) {
  if(!is.null(dp)){
     if(!missing(alpha)) 
       stop("You cannot set both 'dp' and the component parameters")
    xi <- dp[1]
    omega <- dp[2]
    alpha <- dp[3]
  }
  # rs <<- .Random.seed
  z <- rsn(n, 0, omega, alpha)
  #.Random.seed <<- rs  
  y <- xi + z/abs(rnorm(n))
  attr(y, "family") <- "SC"
  attr(y, "parameters") <- c(xi, omega, alpha)
  return(y) 
}


sn.cumulants <- function(xi = 0, omega = 1, alpha = 0, tau=0,  dp=NULL, n=4)                        
{
   cumulants.half.norm <- function(n=4){
     n <- max(n,2)
     n <- as.integer(2*ceiling(n/2))
     half.n  <-  as.integer(n/2)
     m <- 0:(half.n-1)
     a <- sqrt(2/pi)/(gamma(m+1)*2^m*(2*m+1))
     signs <- rep(c(1, -1), half.n)[seq_len(half.n)]
     a <- as.vector(rbind(signs*a, rep(0,half.n)))
     coeff <- rep(a[1],n)
     for (k in 2:n) {
        ind <- seq_len(k-1)
        coeff[k] <- a[k] - sum(ind*coeff[ind]*a[rev(ind)]/k)
        }
     kappa <- coeff*gamma(seq_len(n)+1)
     kappa[2] <- 1 + kappa[2]
     return(kappa)
    }
  if(!is.null(dp)) {
    if(!missing(alpha)) 
      stop("You cannot set both 'dp' and the component parameters")
    dp <- c(dp,0)[1:4]
    dp <- matrix(dp, 1, ncol=length(dp))
    }
  else  dp <- cbind(xi,omega,alpha,tau)
  delta <- ifelse(abs(dp[,3])<Inf, dp[,3]/sqrt(1+dp[,3]^2), sign(dp[,3]))
  tau <- dp[,4]
  if(all(tau==0)) {
    kv <- cumulants.half.norm(n)
    if(length(kv)>n) kv <- kv[-(n+1)]
    kv[2] <- kv[2] - 1
    kappa <- outer(delta,1:n,"^") * matrix(rep(kv,nrow(dp)),ncol=n,byrow=TRUE)
    }
  else{ # ESN
    if(n>4){
       warning("n>4 not allowed with ESN distribution")
       n <- min(n, 4)
       }
    kappa <- matrix(0, nrow=length(delta), ncol=0)
    for (k in 1:n) kappa <- cbind(kappa, zeta(k,tau)*delta^k)
    }
  kappa[,2] <- kappa[,2] + 1 
  kappa <- kappa * outer(dp[,2],(1:n),"^")
  kappa[,1] <- kappa[,1] + dp[,1]
  kappa[,,drop=TRUE]
} 


zeta <- function(k, x)
{ # k integer in (0,5)
  if(k<0 | k>5 | k != round(k)) return(NULL)
  na <- is.na(x)
  x  <- replace(x,na,0)
  x2 <- x^2
  z <- switch(k+1,
            pnorm(x, log.p=TRUE) + log(2),           
            ifelse(x>(-50), exp(dnorm(x, log=TRUE) - pnorm(x, log.p=TRUE)),
                            -x/(1 -1/(x2+2) +1/((x2+2)*(x2+4)) 
                              -5/((x2+2)*(x2+4)*(x2+6))
                              +9/((x2+2)*(x2+4)*(x2+6)*(x2+8)) 
                              -129/((x2+2)*(x2+4)*(x2+6)*(x2+8)*(x2+10)) )), 
            (-zeta(1,x)*(x+zeta(1,x))),
            (-zeta(2,x)*(x+zeta(1,x)) - zeta(1,x)*(1+zeta(2,x))),
            (-zeta(3,x)*(x+2*zeta(1,x)) - 2*zeta(2,x)*(1+zeta(2,x))),
            (-zeta(4,x)*(x+2*zeta(1,x)) -zeta(3,x)*(3+4*zeta(2,x))
                 -2*zeta(2,x)*zeta(3,x)),
            NULL)
  neg.inf <- (x == -Inf)
  if(any(neg.inf))
    z <- switch(k+1,
                z,
                replace(z, neg.inf, Inf),
                replace(z, neg.inf, -1),
                replace(z, neg.inf, 0),
                replace(z, neg.inf, 0),
                replace(z, neg.inf, 0),
                NULL)
  if(k>1) z<- replace(z, x==Inf, 0)
  replace(z, na, NA)
}

st.cumulants <- function(xi=0, omega=1, alpha=0, nu=Inf, dp=NULL, n=4)
{
  if(!is.null(dp)) {
      if(!missing(alpha)) 
        stop("You cannot set both 'dp' and the component parameters")
      xi <- dp[1]
      omega <- dp[2]
      alpha <- dp[3]
      nu <- dp[4]
      }
  if(length(nu) > 1) stop("'nu' must be a scalar value")    
  if(nu == Inf) return(sn.cumulants(xi, omega, alpha, n=n))
  n <- min(as.integer(n), 4)      
  par <- cbind(xi, omega, alpha)
  alpha <- par[,3]
  delta <- ifelse(abs(alpha)<Inf, alpha/sqrt(1+alpha^2), sign(alpha))
  cum <- matrix(NaN, nrow=nrow(par), ncol=n)
  cum[,1] <- mu <- b(nu)*delta
  # r <- function(nu, k1, k2) 1/(1-k2/nu) - k1/(nu-k2)     # = (nu-k1)/(nu-k2)
  s <- function(nu, k) 1/(1 - k/nu)                        # = nu/(nu-k)
  if(n>1 & nu>2) cum[,2] <- s(nu,2) - mu^2
  if(n>2 & nu>3) cum[,3] <- mu*((3-delta^2)*s(nu,3) - 3*s(nu,2) + 2*mu^2)
  if(n>2 & nu==3) cum[,3] <- sign(alpha) * Inf  
  if(n>3 & nu>4) cum[,4] <- (3*s(nu,2)*s(nu,4) - 4*mu^2*(3-delta^2)*s(nu,3)
                             + 6*mu^2*s(nu,2)-3*mu^4) - 3*cum[,2]^2
  if(n>3 & nu==4) cum[,4] <- Inf
  cum <- cum*outer(par[,2], 1:n, "^")
  cum[,1] <- cum[,1]+par[,1]
  cum[,,drop=TRUE]
}
 

T.Owen <- function(h, a, jmax=50, cut.point=8)
{
 T.int <-function(h, a, jmax, cut.point)
   {
     fui <- function(h,i) (h^(2*i))/((2^i)*gamma(i+1)) 
     seriesL <- seriesH <- NULL
     i  <- 0:jmax
     low<- (h <= cut.point)
     hL <- h[low]
     hH <- h[!low]
     L  <- length(hL)
     if (L > 0) {
       b    <- outer(hL, i, fui)
       cumb <- apply(b, 1, cumsum)
       b1   <- exp(-0.5*hL^2) * t(cumb)
       matr <- matrix(1, jmax+1, L) - t(b1)
       jk   <- rep(c(1,-1), jmax)[1:(jmax+1)]/(2*i+1)
       matr <- t(matr*jk) %*%  a^(2*i+1)
       seriesL  <- (atan(a) - as.vector(matr))/(2*pi)
     }
     if (length(hH) > 0)  seriesH <- 
          atan(a)*exp(-0.5*(hH^2)*a/atan(a)) * (1+0.00868*(hH*a)^4)/(2*pi)
     series <- c(seriesL, seriesH)
     id <- c((1:length(h))[low],(1:length(h))[!low]) 
     series[id] <- series  # re-sets in original order
     series
  }
  if(!is.vector(a) | length(a)>1) stop("'a' must be a vector of length 1")
  if(!is.vector(h)) stop("'h' must be a vector")
  aa <- abs(a)    
  ah <- abs(h)
  if(is.na(aa)) stop("parameter 'a' is NA") 
  if(aa==Inf) return(sign(a)*0.5*pnorm(-ah)) # sign(a): 16.07.2007
  if(aa==0)   return(rep(0,length(h)))
  na  <- is.na(h)
  inf <- (ah == Inf)
  ah  <- replace(ah,(na|inf),0)
  if(aa <= 1)
    owen <- T.int(ah,aa,jmax,cut.point)
  else
    owen<- (0.5*pnorm(ah) + pnorm(aa*ah)*(0.5-pnorm(ah)) 
               - T.int(aa*ah,(1/aa),jmax,cut.point))
  owen <- replace(owen,na,NA)
  owen <- replace(owen,inf,0)
  return(owen*sign(a))
}
          
#=========================================================================

makeSECdistr <- function(dp, family, name, compNames)
{
  ndp <- switch(tolower(family), "sn" = 3, "esn" = 4, "st" = 4, "sc" = 3, NULL)
  if(is.null(ndp)) stop(gettextf("unknown family '%s'", family))
  family <- toupper(family)
  if(length(dp) != ndp) 
    stop(gettextf("wrong number of dp components for family '%s'", family))
  if(family == "ST") {
    nu <- as.numeric(dp[4])
    if(nu <= 0) stop("'nu' for ST family must be positive")
    if(nu == Inf) {
      warning("ST family with 'nu==Inf' is changed to SN family")
      family <- "SN"
      dp <- dp[-4]
    }}

  if(is.numeric(dp)){ # univariate distribution
    if(dp[2] <= 0) stop("omega parameter must be positive") 
    fourth <- switch(family, "SN"=NULL, "ESN"="tau", "SC"=NULL, "ST"="nu")
    names(dp) <- c("xi","omega","alpha",fourth)
    name <- if(!missing(name)) as.character(name)[1]  else 
      paste("Unnamed-", toupper(family), sep="")
    obj <- new("SECdistrUv", dp=dp, family=family, name=name)
    }
  else {if(is.list(dp)) {# multivariate distribution
    names(dp) <- rep(NULL,ndp)
    d <- length(dp[[3]])
    if(any(abs(dp[[3]]) == Inf)) stop("Inf in alpha not allowed") 
    if(length(dp[[1]]) != d) stop("mismatch of parameters size")
    Omega <- matrix(dp[[2]],d,d)  
    if(any(Omega != t(Omega))) stop("Omega matrix must be symmetric")
    if(min(eigen(Omega, symmetric=TRUE, only.values=TRUE)$values) <= 0)
      stop("Omega matrix must be positive definite")
    dp0 <- list(xi=as.vector(dp[[1]]), Omega=Omega, alpha=dp[[3]])
    name <- if(!missing(name)) as.character(name)[1]  else 
      paste("Unnamed-", toupper(family), "[d=", as.character(d), "]", sep="")
    if(family=="ST") dp0$nu <- nu
    if(family=="ESN") dp0$tau <- dp[[4]]
    if(d == 1)  warning(paste(
      "A multivariate distribution with dimension=1 is a near-oxymoron.",
      "\nConsider using a 'dp' vector to define a univariate distribution.",
      "\nHowever, I still build a multivariate distribution for you."))
    if(missing(compNames)) { compNames <-
      if(length(names(dp[[1]])) == d) names(dp[[1]]) else
        as.vector(outer("V",as.character(1:d),paste,sep=""))
      }
    else {
      if(length(compNames) != d) stop("Wrong length of 'compNames'")
      compNames <- as.character(as.vector(compNames))
      }
    names(dp0$alpha) <- names(dp0$xi) <- compNames
    dimnames(dp0$Omega) <- list(compNames, compNames)  
    obj <- new("SECdistrMv", dp=dp0, family=family, name=name, 
               compNames=compNames) } 
     else stop("'dp' must be either a numeric vector or a list")}
  obj
}

summary.SECdistrUv <- function(object, cp.type="auto", probs)
{
  cp.type <- match.arg(tolower(cp.type), c("proper", "pseudo", "auto"))
  family <- slot(object,"family")
  lc.family <- lc.family0 <- tolower(family)
  name <- slot(object,"name")
  dp <- dp0 <- slot(object,"dp") 
  # op <- dp2op(dp, family)
  if(family=="ST" || family=="SC") { if(cp.type=="auto") 
    cp.type <- if(family == "SC" | dp[4] <= 4) "pseudo" else "proper" 
    if(family=="SC") {dp <- c(dp, 1); lc.family <- "st" }  }
  if(family=="SN" || family=="ESN") cp.type <- "proper" 
  cp <- dp2cpUv(dp, lc.family, cp.type)
  if(is.null(cp)) stop('Stop. Consider using cp.type=="pseudo"')
  if(missing(probs)) probs <- c(0.05, 0.25, 0.50, 0.75, 0.95)
  if(lc.family == "esn") lc.family <- "sn"
  q.fn <- get(paste("q", lc.family, sep=""), inherits = TRUE)
  q <- q.fn(probs, dp=dp)
  names(q) <- format(probs)
  cum <- switch(lc.family,
           "sn" = sn.cumulants(dp=dp, n=4),
           "st" = st.cumulants(dp=dp, n=4),
           rep(NA,4)
           )
  std.cum <- c(gamma1=cum[3]/cum[2]^1.5, gamma2=cum[4]/cum[2]^2)
  oct <- q.fn(p=(1:7)/8, dp=dp)
  mode <- modeSECdistrUv(dp, lc.family)
  alpha <- as.numeric(dp[3])
  delta <- delta.etc(alpha)
  q.measures <- c(bowley=(oct[6]-2*oct[4]+oct[2])/(oct[6]-oct[2]),
                  moors=(oct[7]-oct[5]+oct[3]-oct[1])/(oct[6]-oct[2]))
  if(family== "SC" & lc.family=="st") cp <- cp[-length(cp)]
  if(family== "SC" & lc.family=="st") dp <- dp[-length(dp)]
  aux <- list(delta=delta, mode=mode, quantiles=q, 
              std.cum=std.cum, q.measures=q.measures)                                         
  new("summary.SECdistrUv", dp=dp, family=family, name=name,   
      cp=cp, cp.type=cp.type, aux=aux)
}

modeSECdistr <- function(dp, family, object=NULL) 
{
  if(!is.null(object)) {
     if(!missing(dp)) stop("you cannot set both arguments dp and obj")
    obj.class <- class(object)
    if(!(obj.class %in% c("SECdistrUv", "SECdistrMv"))) 
      stop(gettextf("wrong object class: '%s'", obj.class), domain = NA)
    family <- slot(object, "family")
    dp <- slot(object, "dp")
    }  
  else {
    if(missing(family)) stop("family required")
    family <- toupper(family)
    if(!(family %in% c("SN", "ESN", "ST","SC")))
      stop(gettextf("family '%s' is not supported", family), domain = NA)
    } 
  if(is.list(dp)) modeSECdistrMv(dp, family) else modeSECdistrUv(dp, family)
}

modeSECdistrUv <- function(dp, family)
{
  if(abs(dp[3]) < .Machine$double.eps) return(as.numeric(dp[1]))
  cp <- dp2cpUv(dp, family, cp.type="auto", upto=1)
  lc.family <- tolower(family)
  if(lc.family == "esn") lc.family <- "sn"
  d.fn <- get(paste("d", lc.family, sep=""), inherits = TRUE)
  int <- c(dp[1], cp[1])
  if(abs(diff(int)) < .Machine$double.eps) return(mean(int))
  opt <- optimize(d.fn, lower=min(int), upper=max(int), maximum=TRUE, dp=dp)
  as.numeric(opt$maximum)
}


modeSECdistrMv <- function(dp, family)
{
  Omega <- dp[[2]]
  alpha <- dp[[3]]
  delta_etc <- delta.etc(alpha, Omega)
  if(delta_etc$alpha.star < .Machine$double.eps) return(dp[[1]])
  lc.family <- tolower(family)
  if(lc.family == "esn") lc.family <- "sn"
  direct <- sqrt(diag(Omega)) * (delta_etc$delta/delta_etc$delta.star)
  if(lc.family == "sn") {# case SN: book (5.49);
    # the same result is used also for ESN, see handwritten Problem 5.18 
    dp1 <- c(xi=0, omega=1, alpha=delta_etc$alpha.star, dp$tau) 
    mode.canon <- modeSECdistrUv(dp1, family)
    mode <- as.numeric(dp[[1]] + mode.canon * direct)  
  } else {# case ST, SC: book Proposition 6.2, p.178, 
    # but maximizes along canonical direction, instead of solving equation
    d.fn <- get(paste("dm", lc.family, sep=""), inherits = TRUE)
    f <- function(u, dp, direct) d.fn(dp[[1]]+ u*direct, dp=dp, log=TRUE)
    direct.pmean <- dp2cpMv(dp, family, "auto", upto=1)[[1]] - dp[[1]]/direct
    maxM <- max(abs(direct.pmean), na.rm=TRUE)
    opt <- optimize(f, lower=0, upper=maxM, dp=dp, direct=direct, maximum=TRUE)
    mode <- as.numeric(dp[[1]]+ opt$maximum * direct)
  }
  return(mode)
}


summary.SECdistrMv <- function(object, cp.type="auto")
{
  cp.type <- match.arg(tolower(cp.type), c("proper", "pseudo", "auto")) 
  family <- slot(object,"family")
  name <- slot(object,"name")
  dp <- slot(object,"dp")
  # op <- dp2op(dp, family)
  if(family == "SN" || family == "ESN") cp.type <- "proper"
  if(family=="ST" || family=="SC") { if(cp.type=="auto") 
    cp.type <- if(family == "SC" || dp$nu <= 4) "pseudo" else "proper"}
  cp <- dp2cpMv(dp, family, cp.type, aux=TRUE)
  aux <- cp$aux
  if(family=="SN" | family=="SC") cp <- cp[1:3] 
  cp[["aux"]] <- NULL
  mode <- modeSECdistrMv(dp, family)
  aux0 <- list(mode=mode, delta=aux$delta, alpha.star=aux$alpha.star, 
    delta.star=aux$delta.star, mardia=aux$mardia)
  new("summary.SECdistrMv", dp=dp, family=family, name=object@name, 
    compNames=object@compNames,  cp=cp, cp.type=cp.type, aux=aux0)
}

dp2cp <- function(dp, family, object=NULL, cp.type="proper", upto=NULL)
{
  if(!is.null(object)){
    if(!missing(dp)) stop("you cannot set both arguments dp and object")
    obj.class <- class(object)
    if(!(obj.class %in% c("SECdistrUv", "SECdistrMv"))) 
      stop(gettextf("wrong object class: '%s'", obj.class), domain = NA)     
    family <- slot(object, "family")
    dp <- slot(object,"dp")
    multiv <- (obj.class == "SECdistrMv")
    }
  else{
    if(missing(family)) stop("family required")
    family <- toupper(family)
    if(!(family %in% c("SN", "ESN", "ST","SC")))
      stop(gettextf("family '%s' is not supported", family), domain = NA)
    multiv <- is.list(dp)
    }
  if(!is.null(upto)) if(upto<0 | upto>4 | upto != round(upto)) { 
      warning("unsuitable value of argument 'upto', reset to NULL")
      upto <- NULL} 
  if(multiv)
    dp2cpMv(dp, family, cp.type, upto=upto)
  else
    dp2cpUv(dp, family, cp.type, upto=upto)
}
 
dp2cpUv <- function(dp, family, cp.type="proper", upto=NULL) 
{ # internal function; works also with regression parameters included
  cp.type <- match.arg(tolower(cp.type), c("proper", "pseudo", "auto"))
  family <- toupper(family)
  if(!(family %in% c("SN", "ESN", "ST", "SC")))
    stop(gettextf("family = '%s' is not supported", family), domain = NA)
  if(family %in% c("SN","ESN")){
    if(cp.type == "pseudo") 
      warning("'cp.type=pseudo' makes no sense for SN and ESN families")
    p <- length(dp)-2-as.numeric(family=="ESN")
    omega <- dp[p+1]
    if(omega <= 0) stop("scale parameter 'omega' must be positive")
    alpha <- dp[p+2]
    tau <- if(family=="ESN") as.numeric(dp[p+3]) else 0
    delta <- if(abs(alpha) < Inf) alpha/sqrt(1+alpha^2) else sign(alpha)
    mu.Z  <- zeta(1,tau)*delta
    s.Z   <- sqrt(1+zeta(2,tau)*delta^2)
    gamma1 <- zeta(3,tau)*(delta/s.Z)^3
    sigma <- omega*s.Z
    mu    <- dp[1:p]
    mu[1] <- dp[1]+sigma*mu.Z/s.Z
    beta1 <- if(p>1) mu[2:p] else NULL
    cp    <- c(mu, sigma, gamma1, if(family=="ESN") tau else NULL)
    names(cp) <- param.names("CP", family, p, x.names=names(beta1))
    if(!is.null(upto)) cp <- cp[1:(upto+p-1)]
    }
  if(family=="ST" || family=="SC") { if(cp.type=="auto") 
    cp.type <- if(family == "SC" || dp[4] <= 4) "pseudo" else "proper" }
  if(family %in%  c("SC", "ST")) {
    fixed.nu <- if(family=="SC") 1 else NULL
    cp <- st.dp2cp(dp, cp.type, fixed.nu, jacobian=FALSE, upto=upto)
    if(is.null(cp)) {warning("no CP could be found"); return(invisible())}
    # param.type <- switch(cp.type, proper="CP", pseudo="pseudo-CP")
    # names(cp) <- param.names(param.type, family)
    } 
   return(cp)
}

dp2cpMv <- 
function(dp, family, cp.type="proper", fixed.nu=NULL, aux=FALSE, upto=NULL) 
{# internal. NB: name of cp[1] must change according to dp[1]
  cp.type <- match.arg(cp.type, c("proper", "pseudo", "auto"))
  family <- toupper(family)
  if(!(family %in% c("SN", "ESN", "ST","SC")))
    stop(gettextf("family '%s' is not supported", family), domain = NA)
  if(family %in% c("SN","ESN")){  
    if(cp.type == "pseudo") 
      warning("'cp.type=pseudo' makes no sense for SN and ESN families")
    cp <- msn.dp2cp(dp, aux=aux)
    if(!is.null(upto)) cp <- cp[1:upto]
    }
  if(family %in% c("SC","ST")){
    if(cp.type=="auto") cp.type <- 
      if(family == "SC" || dp[[4]] <= 4) "pseudo" else "proper"
    if(family == "SC") fixed.nu <- 1
    cp <- mst.dp2cp(dp, cp.type=cp.type, fixed.nu=fixed.nu, aux=aux, upto=upto)
    if(is.null(cp)) {warning("no CP could be found"); return(invisible())}
    }
  return(cp)
}
  
msn.dp2cp <- function(dp, aux=FALSE)
{# dp2cp for multivariate SN and ESN 
  alpha <- dp$alpha
  d <- length(alpha)
  Omega <- matrix(dp$Omega, d, d)  
  omega <- sqrt(diag(Omega))
  lot <- delta.etc(alpha, Omega)
  delta <- lot$delta
  delta.star <- lot$delta.star
  alpha.star <- lot$alpha.star
  names(delta) <- names(dp$alpha)
  tau <- if(is.null(dp$tau)) 0 else dp$tau
  mu.z  <- zeta(1, tau) * delta
  sd.z  <- sqrt(1 + zeta(2, tau) * delta^2)
  Sigma <- Omega + zeta(2,tau) * outer(omega*delta, omega*delta)
  gamma1 <- zeta(3, tau) * (delta/sd.z)^3
  if(is.vector(dp[[1]])) { 
    cp <- list(mean=dp[[1]] + mu.z*omega, var.cov=Sigma, gamma1=gamma1)
    }
  else {
    beta <- dp[[1]]  
    beta[1,] <- beta[1,] + mu.z*omega
    cp <- list(beta=beta, var.cov=Sigma, gamma1=gamma1)
  }
  if(!is.null(dp$tau)) cp$tau <- tau
  if(aux){
    lambda <- delta/sqrt(1-delta^2)
    D <- diag(sqrt(1+lambda^2), d, d)
    Ocor <- lot$Omega.cor
    Psi <- D %*% (Ocor-outer(delta,delta)) %*% D
    Psi <- (Psi + t(Psi))/2
    O.inv <- pd.solve(Omega)
    O.pcor <- -cov2cor(O.inv) 
    O.pcor[cbind(1:d, 1:d)] <- 1
    R <- force.symmetry(Ocor + zeta(2,tau)*outer(delta,delta))
    ratio2 <- delta.star^2/(1+zeta(2,tau)*delta.star^2)
    mardia <- c(gamma1M=zeta(3,tau)^2*ratio2^3, gamma2M=zeta(4,tau)*ratio2^2)
    # SN book: see (5.74), (5.75) on p.153
    cp$aux <- list(omega=omega, cor=R, Omega.inv=O.inv, Omega.cor=Ocor, 
      Omega.pcor=O.pcor, lambda=lambda, Psi=Psi, delta=delta, lambda=lambda,
      delta.star=delta.star, alpha.star=alpha.star, mardia=mardia)
    }
  return(cp)  
}

mst.dp2cp <- function(dp, cp.type="proper", fixed.nu=NULL, symmetr=FALSE, 
   aux=FALSE, upto=NULL)
{# dp2cp for multivariate ST, returns NULL if CP not found (implicitly silent)
  nu <- if(is.null(fixed.nu)) dp$nu else fixed.nu
  if(is.null(upto)) upto <- 4L
  if((round(upto) != upto)||(upto < 1)) stop("'upto' must be positive integer")
  if(nu <= upto && (cp.type =="proper")) return(NULL)
  if(cp.type == "proper")  {
    if(nu <= upto) 
      # stop(gettextf("d.f. '%s' too small, CP is undefined", nu), domain = NA)
      return(NULL)
      a <- rep(0, upto) 
      tilde <- NULL
    } else {
      a <- (1:upto) 
      tilde <- rep("~", upto)
    }
  Omega <- dp$Omega 
  d <- ncol(Omega)
  comp.names <- colnames(dp$Omega)
  alpha <- if(symmetr) rep(0, d) else dp$alpha 
  omega <- sqrt(diag(Omega))
  lot <- delta.etc(alpha, Omega)
  delta <- lot$delta
  delta.star <- lot$delta.star
  alpha.star <- lot$alpha.star
  names(delta) <- comp.names  
  mu0 <- b(nu+a[1]) * delta * omega
  names(mu0) <- comp.names
  mu.2 <- b(nu+a[2]) * delta * omega
  if(is.vector(dp[[1]])) cp <- list(mean=dp[[1]] + mu0)  else {
    beta <- dp[[1]]  
    beta[1,] <- beta[1,] + mu0
    cp <- list(beta=beta)  }
  if(upto > 1) {
    Sigma <- Omega * (nu+a[2])/(nu+a[2]-2) - outer(mu.2, mu.2)
    dimnames(Sigma) <- list(comp.names, comp.names)
    cp$var.cov <- Sigma
    }
  cp$gamma1 <- if(upto > 2 & !symmetr) st.gamma1(delta, nu+a[3]) else NULL
  cp$gamma2M <- if(upto > 3 & is.null(fixed.nu))  
      mst.mardia(delta.star^2, nu+a[4], d)[2] else NULL
  names(cp) <- paste(names(cp), tilde[1:length(cp)], sep="")  
  # cp <- cp[1:length(dp1)]
  if(aux){
    mardia <- mst.mardia(delta.star^2, nu, d)
    cp$aux <- list(fixed.nu=fixed.nu, 
                omega=omega, Omega.cor=lot$Omega.cor, delta=delta,
                delta.star=delta.star, alpha.star=alpha.star, mardia=mardia)
    }
  return(cp)  
}

#-- function mst.gamma2M is subsumend in  mst.mardia, in practical terms
# mst.gamma2M <- function(delta.sq, nu, d)
#  {# Mardia measure of kurtosis \gamma_{2,d} for multiv.ST
#  if(delta.sq < 0 | delta.sq >1 )  stop("delta.sq not in (0,1)")
#   ifelse(nu>4, 
#     {R <- b(nu)^2 * delta.sq * (nu-2)/nu
#      R1R <- R/(1-R)
#      (2*d*(d+2)/(nu-4) + (R/(1-R)^2)*8/((nu-3)*(nu-4))
#       +2*R1R^2*(-(nu^2-4*nu+1)/((nu-3)*(nu-4))+2*(nu/((nu-3)*b(nu)^2)-1))
#      +4*d*R1R/((nu-3)*(nu-4))) },
#     Inf)
# }

mst.mardia <- function(delta.sq, nu, d) 
{# Mardia measures gamma1 and gamma2 for MST; book: (6.31), (6.32), p.178
  if(d < 1) stop("d < 1") 
  if(d != round(d)) stop("'d' must be a positive integer")
  if(delta.sq < 0 | delta.sq > 1)  stop("delta.sq not in (0,1)")
  if(nu <= 3) stop("'nu>3' is required")
  cum <- st.cumulants(0, 1, sqrt(delta.sq/(1-delta.sq)), nu)
  mu <- cum[1]
  sigma <- sqrt(cum[2])
  gamma1 <- cum[3]/sigma^3
  gamma2 <- cum[4]/sigma^4
  gamma1M <- if(nu > 3) (gamma1^2 + 3*(d-1)*mu^2/((nu-3)*sigma^2)) else Inf
  r <- function(nu, k1, k2) 1/(1 - k2/nu) - k1/(nu - k2) # (nu-k1)/(nu-k2)
  gamma2M <- if(nu > 4) (gamma2 + 3 +(d^2-1)*r(nu,2,4) +2*(d-1)*(r(nu,0,4)
                -mu^2*r(nu,1,3))/sigma^2 - d*(d+2)) else Inf
  return(c(gamma1M=gamma1M, gamma2M=gamma2M))            
}    
	
cp2dp <- function(cp, family){
  family <- toupper(family)
  if(!(family %in% c("SN", "ESN", "ST","SC")))
      stop(gettextf("family '%s' is not supported", family), domain = NA)
  dp <- if(is.list(cp))  cp2dpMv(cp, family)  else cp2dpUv(cp, family)
  if(anyNA(dp)) dp <- NULL
  return(dp)
}
 
cp2dpUv <- function(cp, family, silent=FALSE, tol=1e-8) 
{ # internal function; works also with regression parameters included
   family <- toupper(family)
   if(family=="ESN") stop("cp2dp for ESN not yet implemented")
   if(family == "SN") {     
     p <- length(cp)-2-as.numeric(family=="ESN")
     beta1 <- if (p>1) cp[2:p] else NULL
     b <- sqrt(2/pi) 
     sigma  <- cp[p+1]
     excess <- max(0, -sigma)
     gamma1 <- cp[p+2]
     tau <- if(family=="ESN") as.numeric(cp[p+3]) else 0
     max.gamma1 <- 0.5*(4-pi)*(2/(pi-2))^1.5
     if (abs(gamma1) >= max.gamma1) {
       if (silent) excess <- excess + (abs(gamma1) - max.gamma1) else 
         {message("gamma1 outside admissible range"); return(invisible())}}
     if(excess > 0) {
       out <- NA
       attr(out, "excess") <- excess
       return(out)
       }
     r  <- sign(gamma1)*(2*abs(gamma1)/(4-pi))^(1/3)
     delta <- r/(b*sqrt(1+r^2))
     alpha <- delta/sqrt(1-delta^2)
     mu.z <- b*delta
     sd.z <- sqrt(1-mu.z^2)
     beta <- cp[1:p]
     omega <- cp[p+1]/sd.z
     beta[1] <- cp[1] - omega*mu.z
     dp <- as.numeric(c(beta, omega, alpha))
     names(dp) <- param.names("DP", family, p, x.names=names(beta1))
     return(dp)
     }
  if(family == "ST") return(st.cp2dp(cp, silent=silent, tol=tol))
  if(family == "SC") stop("this makes no sense for SC family")
  warning(gettextf("family = '%s' is not supported", family), domain = NA)
  invisible(NULL)
}

cp2dpMv <- function(cp, family, silent=FALSE, tol=1e-8) 
{ # internal function
  if(family == "SN")  dp <- msn.cp2dp(cp, silent)
  else if(family == "ESN") stop("cp2dp for ESN not yet implemented")
  else if(family == "ST") dp <- mst.cp2dp(cp, silent, tol=tol)
  else if(family == "SC") stop("this makes no sense for SC family")
  else warning(gettextf("family = '%s' is not supported", family), domain = NA)
  return(dp)
}


msn.cp2dp <- function(cp, silent=FALSE) {
  beta <- cp[[1]]
  Sigma <- cp[[2]]
  gamma1 <- cp[[3]]
  d <- length(gamma1)
  b <- sqrt(2/pi)  
  max.gamma1 <- 0.5*(4-pi)*(2/(pi-2))^1.5
  if(any(abs(gamma1) >= max.gamma1))  
    {if(silent) return(NULL) else stop("non-admissible CP")}
  R <- sign(gamma1)*(2*abs(gamma1)/(4-pi))^(1/3)
  delta <-  R/(b*sqrt(1+R^2))
  mu.z <- b*delta
  omega <- sqrt(diag(Sigma)/(1-mu.z^2))
  Omega <- Sigma + outer(mu.z*omega, mu.z*omega)
  Omega.bar <- cov2cor(Omega)
  Obar.inv <- pd.solve(Omega.bar, silent=silent)
  if(is.null(Obar.inv))  
    {if(silent) return(NULL) else stop("non-admissible CP")}
  Obar.inv.delta <- as.vector(Obar.inv %*% delta)
  delta.sq <- sum(delta * Obar.inv.delta)
  if(delta.sq >= 1) 
    {if(silent) return(NULL) else stop("non-admissible CP")}
  alpha <- Obar.inv.delta/sqrt(1-delta.sq)
  if(is.vector(beta)) {
    beta <- beta - omega*mu.z
    dp <- list(beta=beta, Omega=Omega, alpha=alpha)
    }
  else {
    beta[1,] <- beta[1,] - omega*mu.z
    dp <- list(beta=beta, Omega=Omega, alpha=alpha)  
    }
  attr(dp, "delta.star") <- sqrt(delta.sq)
  return(dp)
  }

st.dp2cp <- function(dp, cp.type="proper", fixed.nu=NULL, symmetr=FALSE, 
              jacobian=FALSE, upto=NULL) 
{
  if(any(is.na(dp))) stop("NA's in argument 'dp'")
  if(!(cp.type %in% c("proper", "pseudo"))) stop("invalid cp.type") 
  nu <- if(is.null(fixed.nu)) dp[length(dp)] else fixed.nu
  if(is.null(upto)) upto <- 4L
  if((round(upto) != upto)||(upto < 1)) stop("'upto' must be positive integer")
  if(nu <= upto && (cp.type =="proper")) return(NULL)
  p <- length(dp) - 2 - is.null(fixed.nu)
  beta1 <- if(p>1) dp[2:p] else  NULL   
  dp <- c(dp[1], dp[p+1], dp[p+2], nu)
  a <- if(cp.type == "proper") rep(0,upto) else (1:upto) 
  omega <- dp[2]
  alpha <- dp[3]
  delta <- delta.etc(alpha)
  mu.z <- function(delta, nu) delta*b(nu)
  mu <- dp[1] + dp[2]* mu.z(delta, nu+a[1])
  rv.comp <- c(rep(TRUE, upto-1), rep(FALSE, 4-upto))
  param.type <- switch(cp.type, proper="CP", pseudo="pseudo-CP")
  cp.names <- param.names(param.type, "ST", p, names(beta1), rv.comp)
  cp <- c(mu, beta1)
  names(cp) <- cp.names[1:p]
  if(upto > 1) {
    kappa2 <- function(delta,nu) nu/(nu-2) - mu.z(delta,nu)^2
    sigma <- omega * sqrt(kappa2(delta, nu+a[2]))
    cp <- c(cp, sigma)
    names(cp) <- cp.names[1:(p+1)]
    }
  if(upto > 2 & ! symmetr) {
    g1 <- st.gamma1(delta, nu+a[3])
    cp <- c(cp, g1)
    names(cp) <- cp.names[1:(p+2)]
    }
  if(upto > 3 & is.null(fixed.nu)) { 
    g2 <- st.gamma2(delta, nu+a[4])
    cp <- c(cp, g2)
    names(cp) <- cp.names
    }
  if(!is.null(fixed.nu) && upto==4) cp <- cp[-length(cp)]
  if(jacobian && (nu+a[3] > 3)) {
    u <- function(nu) 0.5*(1/nu + digamma((nu-1)/2) - digamma(nu/2)) 
    Ddelta <- 1/(1+alpha^2)^1.5
    Dkappa2.nu <- function(delta,nu) 
      (-2)*(1/(nu-2)^2 + mu.z(delta,nu)^2 * u(nu))
    Dg1.delta <- function(delta,nu) { # derivative of gamma1 wrt delta
      k2 <- kappa2(delta,nu)
      tmp <- nu/(nu-2)-delta^2*(nu-2*b(nu)^2*(nu-2))    
      (3*b(nu) *nu *tmp)/(k2^2.5 * (nu-2)*(nu-3))
      }
    Dg1.nu <-  function(delta,nu) {# derivative of gamma1 wrt nu
      k1 <- mu.z(delta,nu)
      k2 <- kappa2(delta,nu)
      Dk2.nu <- Dkappa2.nu(delta,nu)
      (g1*u(nu)
       + k1/k2^1.5*(-3*(3-delta^2)/(nu-3)^2 + 6/(nu-2)^2 + 4*k1^2*u(nu))
       -3*g1*Dk2.nu/(2*k2))
       }
    Dg2.delta <- function(delta,nu) {# derivative of gamma2 wrt delta
      k1 <- mu.z(delta, nu)
      k2 <- kappa2(delta,nu)
      4*b(nu)^2*delta/k2 * (g2 + 3 -(2*(3-2*delta^2)*nu/(nu-3)
                 -3*nu/(nu-2)+3*k1^2)/k2)
      }
    Dg2.nu <- function (delta, nu) {# derivative of gamma2 wrt nu
      k1 <- mu.z(delta, nu)
      k2 <- kappa2(delta,nu)
      b. <- b(nu)
      u. <- u(nu)
      k4 <- (3 * nu^2/((nu - 2) * (nu - 4))
              -6*(delta*b.)^2 * nu*(nu-1)/((nu-2)*(nu-3))
              + delta^4 * b.^2* (4*nu/(nu-3)-3*b.^2))
      Dk4.nu <- (-6*nu*(3*nu-8)/((nu-2)*(nu-4))^2
               -4*k1^2*(3-delta^2)*((2*u.*nu+1)*(nu-3)-nu)/(nu-3)^2
               +6*k1^2*((2*u(nu)*nu+1)*(nu-2)-nu)/(nu-2)^2
               -12*k1^4*u.)
      Dk2.nu <- Dkappa2.nu(delta,nu)
      Dk4.nu/k2^2 - 2*k4*Dk2.nu/k2^3
      }
    Dcp.dp <- if(is.null(fixed.nu)) diag(1, p+3) else  diag(1, p+2) 
    Dcp.dp[1, p+1] <- mu.z(delta, nu+a[1])
    Dcp.dp[1, p+2] <- omega * Ddelta * b(nu+a[1])
    sigma.z <- sqrt(kappa2(delta, nu+a[2]))
    Dcp.dp[p+1,p+1] <- sigma.z
    Dcp.dp[p+1,p+2] <- -omega *delta *b(nu+a[2])^2 *Ddelta/sigma.z
    Dcp.dp[p+2,p+2] <- Dg1.delta(delta, nu+a[3]) * Ddelta
    if(is.null(fixed.nu) && (nu+a[4] > 4)) {
      Dcp.dp[1, p+3] <- omega * mu.z(delta, nu+a[1]) * u(nu+a[1])
      Dcp.dp[p+1,p+3] <- omega * Dkappa2.nu(delta, nu+a[2])/(2 * sigma.z)
      Dcp.dp[p+2,p+3] <- Dg1.nu(delta, nu+a[3])
      Dcp.dp[p+3,p+2] <- Dg2.delta(delta, nu+a[4]) * Ddelta 
      Dcp.dp[p+3,p+3] <- Dg2.nu(delta, nu+a[4])
      }
    attr(cp, "jacobian") <- Dcp.dp
    }    
  return(cp)
}

# b <- function (nu)  ifelse(nu>1, ifelse(nu < 1e8, 
#        sqrt(nu/pi)*exp(lgamma((nu-1)/2)-lgamma(nu/2)), sqrt(2/pi)), NA)

b <- function(nu)  # function b(.) in SN book, eq.(4.15)
{# vectorized for 'nu', intended for values nu>1, otherwise it returns NaN
   out <- rep(NaN, length(nu))
   big <- (nu > 1e4)
   ok  <- ((nu > 1) & (!big) & (!is.na(nu)))  
   # for large nu use asymptotic expression (from SN book, exercise 4.6)
   out[big] <- sqrt(2/pi) * (1 + 0.75/nu[big] + 0.78125/nu[big]^2)
   out[ok] <-  sqrt(nu[ok]/pi) * exp(lgamma((nu[ok]-1)/2) - lgamma(nu[ok]/2))
   return(out)
}
#
st.gamma1 <- function(delta, nu)
{# this function is vectorized for delta, works for a single value of nu
  if(length(nu) > 1) stop("'nu' must be a single value")
  if(nu <= 0) stop("'nu' must be positive")
  out <- rep(NaN, length(delta)) 
  names(out) <- names(delta)
  ok <- (abs(delta) <= 1) 
  if((nu >= 3) & (sum(ok) > 0)) {
    alpha <- delta[ok]/sqrt(1 - delta[ok]^2)
    cum <- st.cumulants(0, 1, alpha, nu, n=3)
    out[ok] <- if(sum(ok) == 1) cum[3]/cum[2]^1.5 else cum[,3]/cum[,2]^1.5  
    }
  return(out) 
}
#     
st.gamma2 <- function(delta, nu) 
{# this function is vectorized for delta, works for a single value of nu
  if(length(nu) > 1) stop("'nu' must be a single value")
  if(nu <= 0) stop("'nu' must be positive")
  out <- rep(NaN, length(delta)) 
  names(out) <- names(delta)
  ok <- (abs(delta) <= 1)
  if((nu >= 4) & (sum(ok) > 0)) {
    alpha <- delta[ok]/sqrt(1 - delta[ok]^2)
    cum <- st.cumulants(0, 1, alpha, nu, n=4)
    out[ok] <- if(sum(ok) == 1) cum[4]/cum[2]^2 else cum[,4]/cum[,2]^2
    }
  return(out)  
}
#
st.cp2dp <- 
function(cp, cp.type="proper", start=NULL, silent=FALSE, tol=1e-8, trace=FALSE) 
{
  if(any(is.na(cp))) stop("NA's in argument 'cp'")
  if(!(cp.type %in% c("proper", "pseudo"))) stop("invalid cp.type") 
  a <- if(cp.type == "proper") rep(0,4) else (1:4) 
  p <- length(cp)-3
  x.names <- if(p>1) names(cp[2:p]) else NULL
  gamma1 <- cp[p+2]
  abs.g1 <- abs(gamma1)
  gamma2 <- cp[p+3]
  tiny <- sqrt(.Machine$double.eps)
  fn0 <- function(log.nu, g1, a) st.gamma1(1, exp(log.nu) + a[3]) - g1
  if(abs.g1 <=  0.5*(4-pi)*(2/(pi-2))^1.5) {
    sn.gamma2 <- 2*(pi-3)*(2*abs.g1/(4-pi))^(4/3)   # SN book: (2.29)+(3.20)
    margin <- (gamma2 - sn.gamma2)
    if(abs(margin) < tiny) return(c(cp2dpUv(cp[-length(cp)], "SN"), nu=Inf))
    feasible <- (margin > 0) 
    excess <- max(0, sn.gamma2 - gamma2)
    } 
  else {
    if(abs.g1 >= 4 & cp.type=="proper") {
        feasible <- FALSE; excess <- Inf
      } else {
        r0 <- uniroot(fn0, c(log(4-a[4]+tiny), 1000), tol=tol, g1=abs.g1, a=a)
        nu0 <- exp(r0$root) + a[3] 
        feasible <- (gamma2 >= st.gamma2(1, nu0+a[4]))
        excess <- max(0, st.gamma2(1, nu0+a[4]) - gamma2)
      }
    }
  if(!feasible) {
    if(silent) {
      out <- NA
      attr(out, "excess") <- excess
      return(out)} 
    else stop("CP outside feasible region")}
  if(is.null(start)){  
    delta <- 0.75 * sign(gamma1)
    old <- c(delta, Inf)
    } else {
    delta <- start[p+2]/sqrt(1+start[p+2]^2)
    old <- c(delta, start[p+3])
    }
  step <- Inf
  fn1 <- function(delta, g1, nu, a) st.gamma1(delta, nu+a[3]) - g1
  fn2 <- function(log.nu, g2, delta, a) st.gamma2(delta, exp(log.nu)+a[4]) - g2
  out <- NULL
  while(step > tol){
    fn21 <- fn2(log(4 - a[4]+ tiny), gamma2, delta, a)
    fn22 <- fn2(log(1e9), gamma2, delta, a)
    if(any(is.na(c(fn21, fn22)))) stop("parameter inversion failed")  
    if(fn21 * fn22 > 0) {
      out <- NA
      attr(out, "excess") <- fn21*fn22
      break}
    r2 <- uniroot(fn2, interval=c(log(4-a[4] +sqrt(.Machine$double.eps)), 100), 
           tol=tol, g2=gamma2, delta=delta, a=a)
    nu <- exp(r2$root)
    if(fn1(-1, gamma1, nu, a) * fn1(1, gamma1, nu, a)> 0) {
      out <- NA
      attr(out, "excess") <- fn1(-1, gamma1, nu, a) * fn1(1, gamma1, nu, a=a)
      break}
    r1 <- uniroot(fn1, interval=c(-1,1), tol=tol, g1=gamma1, nu=nu, a=a)
    delta <- r1$root
    new <- c(delta, nu)
    step <- abs(old-new)[1] + abs(log(old[2])- log(new[2]))
    if(trace) 
      cat("[st.cp2dp] delta, nu, log(step):", format(c(delta, nu, log(step))),"\n")
    old <- new
    }
  if(anyNA(out)) return(out)
  mu.z <- function(delta, nu) delta*b(nu)
  kappa2 <- function(delta,nu) nu/(nu-2) - mu.z(delta,nu)^2
  omega <- cp[p+1]/sqrt(kappa2(delta, nu+a[2]))
  xi <- cp[1] - omega*mu.z(delta, nu+a[1])
  if(omega < 0) {
    if(silent) {
      out <- NA
      attr(out, "excess") <- abs(omega)
      return(out)} 
    else stop("CP outside feasible region")}
  alpha <- delta/sqrt(1-delta^2)
  dp <- c(xi, if(p>1) cp[2:p] else NULL, omega, alpha, nu)
  names(dp) <- param.names("DP", "ST", p, x.names=x.names)
  return(dp)
}

mst.cp2dp <- function(cp, silent=FALSE, tol=1e-8, trace=FALSE) 
{
  mu <- drop(cp[[1]])
  Sigma <- cp[[2]]
  gamma1 <- cp[[3]]
  gamma2M <- cp[[4]]
  d <- length(gamma1)
  # fn1 <- function(delta, g1, nu) st.gamma1(delta, nu) - g1
  # fn2 <- function(log.nu, g2, delta.sq, d)
  #                mst.gamma2M(delta.sq, exp(log.nu), d) - g2
  if(any(abs(gamma1) >= 4)) 
    {if(silent) return(NULL) else stop("cp$gamma1 not admissible")}
  dp.marg <- matrix(NA, d, 4)
  for(j in 1:d) {  
     dp <- st.cp2dp(c(0,1,gamma1[j], gamma2M), silent=silent)
     if(is.null(dp)) 
       {if(silent) return(NULL) else stop("no CP could be found")}
     dp.marg[j,] <- dp
  }
  if(trace) cat("[mst.cp2dp] starting dp values:", dp.marg, "\n")
  fn <- function(par, Sigma, gamma1, gamma2M, trace=FALSE){
    if(trace)  cat("[mst.cp2dp[fn]] par:", format(par), "\n")
    nu <- exp(par[1])+4
    delta <- par[-1]/sqrt(1+par[-1]^2)
    d <- length(delta)
    mu.z <- delta*b(nu)
    omega <- sqrt(diag(Sigma)/(nu/(nu-2)-mu.z^2))
    Omega.bar <- (diag(1/omega, d, d) %*% Sigma %*% diag(1/omega, d, d)
                   + outer(mu.z, mu.z)) * (nu-2)/nu
    Obar.inv <- pd.solve(force.symmetry(Omega.bar))
    delta.sq <- sum(delta * as.vector(Obar.inv %*% delta))
    if(delta.sq >= 1) return(delta.sq*10^10)
    L1 <- sum((st.gamma1(delta, nu) - gamma1)^2)
    L2 <- (mst.mardia(delta.sq, nu, d)[2] - gamma2M)^2
    # if(trace){  ecat(c(nu,delta,L1,L2))} # ; readline("<cr>")}
    L1 + L2
    }
  nu <- min(dp.marg[,4])
  par <- c(log(nu-4), dp.marg[,3])
  if(trace) cat("[mst.cp2dp] par:", format(par), "\n")
  opt <- nlminb(par, fn, Sigma=Sigma, gamma1=gamma1, gamma2M=gamma2M,
                trace=trace)
  if(trace) {
    cat("[mst.cp2dp] outcome from optimization step\n")
    cat("opt$convergence:", opt$convergence, "\n")
    cat("nopt$message", opt$message, "\n")
    }
  if(opt$convergence != 0) 
    { if(silent) return(NULL) else stop ("no CP could be found") }
  par <- opt$par
  nu <- exp(par[1])+4
  delta <- par[-1]/sqrt(1+par[-1]^2)
  if(trace) {
    cat("[mst.cp2dp] min opt$fn:", format(opt$obj),"\n")
    print(c(nu,delta))
    }
  mu.z <- delta*b(nu)
  omega<- sqrt(diag(Sigma)/(nu/(nu-2)-mu.z^2))
  Omega.bar <- (diag(1/omega, d, d) %*% Sigma %*% diag(1/omega, d, d)
                   + outer(mu.z,mu.z)) * (nu-2)/nu
  Obar.inv <- pd.solve(Omega.bar)
  delta.sq <- sum(delta * as.vector(Obar.inv %*% delta))
  alpha <- as.vector(Obar.inv %*% delta)/sqrt(1-delta.sq)
  if(is.matrix(mu)) {
     xi <- mu
     xi[1,] <- mu[1,] - omega*mu.z }
  else xi <- mu - omega*mu.z
  Omega <- diag(omega) %*% Omega.bar %*% diag(omega)
  return(list(xi=xi, Omega=Omega, alpha=alpha, nu=nu))
}
 

affineTransSECdistr <- function(object, a, A, name, compNames, drop=TRUE)
{# object is of class SECdistrMv
 # computes distribution of affine transformation of SEC variable T=a+t(A)Y
  if(!is(object, "SECdistrMv")) stop("wrong object class")
  dp <- slot(object, "dp")
  alpha <- dp$alpha
  d <- length(alpha)
  if(!is.matrix(A) || nrow(A) != d) stop("A is not a matrix or wrong nrow(A)")
  h <- ncol(A)
  if(length(a) != h) stop("size mismatch of arguments 'a' and 'A'")
  if(missing(name)) name<- paste(deparse(substitute(a)), " + t(",  
    deparse(substitute(A)), ") %*% (", deparse(substitute(object)),")", sep="")
  else name <- as.character(name)[1]
  compNames <- if(missing(compNames)) 
    as.vector(outer("V",as.character(1:h),paste,sep=""))
    else as.character(as.vector(compNames)[1:h])
  family <- object@family
  xi.X  <- as.vector(a + t(A) %*% matrix(dp$xi, ncol=1))
  Omega <- dp$Omega
  omega <- sqrt(diag(Omega))
  Omega.X <- force.symmetry(t(A) %*% Omega %*% A) 
  invOmega.X <- pd.solve(Omega.X, silent=TRUE)
  if (is.null(invOmega.X)) stop("not full-rank transformation") 
  omega.X <- sqrt(diag(Omega.X))
  omega.delta <- omega * delta.etc(alpha, Omega)$delta
  m <- as.vector(invOmega.X %*% t(A) %*% matrix(omega.delta, ncol=1))
  u <- sum(omega.delta * as.vector(A %*% matrix(m, ncol=1)))
  alpha.X <- (omega.X * m)/sqrt(1 - u)
  dp.X <- list(xi=xi.X, Omega=Omega.X, alpha=alpha.X)
  if(family == "ESN") dp.X$tau <- dp$tau
  if(family == "ST") dp.X$nu <- dp$nu
  if(h==1 & drop) {
     dp1 <- unlist(dp.X)
     dp1[2] <- sqrt(dp1[2])
     names(dp1) <- names(dp.X) 
     names(dp1)[2] <- tolower(names(dp)[2])
     new.obj <- makeSECdistr(dp=dp1, family=family, name=name)
     } else 
  new.obj <- makeSECdistr(dp.X, family, name, compNames) 
  return(new.obj)
}
  
                       
marginalSECdistr <- function(object, comp, name, drop=TRUE)   
{# marginals of SECdistrMv obj; version 2, computing marginal delta's
  family <- slot(object,"family")
  if(missing(name)) {
     basename <- if(object@name != "") object@name 
                 else deparse(substitute(object))               
     name <- if(length(comp)>1) paste(basename, "[",
                paste(as.character(comp), collapse=","), "]", sep="") else
                paste(basename, "[", as.character(comp), "]", sep="")
     }
  else name <- as.character(name)[1]
  dp <- slot(object,"dp")
  xi    <- dp$xi
  Omega <- dp$Omega
  alpha <- dp$alpha
  compNames <- slot(object,"compNames")
  d <- length(alpha)
  comp <- as.integer(comp)
  Omega11 <- Omega[comp,comp,drop=FALSE]
  if(length(comp) < d){
    if(any(comp>d | comp<1)) stop("comp makes no sense")
    delta_etc <- delta.etc(alpha, Omega)
    delta1 <- delta_etc$delta[comp]
    R11 <- delta_etc$Omega.cor[comp, comp, drop=FALSE]
    iR11.delta1 <- as.vector(pd.solve(R11, silent=TRUE) %*% delta1)
    diRd <- sum(delta1*iR11.delta1)
    alpha1_2 <- if(diRd < 1) iR11.delta1/sqrt(1 - diRd) else sign(delta1)*Inf
    dp0 <- list(xi=xi[comp], Omega=Omega11, alpha=alpha1_2)
  }
  else {
    if(any(sort(comp) != (1:d))) stop("comp makes no sense")
    dp0 <- list(xi=xi[comp], Omega=Omega11, alpha=alpha[comp])
  }
  if(family=="ESN") dp0$tau <- dp$tau
  if(family=="ST") dp0$nu <- dp$nu
  new.obj <- new("SECdistrMv", dp=dp0, family=family, name=name, 
                 compNames=compNames[comp])
  if(length(comp)==1 & drop) 
    {# new.obj <- as(new.obj, "SECdistrUv") # non va..
     dp <- unlist(dp0)
     names(dp) <- names(dp0)
     dp[2] <- sqrt(dp[2])
     names(dp)[2] <- "omega"
     new.obj <- new("SECdistrUv", dp=dp, family=family, name=name)
     }
  new.obj
}
                     
conditionalSECdistr <- 
function(object, fixed.comp, fixed.values, name, drop=TRUE)
{ # conditional distribution of SN/ESN object 
  family <- slot(object,"family")
  if(!(family %in% c("SN", "ESN"))) stop("family must be either SN or ESN")
  dp <- slot(object,"dp")
  xi    <- dp$xi
  Omega <- dp$Omega
  alpha <- dp$alpha
  tau   <- if(family=="SN") 0 else dp$tau
  d <- length(alpha)
  fix <- fixed.comp
  h <- length(fix)
  if(any(fix != round(fix)) | !all(fix %in% 1:d) | h == d) 
    stop("fixed.comp makes no sense")
  if(length(fixed.values) != h) 
    stop("length(fixed.comp) != lenght(fixed.values)")
  compNames <- slot(object,"compNames")
  if(missing(name)) {
     basename <- if(object@name != "") object@name 
                 else deparse(substitute(object))
     name<- paste(basename,"|(",
                paste(compNames[fix],collapse=","), ")=(", 
                paste(format(fixed.values),collapse=","), ")",
                sep="")
     }
  else name <- as.character(name)[1]
  # free.fix <- setdiff(1:d, fix)
  omega <- sqrt(diag(Omega))
  omega1 <- omega[fix]
  omega2 <- omega[-fix]
  R   <- cov2cor(Omega)
  R11 <- R[fix,fix, drop=FALSE]
  R12 <- R[fix,-fix, drop=FALSE]
  R21 <- R[-fix,fix, drop=FALSE]
  R22 <- R[-fix,-fix, drop=FALSE]
  alpha1 <- matrix(alpha[fix], ncol=1)
  alpha2 <- matrix(alpha[-fix], ncol=1)
  iR11  <- pd.solve(R11)
  R22.1 <- R22 - R21 %*% iR11 %*% R12
  a.sum <- as.vector(t(alpha2) %*% R22.1 %*% alpha2)
  alpha1_2 <- as.vector(alpha1 + iR11 %*% R12 %*% alpha2)/sqrt(1+a.sum)
  tau2.1 <- (tau * sqrt(1 + sum(alpha1_2 * as.vector(iR11 %*% alpha1_2)))
             + sum(alpha1_2 * (fixed.values-xi[fix])/omega1))
  O11 <- Omega[fix,fix, drop=FALSE]
  O12 <- Omega[fix,-fix, drop=FALSE]
  O21 <- Omega[-fix,fix, drop=FALSE]
  O22 <- Omega[-fix,-fix, drop=FALSE]
  iO11<- (1/omega1) * iR11 * rep(1/omega1, each=h)  # solve(O11)
  reg <- O21 %*% iO11
  xi2.1 <- as.vector(xi[-fix]+ reg %*% (fixed.values - xi[fix]))
  O22.1 <- O22 - reg %*% O12
  omega22.1 <- sqrt(diag(O22.1))
  alpha2.1 <- as.vector((omega22.1/omega2)*alpha2)
  dp2.1 <- list(xi=xi2.1, Omega=O22.1, alpha=alpha2.1, tau=tau2.1)
  obj <- if((d-h)==1 & drop) {
    dp2.1 <- unlist(dp2.1)
    dp2.1[2] <- sqrt(dp2.1[2])
    names(dp2.1) <- c("xi","omega","alpha","tau")
    new("SECdistrUv", dp=dp2.1, family="ESN", name=name)
    } else new("SECdistrMv", dp=dp2.1, family="ESN", name=name, 
               compNames=compNames[-fix])   
  return(obj)
}


delta.etc <- function(alpha, Omega=NULL) 
{ 
  inf <- which(abs(alpha) == Inf)
  if(is.null(Omega) | length(Omega) == 1){ # case d=1
    delta <- alpha/sqrt(1+alpha^2)
    delta[inf] <- sign(alpha[inf])
    return(delta)
    }
  else { # d>1
    if(any(dim(Omega) != rep(length(alpha),2))) stop("dimension mismatch")
    Ocor <- cov2cor(Omega)
    if(length(inf) == 0) { # d>1, standard case
      Ocor.alpha <- as.vector(Ocor %*% alpha)
      alpha.sq <- sum(alpha * Ocor.alpha)
      delta <- Ocor.alpha/sqrt(1 + alpha.sq)
      alpha. <- sqrt(alpha.sq)
      delta. <- sqrt(alpha.sq/(1 + alpha.sq))
      }
     else { # d>1, case with some abs(alpha)=Inf
       if(length(inf) > 1) 
         warning("Several abs(alpha)==Inf, I handle them as 'equal-rate Inf'") 
       k <- rep(0,length(alpha))
       k[inf] <- sign(alpha[inf])
       Ocor.k <- as.vector(Ocor %*% k) 
       delta <- Ocor.k/sqrt(sum(k * Ocor.k))
       delta. <- 1
       alpha. <- Inf
       }
  return(
    list(delta=delta, alpha.star=alpha., delta.star=delta., Omega.cor=Ocor))
  }
}

selm <- function (formula, family="SN", data, weights, subset, na.action, 
    start=NULL, fixed.param=list(), method="MLE",  penalty=NULL, 
    model=TRUE, x = FALSE, y = FALSE,  contrasts = NULL, offset,  ...) 
{
    ret.x <- x
    ret.y <- y
    cl <- match.call()
    formula <- as.formula(formula)
    if (length(formula) < 3)  stop("formula must be a two-sided formula")
    mf <- match.call(expand.dots = FALSE)
    m <- match(c("formula", "data", "subset", "weights", "na.action", 
        "offset"), names(mf), 0L)
    mf <- mf[c(1L, m)]
    mf$drop.unused.levels <- TRUE 
    mf[[1L]] <- as.name("model.frame")  # in lm(): quote(stats::model.frame)
    mf <- eval(mf, parent.frame())
    method <- toupper(method)
    if(!(method %in% c("MLE", "MPLE"))) {
      warning(gettextf("method = '%s' is not supported, replaced by 'MLE'", 
         method), domain = NA)
      method <- "MLE"}
    penalty.name <- if(method == "MPLE") {
      if(is.null(penalty)) "Qpenalty" else penalty }
      else   NULL  
    contr <- list(penalty=penalty.name, trace=FALSE,  info.type="observed", 
                  opt.method="nlminb", opt.control=list())
    control <- list(...)
    contr[(namc <- names(control))] <- control
    if (length(noNms <- namc[!namc %in% names(contr)])) warning(
       "unknown names in control: ", paste(noNms, collapse = ", "))
    mt <- attr(mf, "terms")
    y <- model.response(mf, "numeric")
    w <- as.vector(model.weights(mf))
    if(is.null(w))  w <- rep(1, NROW(y))      
    if(any(w != round(w)) | all(w == 0))
      stop("weights must be non-negative integers (=frequencies), not all 0")  
    offset <- as.vector(model.offset(mf))
    if (!is.null(offset)) {
      if (length(offset) == 1) 
        offset <- rep(offset, NROW(y))
      else if (length(offset) != NROW(y)) 
        stop(gettextf(
         "number of offsets is %d, should equal %d (number of observations)", 
         length(offset), NROW(y)), domain = NA)
      }          
    if(length(fixed.param) > 0) {
      if(!all(names(fixed.param)  %in%  c("nu", "alpha")))
        stop("Not admissible component of 'fixed.param'")
      if(!is.null(fixed.param$alpha)) { 
        if(fixed.param$alpha != 0) stop("'alpha' can only be fixed at 0")
        if(method == "MPLE") stop('method MPLE not allowed when alpha=0')      
        }
      }  
    if (is.empty.model(mt)) stop("empty model") else
    {
      x <- model.matrix(mt, mf, contrasts)                    
      xt <- pd.solve(force.symmetry(t(x) %*% (w*x)), silent=TRUE)
      if(is.null(xt)) stop("design matrix appears to be of non-full rank")
      z <- selm.fit(x, y, family=family, start, w=w, fixed.param=fixed.param, 
             offset=offset, selm.control=contr)
    }
    class(z) <- c(if (is.matrix(y)) "mselm", "selm")
    z$na.action <- attr(mf, "na.action")
    z$offset <- offset
    z$contrasts <- attr(x, "contrasts")
    z$xlevels <- .getXlevels(mt, mf)
    z$call <- cl
    z$terms <- mt
    input <- list()
    if (model) input$model <- mf
    if (ret.x) input$x <- x
    if (ret.y) input$y <- y
    # input$weights <- as.vector(model.weights(mf))
    # input$offset <- as.vector(model.offset(mf))
    # cl.obj <- if(is.matrix(y)) "mselm" else "selm"
    obj <- new(class(z), call=cl, family=toupper(family), logL=z$logL, 
               method=c(method, contr$penalty),  param=z$param,
               param.var=z$param.var, size=z$size,  
               residuals.dp=z$resid.dp, fitted.values.dp=z$fitted.dp,
               control=control, input=input, opt.method=z$opt.method)
    return(obj)
}
#
#selm.control <- function(method="MLE", info.type="observed",  
#   trace=FALSE, algorithm="nlminb", opt.control=list()) 
#{     
#  if(algorithm !="nlminb") stop("only algorithm='nlminb' handled so far")
#  if(info.type !="observed") stop("only info.type='observed' handled so far")
#  list(method=method, info.type=info.type,  trace=trace, 
#    algorithm=algorithm, opt.control=opt.control)
#}


#------------------------------------------------------
selm.fit <- function(x, y, family="SN", start=NULL, w, fixed.param=list(), 
                 offset = NULL, selm.control=list()) 
{
    if (!(toupper(family) %in% c("SN", "ST", "SC")))
        stop(gettextf("I do not know family '%s'", family), domain = NA)
    family <- toupper(family)    
    if (is.null(n <- nrow(x))) stop("'x' must be a matrix")
    if (n == 0L) stop("0 (non-NA) cases")
    if(NROW(y) != n) stop("'x' and 'y' have non-compatible dimensions")
    p <- ncol(x)
    if ((p == 0L) || !(all(data.matrix(x)[,1] == 1))) 
      stop("first column of model matrix is not all 1's")
    y <- drop(y)
    d <- NCOL(y)
    if(d>1 && is.null(colnames(y))) colnames(y) <- paste("V", 1:d, sep="") 
    if(is.null(colnames(x))) colnames(x) <- paste("x", 0L:(p-1), sep=".")
    if (!is.null(offset))  y <- (y - offset)
    if (NROW(y) != n)  stop("incompatible dimensions")
    if (missing(w) || is.null(w)) w <- rep(1, n)
    nw <- sum(w)
    n.obs <- NROW(y)
    contr <- list(method="MLE", penalty=NULL, trace=FALSE,  
                 info.type="observed", opt.method="nlminb", opt.control=list())
    control <- selm.control
    contr[(namc <- names(control))] <- control   
    symmetr <- FALSE   
    if(length(fixed.param) > 0) {
      if(!all(names(fixed.param)  %in%  c("nu", "alpha")))
        stop("Not admissible component of 'fixed.param'")
      if(!is.null(fixed.param$alpha)) {      
        if( fixed.param$alpha != 0 ) stop("'alpha' can only be fixed at 0") 
        else symmetr <- TRUE }
      }    
    zero.weights <- any(w == 0)
    if(zero.weights) {
      save.r <- y
      save.f <- y
      save.w <- w
      ok <- (w != 0)
      nok <- !ok
      w <- w[ok]
      x0 <- x[!ok, , drop = FALSE]
      x <- x[ok, , drop = FALSE]
      n <- nrow(x)
      y0 <- if (d > 1L) y[!ok, , drop = FALSE] else y[!ok]
      y <- if (d > 1L) y[ok, , drop = FALSE] else y[ok]
      }
    storage.mode(x) <- "double"
    storage.mode(y) <- "double"
    info.type <- contr$info.type # so far, only "observed"
    yInfo <- if(contr$info.type == "observed") y else NULL
    penalty <- contr$penalty  # either NULL or a char string 
    penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE) 
    trace <- contr$trace
    if(d == 1) {
      y <- as.vector(y) 
      if(family == "SN") {
        npar <- p + 2 - as.numeric(symmetr)
        if(symmetr) { # SN with alpha=0 is the Gaussian distribution
          ls <- lm.wfit(x, y, w) # note: offset already subtracted if any
          res <- residuals(ls)
          s2 <- sum(w*res^2)/nw
          dp <- cp <- param <- c(coef(ls), sqrt(s2))
          x.names <- if(p==1) NULL else colnames(x)[-1]
          names(dp) <- param.names("DP", "SN", p, x.names)[1:npar]
          names(cp) <- param.names("CP", "SN", p, x.names)[1:npar]
          j <- rbind(cbind(t(x) %*% (w*x)/s2, 0), c(rep(0,p), 2*nw/s2))
          j.inv <- pd.solve(j)
          se <- sqrt(diag(j.inv))
          info <- list(dp=param, cp=param, info.dp=j, info.cp=j, 
                   asyvar.dp=j.inv, asyvar.cp=j.inv, se.dp=se, se.cp=se,
                   aux=NULL)
          logL <- (-0.5*nw)*(log(2*pi*s2) +1)
          fit <- list(cp=cp, dp=dp, dp.complete=c(dp,0), 
                      opt.method=list(ls$qr), logL=logL)  
          boundary <- FALSE
          fit$opt.method <- list(method="least_squares", called.by= "lm.wfit")
          mu0 <- 0
          fixed.comp <- p + 2
          fixed.value <- 0
          }
        else { # proper SN case 
        cp <- if(is.null(start)) NULL else dp2cpUv(start, "SN")
        fit <- sn.mple(x, y, cp, w, penalty, trace, contr$opt.method, 
                 contr$control)
        fit$dp <- cp2dpUv(cp=fit$cp, family="SN")
        boundary <- fit$boundary
        mu0 <- fit$cp[1] - fit$dp[1]
        info <- if(boundary) NULL else 
          sn.infoUv(dp=fit$dp, x=x, y=yInfo, w=w, penalty=penalty)
        }}
      if(family == "ST" | family == "SC") {
        fixed.nu <- fixed.param$nu  
        if(family == "SC") fixed.nu <- 1
        fixed.comp <- fixed.value <- NULL
        if(symmetr) {
          fixed.comp <- p+2 
          fixed.value <- 0
          } 
        if(!is.null(fixed.nu)) {
          fixed.comp <- c(fixed.comp, p+3)
          fixed.value <- c(fixed.value, fixed.nu)
          }
        # free: the free components of (full) DP, those not in fixed.comp
        free <- setdiff(1:(p+3), fixed.comp)
        npar <- length(free)
        fit <- st.mple(x, y, dp=start, w, fixed.nu, symmetr, penalty, trace,
           contr$opt.method, contr$control)
        dp <- fit$dp   
        dp.complete <- fit$dp.complete
        fit$cp <- cp <- st.dp2cp(dp.complete, cp.type="proper")[free]
        pseudo_cp <- st.dp2cp(dp.complete, cp.type="pseudo", jacobian=TRUE)
        fit$p_cp <- p_cp <- pseudo_cp[free]
        Dpcp.dp <- attr(pseudo_cp, "jacobian")[free, free] 
        boundary <- fit$boundary
        nu <- if(is.null(fixed.nu)) dp[npar] else fixed.nu
        mu0 <- if(nu <= 1) NA else { if(symmetr) 0 else
                  st.dp2cp(dp.complete, upto=1)[1] - dp[1] }
        info <- if(boundary)  NULL  else 
          st.infoUv(dp=fit$dp, NULL, x, yInfo, w, fixed.nu, symmetr, penalty)                
        }
      if(!boundary && family %in% c("ST","SC"))  {
        # 2018-04-24
        u <- try(Dpcp.dp %*% info$asyvar.dp %*% t(Dpcp.dp), silent=TRUE)
        info$asyvar.p_cp <- if(inherits(u, "try-error"))  NULL else u
        }
      beta.dp <- fit$dp[1:p]
      dp <- fit$dp
      cp <- fit$cp
      }
    else { # d>1
      npar0 <- p*d + d*(d+1)/2
      if(family == "SN") {
        if(symmetr) { # SN with alpha=0 is Gaussian case
          npar <-  npar0
          ls <- lm.wfit(x, y, w) # note: offset already subtracted if any
          beta <- coef(ls)
          res <- residuals(ls)
          s2 <- t(res) %*% (w*res)/nw
          dp <- dp. <- list(beta=beta, Omega=s2)
          dp.$alpha <- rep(0,d)
          param <- c(beta, vech(s2))
          conc <- solve(s2)
          betaBlock <- conc %x% (t(x) %*% (w*x))
          D <- duplicationMatrix(d)
          varBlock <- (n/2) * t(D) %*% (conc %x% conc) %*% D
          m0 <- matrix(0, p*d, d*(d+1)/2)
          j <- rbind(cbind(betaBlock, m0), cbind(t(m0), varBlock)) 
          # use (10) in section 15.8 of Magnus & Neudecker (1988/1999, p.321)
          j.inv <- rbind(cbind(solve(betaBlock), m0), 
                         cbind(t(m0), solve(varBlock))) 
          diags.dp <- sqrt(diag(j.inv))
          se.beta <- matrix(diags.dp[1:(p*d)], p, d)
          se.diagOmega <- diags.dp[p*d + d*(d+1)/2 +1 -rev(cumsum(1:d))]
          se <- list(beta=se.beta, diagOmega=se.diagOmega)
          info <- list(dp=param, cp=param, info.dp=j, info.cp=j, 
                   asyvar.dp=j.inv, asyvar.cp=j.inv, se.dp=se, se.cp=se,           
                   aux=NULL)
          logL <- (-0.5*nw)*(determinant(2*pi*s2, logarithm=TRUE)$modulus + d)
          # see (6.2.7) of Mardia, Kent & Bibby (1979)
          fit <- list(dp=dp, cp=dp, dp.complete=dp., logL=logL)  
          fit$opt.method <- list(method="lm.wfit")
          boundary <- FALSE
          mu0 <- rep(0, d)
          }
        else { # proper SN case 
        npar <-  npar0 + d
        if(is.null(penalty)) { # MLE
          fit <- msn.mle(x, y, start, w, trace=trace, 
                 opt.method=contr$opt.method, control=contr$opt.control)
          boundary <- ((1 - fit$aux$delta.star) < .Machine$double.eps^(1/4))
          if(!boundary) info <- 
             sn.infoMv(fit$dp, x=x, y=yInfo, w=w)
          } else { # MPLE
          fit <- msn.mple(x, y, start, w, penalty, trace=trace, 
                   opt.method=contr$opt.method, control=contr$opt.control)
          boundary <- FALSE
          info <- sn.infoMv(fit$dp, x=x, y=y, w=w, penalty=penalty)
          }
        fit$cp <- msn.dp2cp(fit$dp)
        mu0 <- as.vector(fit$cp[[1]][1,] - fit$dp[[1]][1,])
        }}
      if(family == "ST"){
        fixed.nu <- fixed.param$nu 
        npar <- npar0 + d*as.numeric(!symmetr) + as.numeric(is.null(fixed.nu))
        fit <- mst.mple(x, y, start, w, fixed.nu=fixed.nu, symmetr=symmetr,
                  penalty=penalty, trace=trace, opt.method=contr$opt.method, 
                  control=contr$opt.control)
        fit$opt.method$called.by <- "mst.mple"
        boundary <- fit$boundary
        dp <- fit$dp
        nu <- if(is.null(fixed.nu)) dp$nu else fixed.nu
        mu0 <- if(nu <= 1) NA else { if(symmetr) rep(0,d) else
                  c(mst.dp2cp(dp, fixed.nu=fixed.nu, symmetr=symmetr, 
                      upto=1)[[1]][1,] - dp[[1]][1,])}
        fit$cp <- mst.dp2cp(dp, cp.type="proper", fixed.nu, symmetr)
        fit$p_cp <- mst.dp2cp(dp, cp.type="pseudo", fixed.nu, symmetr)
        if(!boundary) info <- 
           st.infoMv(dp, x=x, y=yInfo, w, fixed.nu, symmetr, penalty)
        }
      if(family == "SC") {
        npar <- npar0 + d*as.numeric(!symmetr)
        if(is.null(start)) {
          fit.sn <- msn.mle(x, y, NULL, w, control=list(rel.tol=1e-4))
          start <- fit.sn$dp  
          }
        fit <- mst.mple(x, y, start, w,  fixed.nu=1, symmetr=symmetr,
                  penalty=penalty, trace=trace,
                  opt.method=contr$opt.method, control=contr$opt.control)
        fit$opt.method$called.by <- "mst.mple"
        npar <- p*d + d*(d+1)/2 + d*as.numeric(!symmetr)
        boundary <- fit$boundary 
        mu0 <- NA
        fit$cp <- NULL
        fit$p_cp <- mst.dp2cp(fit$dp, "pseudo", fixed.nu=1)   
        if(!boundary)  info <-
          st.infoMv(fit$dp, x=x, y=yInfo, w, fixed.nu=1, symmetr, penalty)
        }
      beta.dp <- fit$dp[[1]]
      }
    param <- list(dp=fit$dp, cp=fit$cp, "pseudo-cp"=fit$p_cp, 
                boundary=boundary, mu0=mu0)
    if(!boundary && !is.null(info)) {
      asyvar.dp <- info$asyvar.dp[1:npar, 1:npar] 
      asyvar.cp <- info$asyvar.cp[1:npar, 1:npar]
      asyvar.p_cp <- info$asyvar.p_cp[1:npar, 1:npar]
      param.var <- list(info.type=info.type, dp=asyvar.dp, cp=asyvar.cp, 
        "pseudo-cp"=asyvar.p_cp) 
      } 
    else  param.var <- list()
    dn <- colnames(x)  
    fv <- drop(x %*% beta.dp)
    if(is.matrix(fv)) colnames(fv) <- colnames(y)
    size <- c(d=d, p=p, n.param=npar, n.obs=n.obs, nw.obs=sum(w)) 
    z <- list(call=match.call(), logL=fit$logL, param=param, 
            param.var=param.var, fitted.dp=fv, resid.dp=y-fv, size=size,
            selm.control=contr, opt.method=fit$opt.method)
    r1 <- y - z$resid.dp 
    z$weights <- w
    if (zero.weights) {
        # coef[is.na(coef)] <- 0
        f0 <- x0 %*% beta.dp
        if (d > 1) {
            save.r[ok, ] <- z$resid.dp
            save.r[nok, ] <- y0 - f0
            save.f[ok, ] <- z$fitted.dp
            save.f[nok, ] <- f0
        }
        else {
            save.r[ok] <- z$resid.dp
            save.r[nok] <- y0 - f0
            save.f[ok] <- z$fitted.dp
            save.f[nok] <- f0
        }
        z$resid.dp <- save.r
        z$fitted.dp <- save.f
        z$weights <- save.w
    }
  if(!is.null(offset)) {
      z$fitted.dp <- z$fitted.dp + offset
      r1 <- r1 + offset
      }
    # z$fitted.dp <- r1
  if(length(fixed.param) > 0)  {
    z$param$fixed <- fixed.param 
    if(d==1) 
      z$param$fixed.terms <- list(fixed.comp=fixed.comp, fixed.value=fixed.value)
    } else  z$param$fixed <- list()       
  z$param$dp.complete <- fit$dp.complete 
  return(z)
}

#---------------------------------------------------

summary.selm <- function(object, param.type="CP", cov=FALSE, cor=FALSE)
{
  family <- slot(object,"family")
  fixed <- slot(object, "param")$fixed
  if(length(fixed$alpha==0)>0 && fixed$alpha==0 & family=="ST") {
    param.type <- "DP"
    note <- "ST model with alpha=0 is summarized with param.type=DP"} 
  else note <- ""
  lc.param.type <- tolower(param.type) 
  if(!(lc.param.type %in% c("cp", "op", "dp", "pseudo-cp")))
     stop(gettextf("unknown param.type '%s'", param.type), domain = NA)     
  param.type <- switch(lc.param.type, 
     "dp"="DP", "op"="OP", "cp"="CP", "pseudo-cp"="pseudo-CP")

  if(param.type=="pseudo-CP" && !(family %in% c("ST", "SC"))) 
    stop("pseudo-CP makes sense only for ST and SC families")
  if (!(family %in% c("SN","ST","SC"))) 
     stop(gettextf("family '%s' is not handled", family), domain = NA)
  param <- slot(object, "param")[[lc.param.type]]
  if(param.type=="CP" && is.null(param)) { 
    if(family %in% c("ST", "SC")) {
      {message("CP does not exist. Consider param.type='DP' or 'pseudo-CP'") 
      return(invisible())}}}
  param.var <- slot(object, "param.var")[[lc.param.type]]
  if(is.null(param.var)) param.var <- diag(NA, length(param))
  se <- sqrt(diag(param.var))
  z <- param/se
  param.table <- cbind(param, se, z, 2*pnorm(-abs(z)))
  dimnames(param.table) <- list(names(param), 
    c("estimate", "std.err","z-ratio", "Pr{>|z|}"))
  resid <- residuals(object, lc.param.type)
  aux <- list()
  aux$param.cov <- if(cov) param.var else NULL
  aux$param.cor <- if(cor) cov2cor(param.var) else NULL
  new("summary.selm", call=slot(object,"call"), 
           family = slot(object, "family"), 
           logL = slot(object, "logL"),
           method=slot(object, "method"),
           resid = resid, 
           param.type = param.type,
           param.table = param.table,
           param.fixed = fixed,
           control = slot(object, "control"),
           aux = aux,
           boundary=slot(object, "param")$boundary,
           size=object@size,
           note=note)      
}


residuals.selm <- function(object, param.type="CP", ...){
  param.type <- tolower(param.type) 
  if(!(param.type %in% c("cp", "dp", "pseudo-cp"))) 
     stop("param.type must be either 'CP' or 'DP' or 'pseudo-CP'")
  # param <- slot(object, "param")[[param.type]]
  p <- object@size["p"]
  n <- object@size["n.obs"]
  r <- slot(object, "residuals.dp") 
  dp <- slot(object, "param")$dp
  pseudo.mu0 <- (slot(object, "param")$"pseudo-cp"[1] - dp[1])
  resid <- switch(param.type, 
     'dp' = r, 
     'cp' = r - rep(slot(object,"param")$mu0, n),
     'pseudo-cp' = r - rep(pseudo.mu0, n))
  # resid <- resid/param[p+1] # AA: standardize resid?
  w <- slot(object,"input")$weights
  if(!is.null(w)) attr(resid,"weights") <- w
  return(resid)
  }


fitted.selm <- function(object, param.type="CP", ...) {
  param.type <- tolower(param.type) 
  if(!(param.type %in% c("cp", "dp", "pseudo-cp")))
   stop("param.type must be either 'CP' or 'DP' or 'pseudo-CP'")
  # param <- slot(object, "param")[[param.type]]
  n <- object@size["n.obs"]
  dp <- slot(object, "param")$dp
  fit.dp <- slot(object,"fitted.values.dp")
  pseudo.mu0 <- (slot(object, "param")$"pseudo-cp"[1] - dp[1])
  fitted <- switch(param.type,
    'dp' = fit.dp,
    'cp' = fit.dp + rep(slot(object,"param")$mu0, n),
    'pseudo-cp' = fit.dp + rep(pseudo.mu0, n))
  w <- slot(object, "input")$weights
  if(!is.null(w)) attr(fitted,"weights") <- w
  return(fitted)
  }
  
weights.selm <- function(object, ...) slot(object, "input")$weights

summary.mselm <- function(object, param.type="CP", cov=FALSE, cor=FALSE) 
{
  fixed <- slot(object, "param")$fixed
  if(length(fixed$alpha==0)>0 && fixed$alpha==0) {
    param.type <- "DP"
    note <- "param.type=DP has been set because of constraint alpha=0"
    } else note <- ""
  lc.param.type <- tolower(param.type) 
  if(!(lc.param.type %in% c("dp", "op", "cp", "pseudo-cp")))
     stop(gettextf("unknown param.type '%s'", param.type), domain = NA)
  param.type <- switch(lc.param.type, 
     "dp"="DP", "op"="DP", "cp"="CP", "pseudo-cp"="pseudo-CP")
  # OP not yet implemented, currently re-directed to DP     
  family <- slot(object, "family")
  method <- slot(object, "method")
  if(param.type=="pseudo-CP" & !(family %in% c("ST","SC"))) 
    stop("pseudo-CP makes sense only for ST and SC families")
  p <- object@size["p"]
  d <- object@size["d"]
  npar <- object@size["n.param"]
  param <- object@param[[lc.param.type]]
  if(is.null(param) && family %in% c("ST", "SC")) {
    message("CP does not exist. Consider param.type='DP' or 'pseudo-CP'")
    return(invisible())}
  beta <- param[[1]]
  param.var <- slot(object, "param.var")[[lc.param.type]]
  if(object@param$boundary | is.null(param.var)) 
    param.var <- matrix(NA, npar, npar)
  coef.tables <- list()
  par.names <- param.names(param.type, family, p, x.names=rownames(beta)[-1])
  for(j in 1:d) {
    beta.j <- beta[,j]
    var.j <- param.var[((j-1)*p+1):(j*p), ((j-1)*p+1):(j*p), drop=FALSE]
    se.j <- sqrt(diag(var.j))
    z <- beta.j/se.j
    coef.table <- cbind(beta.j, se.j, z, 2*pnorm(-abs(z)))
    dimnames(coef.table) <- list(par.names[1:p], 
      c("estimate","std.err","z-ratio", "Pr{>|z|}"))
    coef.tables[[j]] <- coef.table
    }
  scatter <- list(matrix=param[[2]], name=names(param)[2])
  resid <- residuals.mselm(object, param.type)
  # resid <- t(t(resid)/sqrt(diag(scatter$matrix))) # for normalized/std resid
  if(is.null(fixed$alpha)) {
    se.slant <- sqrt(diag(param.var)[(p*d+d*(d+1)/2+1):(p*d+d*(d+1)/2+d)])
    slant <- list(param=param[[3]], se=se.slant, name=names(param)[3])} 
    else { if(fixed$alpha == 0) slant <- list() else 
       stop('cannot have fixed alpha at non-zero value, please report')} 
  tail <- if(family== "ST" & is.null(fixed$nu) )
             list(param=param[[length(param)]],     
              se=sqrt(diag(param.var)[npar]),  name=names(param)[length(param)]) 
          else list()
  aux <- list()
  aux$param.cov <- if(cov) param.var else NULL
  aux$param.cor <- if(cor) cov2cor(param.var) else NULL
  out <- new("summary.mselm", call=slot(object,"call"), 
           family = family, 
           logL = slot(object, "logL"),
           method=slot(object, "method"),
           resid = resid,
           param.type=param.type,
           coef.tables = coef.tables,
           param.fixed = fixed,
           scatter = scatter,
           slant = slant,
           tail = tail,
           control = slot(object, "control"),
           aux = aux,
           boundary=slot(object, "param")$boundary,
           size=slot(object, "size"),
           note=note)
   out        
}

residuals.mselm <- function(object, param.type="CP", ...){
  param.type <- tolower(param.type) 
  if(!(param.type %in% c("cp", "dp", "pseudo-cp"))) 
     stop("param.type must be either 'CP' or 'DP' or 'pseudo-CP'")
  # param <- slot(object, "param")[[param.type]]
  # beta <- param[[1]]
  n <- object@size["n.obs"]
  r <- slot(object,"residuals.dp")
  param <- slot(object, "param")
  pseudo.mu0 <- as.vector(param$"pseudo-cp"[[1]][1,] - param$dp[[1]][1, ])
  resid <- switch(param.type, 
    'dp' = r, 
    'cp' = r - outer(rep(1,n), param$mu0),
    'pseudo-cp' = r  - outer(rep(1,n), pseudo.mu0))
  w <- slot(object, "input")$weights
  if(!is.null(w)) attr(resid,"weights") <- w
  return(resid)
  }

fitted.mselm <- function(object, param.type="CP", ...) {
  param.type <- tolower(param.type) 
  if(!(param.type %in% c("cp", "dp", "pseudo-cp"))) 
     stop("param.type must be either 'CP' or 'DP' or 'pseudo-CP'")
  n <- object@size["n.obs"]
  fit.dp <- slot(object, "fitted.values.dp")
  param <- slot(object, "param")
  pseudo.mu0 <- as.vector(param$"pseudo-cp"[[1]][1,] - param$dp[[1]][1, ])
  fitted <- switch(param.type, 
    'dp' = fit.dp, 
    'cp' = fit.dp + outer(rep(1,n), param$mu0),
    'pseudo-cp' = fit.dp + outer(rep(1,n), pseudo.mu0))
  w <- slot(object, "input")$weights
  if(!is.null(w)) attr(fitted,"weights") <- w
  return(fitted)
  }

weights.mselm <- function(object, ...) slot(object, "input")$weights

#------------------------------------------------------------
# 
# sn.info<- function(dp=NULL, cp=NULL, x=NULL, y=NULL, w, penalty=NULL, 
#              type="observed", norm2.tol=1e-6) { 
# if(any(is.list(dp), is.list(cp))) {
#   if(is.null(dp)) stop("in the multivariate case, 'dp' must be non-NULL")
#   info <-  sn.infoMv(dp=dp, x=x, y=y, w=w, type=type, norm2.tol=norm2.tol)
#   } else {
#   if(any(is.numeric(dp), is.numeric(cp)))
#   info <- sn.infoUv(dp=dp, cp=cp, x=x, y=y, w=w, penalty=penalty, 
#     type=type, norm2.tol = norm2.tol)
#   else stop("invalid input")
#   }
# return(info)
# } 
 
sn.infoUv <- function(dp=NULL, cp=NULL, x=NULL, y, w, penalty=NULL,   
                      norm2.tol=1e-6)
{# computes observed/expected Fisher information for univariate SN variates
  if(missing(y)) {y <- NULL; type <- "expected"} else type <- "observed"
  if(type == "observed") {if(!is.numeric(y)) stop("y is non-numeric")} 
  if(is.null(dp) & is.null(cp)) stop("either dp or cp must be set")
  if(!is.null(dp) & !is.null(cp)) stop("cannot set both dp and cp")
  if(missing(w)) w <- rep(1, max(NROW(cbind(x,y)),1)) 
  if(any(w != round(w)) | any(w<0))
    stop("weights must be non-negative integers")
  n <- length(w)
  nw <- sum(w)
  if(is.null(x)) {
    p <- 1
    wx <- w
    xx <- sum.x <- nw
    x <- matrix(1, nrow=n, ncol=1)
    }
  else { 
    p <- NCOL(x)
    # x <- matrix(x, n, p)
    wx <- w*x
    xx <- t(x) %*% (wx)
    sum.x <- matrix(colSums(wx))
    }
  x.names <- if(length(colnames(x)) == p) colnames(x)[2:p]  else
               { if(p==1) NULL else paste("x", 1L:(p-1), sep=".")}
  if(is.null(cp)) {
    if(length(dp) != (p+2)) stop("length(dp) must be equal to ncol(x)+2")
    if(is.null(names(dp))) names(dp) <- param.names("DP", "SN", p, x.names)
    cp <- dp2cpUv(dp, "SN")
    }
  if(is.null(dp)) {
    if(length(cp) != (p+2)) stop("length(cp) must be equal to ncol(x)+2")
    if(is.null(names(cp))) names(cp) <- param.names("CP", "SN", p, x.names)
    dp <- cp2dpUv(cp, "SN")
    }       
  penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE)
  omega <- dp[p+1]
  alpha <- dp[p+2]
  mu.z   <- sqrt(2/pi)*alpha/sqrt(1+alpha^2)
  sd.z   <- sqrt(1-mu.z^2)
  sigma  <- cp[p+1]
  gamma1 <- cp[p+2]
  R <- mu.z/sd.z
  T <- sqrt(2/pi-(1-2/pi)*R^2)
  Da.Dg <- 2*(T/(T*R)^2+(1-2/pi)/T^3)/(3*(4-pi))
  Dmu.z <- sqrt(2/pi)/(1+alpha^2)^1.5
  Dsd.z <- (-mu.z/sd.z)*Dmu.z
  Ddp.cp <- diag(p+2)
  Ddp.cp[1,p+1] <- (-R)
  Ddp.cp[1,p+2] <- (-sigma*R)/(3*gamma1)
  Ddp.cp[p+1,p+1] <- 1/sd.z
  Ddp.cp[p+1,p+2] <- (-sigma)* Dsd.z* Da.Dg/sd.z^2
  Ddp.cp[p+2,p+2] <- Da.Dg
  I.dp <- I.cp  <- matrix(NA,p+2,p+2)
  if(type == "observed"){
    score <- sn.pdev.gh(cp, x, y, w, penalty.fn, trace=FALSE, hessian=TRUE)/(-2)
    I.cp <- attr(score, "hessian")/2
    attr(score,"hessian") <- NULL
    dimnames(I.cp) <- list(names(cp), names(cp))
    Dcp.dp <- solve(Ddp.cp)
    I.dp <- force.symmetry(t(Dcp.dp) %*% I.cp %*% Dcp.dp)
    dimnames(I.dp) <- list(names(dp), names(dp))
    a.coef <- NULL
    asyvar.cp <- pd.solve(I.cp, silent=TRUE)
    if(is.null(asyvar.cp)) {
      asyvar.dp <- NULL
      not.mle <- TRUE} 
    else {
      not.mle <- (abs(sum(score * as.vector(asyvar.cp %*% score))) > norm2.tol)
      asyvar.dp <- pd.solve(I.dp, silent=TRUE)
      }
    if(not.mle) warning("something peculiar, parameters do not seem at MLE")  
    #--Iinfo.dp 2nd form 
    I2 <- matrix(NA,p+2,p+2)
    z <- (y - as.vector(x%*% dp[1:p]))/omega
    z1 <- zeta(1, alpha*z)
    z2 <- zeta(2, alpha*z)
    I2[1:p,1:p] <- t(wx) %*% ((1 - alpha^2*z2)*x)/omega^2
    I2[1:p,p+1] <- t(wx) %*% (2*z - alpha*z1 - alpha^2*z2*z)/omega^2
    I2[p+1,1:p] <- t(I2[1:p,p+1])
    I2[1:p,p+2] <- t(wx) %*% (z1 + alpha*z2*z)/omega
    I2[p+2,1:p] <- t(I2[1:p,p+2])
    I2[p+1,p+1] <- (-nw + 3*sum(w*z^2) -2*alpha*sum(w*z1*z)
                    -alpha^2*sum(w*z2*z^2))/omega^2 
    I2[p+1,p+2] <- I2[p+2,p+1] <- (sum(w*z*z1) + alpha*sum(w*z2*z^2))/omega
    I2[p+2,p+2] <- sum(-w*z2*z^2)
   }
  else { # type == "expected"
    I2 <- NULL
    if(abs(alpha) < 200) {
      f.a <- function(x, alpha, k) x^k * dsn(x,0,1,alpha) * zeta(1,alpha*x)^2
      err <- .Machine$double.eps^0.5
      a0 <- integrate(f.a, -Inf, Inf, alpha=alpha, k=0, rel.tol=err)$value
      a1 <- integrate(f.a, -Inf, Inf, alpha=alpha, k=1, rel.tol=err)$value
      a2 <- integrate(f.a, -Inf, Inf, alpha=alpha, k=2, rel.tol=err)$value
      }
    else {# approx of Bayes & Branco (2007) with multiplicative adjustment
      u <- 1 + 8*(alpha/pi)^2
      b <- sqrt(2/pi) 
      a0 <- 1.019149098 * b^2/sqrt(u)
      a1 <- 1.020466516 * (-alpha * b^3/sqrt(u^3*(1+alpha^2/u)))
      a2 <- 1.009258704 * b^2/sqrt(u)^3
      }
    a.coef <- c(a0, a1, a2)
    I.dp[1:p,1:p] <- xx * (1+alpha^2*a0)/omega^2  
    I.dp[p+1,p+1] <- nw * (2+alpha^2*a2)/omega^2
    I.dp[p+2,p+2] <- nw * a2
    I.dp[1:p,p+1] <- sum.x * (mu.z*(1+mu.z^2*pi/2)+alpha^2*a1)/omega^2
    I.dp[p+1,1:p] <- t(I.dp[1:p,p+1])
    I.dp[1:p,p+2] <- sum.x * (sqrt(2/pi)/(1+alpha^2)^1.5-alpha*a1)/omega
    I.dp[p+2,1:p] <- t(I.dp[1:p,p+2])
    I.dp[p+1,p+2] <- I.dp[p+2,p+1] <- nw*(-alpha*a2)/omega 
    eps <- 0.005
    if(abs(alpha) >  eps) 
      I.cp  <- force.symmetry(t(Ddp.cp) %*% I.dp %*% Ddp.cp)
    else{ 
      if(alpha == 0) 
        I.cp <- diag(c(1/omega^2, 2/omega^2, 1/6))
      else {       
        add <- c(rep(0,p+1), 3*eps)
        i1 <- sn.infoUv(dp=dp+add, x=x, w=w)
        i2 <- sn.infoUv(dp=dp-add, x=x, w=w)
        I.cp <- (i1$info.cp + i2$info.cp)/2
        }
      }
    score <- NULL
    asyvar.dp <- pd.solve(I.dp, silent=TRUE)
    asyvar.cp <- pd.solve(I.cp, silent=TRUE)
    }
  dimnames(I.dp) <- list(names(dp), names(dp))
  if(!is.null(asyvar.dp)) dimnames(asyvar.dp) <- list(names(dp), names(dp))
  if(!is.null(I.cp)) dimnames(I.cp) <- list(names(cp), names(cp))
  if(!is.null(asyvar.cp)) dimnames(asyvar.cp) <- list(names(cp), names(cp))
  aux <- list(Ddp.cp=Ddp.cp, a.coef=a.coef, score.cp=score)
  list(dp=dp, cp=cp, type=type, info.dp=I.dp, info.cp=I.cp, 
       asyvar.dp=asyvar.dp, asyvar.cp=asyvar.cp, aux=aux)
}

sn.infoMv <- function(dp, x=NULL, y, w, penalty=NULL, norm2.tol=1e-6, at.MLE=TRUE)
{# computes observed/expected Fisher information matrix for multiv.SN variates
 # using results in Arellano-Valle & Azzalini (JMVA, 2008+erratum)
  type <- if(missing(y)) "expected" else "observed"
  if(type == "expected") {
    y <- NULL 
    if(!missing(w)) 
    stop("argument 'w' is meaningless for expected information")
    }
  if(type == "observed" & !is.matrix(y)) stop("y is not a matrix")
  cp <- dp2cpMv(dp, "SN")
  d <- length(dp$alpha)
  d2 <- d*(d+1)/2
  if(missing(w)) w <- rep(1, max(NROW(x), 1))
  if(any(w != round(w)) | any(w<0))
    stop("weights must be non-negative integers")
  n <- if(type=="expected") length(w) else nrow(y)
  nw <- sum(w)
  if(is.null(x)) {
    p <- 1
    xx <- sum.x <- nw
    x <- matrix(1, nrow=n, ncol=1)
    }
  else { 
    p <- NCOL(x)
    # x <- matrix(x, n, p)
    xx <- drop(t(x) %*% (w*x))
    sum.x <- drop(matrix(colSums(w*x)))
    }
  beta <- matrix(dp[[1]],p,d)
  Omega <- dp$Omega
  omega <- sqrt(diag(Omega))
  alpha <- dp$alpha
  eta   <- alpha/omega
  # vOmega <- Omega[lower.tri(Omega,TRUE)]
  Obar <- cov2cor(Omega)
  Obar.alpha <-  as.vector(Obar %*% alpha)
  alpha.star <- sqrt(sum(alpha * Obar.alpha)) 
  if(alpha.star < 1e-4) {warning(
    "information matrix of multivariate SN not computed at/near alpha=0")
    return(NULL)
    }
  # delta.star <- alpha.star/sqrt(1+alpha.star^2)
  c1 <- sqrt(2/pi)/sqrt(1+alpha.star^2)
  c2 <- 1/(pi*sqrt(1+2*alpha.star^2))
  # theta <- c(beta,vOmega,eta)
  D <- duplicationMatrix(d)
  i1 <- 1:prod(dim(beta))
  i2 <- max(i1) + 1:(d*(d+1)/2)
  i3 <- max(i2) + 1:d
  # ind <- list(i1=i1, i2=i2, i3=i3)
  O.inv <- pd.solve(Omega, silent=TRUE)
  if(type == "observed"){ 
    y0 <- y - x %*% beta
    S0 <- t(y0) %*% (w*y0) / nw
    y0.eta <- as.vector(y0 %*% eta)
    z1 <- zeta(1, y0.eta) * w
    z2 <- (-zeta(2, y0.eta) * w)
    # Z2 <- diag(z2, n)
    # score function of theta; see 2008 JMVA paper, p.1377, lines 9-11
    # (except for a multiplicative constant of S2, irrelevant for MLE eqn's) 
    S1 <- (O.inv %x% t(x)) %*% as.vector(w*y0)- (eta %x% t(x)) %*% z1
    S2 <- (nw/2) * t(D) %*% ((O.inv %x% O.inv) %*% as.vector(S0-Omega))
    S3 <- t(y0) %*% z1
    score <- c(S1,S2,S3) 
    u  <- t(x) %*% z1
    U  <- t(x) %*% (z2 * y0)
    V  <- O.inv %*% (2*S0-Omega) %*% O.inv
    # terms as given in the last but one matrix of p.1377 on JMVA paper 2008
    j11 <- O.inv %x% xx + outer(eta,eta) %x% (t(x) %*% (z2 *x) )
    j12 <- (O.inv %x% (t(x) %*% (w*y0) %*% O.inv))  %*% D
    j13 <- diag(d) %x% u - eta %x% U
    j22 <- (nw/2) * t(D) %*% (O.inv %x% V) %*% D
    j23 <- matrix(0, d*(d+1)/2, d)
    j33 <- t(y0) %*% (z2 * y0)            
    uaA.coef <- NULL
    }
  else { # expected information
    Omega.eta <- omega * Obar.alpha
    mu.c <- Omega.eta/alpha.star^2 
    Omega.c <- Omega - outer(Omega.eta, Omega.eta)/alpha.star^2 
    alpha.bar <- alpha.star/sqrt(1+2*alpha.star^2)
    ginvMills <- function(x, m=0, s=1)  
        # generalized inverse Mills ratio: \phi(x; m, s^2)/\Phi(x)
        exp(-0.5*((x-m)^2/s^2-x^2)+log(zeta(1,x))-log(s))
    fn.u <- function(x, sd, k) x^k * ginvMills(x,0,sd) 
    if(alpha.bar > 0) {
      err<- .Machine$double.eps^0.5
      u0 <- integrate(fn.u, -Inf, Inf, sd=alpha.bar, k=0, rel.tol=err)$value
      u1 <- integrate(fn.u, -Inf, Inf, sd=alpha.bar, k=1, rel.tol=err)$value
      u2 <- integrate(fn.u, -Inf, Inf, sd=alpha.bar, k=2, rel.tol=err)$value }
    else {u0 <- 2; u1<- u2 <- 0}
    a0 <- u0
    a1 <- u1 * mu.c
    A2 <- u2 * outer(mu.c, mu.c) + u0 * Omega.c                    # cf  (19)
    A1 <- (c1*(diag(d)-outer(eta,eta) %*% Omega/(1+alpha.star^2))
           - c2*outer(eta, a1))   # cf  line after (12)
    # terms as given in the last matrix of p.16
    j11 <- (O.inv + c2*a0*outer(eta,eta)) %x% xx
    j12 <- c1*(O.inv %x% outer(sum.x, eta)) %*% D
    j13 <- A1 %x% sum.x
    j22 <- 0.5*nw *t(D) %*% (O.inv %x% O.inv) %*% D
    j23 <- matrix(0, d*(d+1)/2, d)
    j33 <- nw *c2 * A2
    uaA.coef <- list(u0=u0, u1=u1, u2=u2, a1=a1, A1=A1, A2=A2)
    score <- NULL
    }
  I.theta <-rbind(cbind( j11,    j12,   j13),
                  cbind(t(j12),  j22,   j23),
                  cbind(t(j13), t(j23), j33))
  if(!is.null(penalty)) { 
    # penalization depends on blocks (2,3) of the parameter set only
    penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE) 
    penalty.theta <- function(theta23, penalty, d) {
      vOmega <- theta23[1:(d*(d+1)/2)]
      eta <- theta23[(d*(d+1)/2) + (1:d)]
      Omega <- vech2mat(vOmega)
      alpha <- eta *sqrt(diag(Omega))
      penalty(list(alpha=alpha, Omega=Omega))
      } 
    i23 <- c(i2,i3)
    theta23 <- c(Omega[lower.tri(Omega,TRUE)], eta) # beta does not enter here
    score[i23] <- (score[i23] - 
      numDeriv::grad(penalty.theta, theta23, penalty=penalty.fn, d=d))
    jQ <- numDeriv::hessian(penalty.theta, theta23, penalty=penalty.fn, d=d)
    I.theta[i23, i23] <- I.theta[i23, i23] + jQ
    }                 
  I.theta <- force.symmetry(I.theta, tol=1e3)
  inv_I.theta <- pd.solve(I.theta, silent=TRUE)
  if(is.null(inv_I.theta)) {
    inv_I.theta <- matrix(NaN, nrow(I.theta), ncol(I.theta))
    if(at.MLE){
     warning("information matrix numerically not positive-definite")
     return(NULL)
     }}
  if(type == "observed" ) {
    score.norm2 <- sum(score * as.vector(inv_I.theta %*% score))
    if(at.MLE & (score.norm2/d > norm2.tol)) 
      stop("'dp' does not seem to be at the MLE")
    }
  D32 <- matrix(0,d, d2)
  tmp32 <- matrix(0,d^2,d^2)
  for(i in 1:d){
    Eii <- matrix(0,d,d)
    Eii[i,i] <- 1
    tmp32 <- tmp32 + Eii %x% Eii
    }
  D32 <- (-0.5)* (t(eta) %x% diag(1/omega^2, d,d)) %*% tmp32 %*% D
  # here we use the expression given in the notes, not in the paper
  Dlow <- cbind(matrix(0,d,d*p), D32, diag(1/omega,d,d))
  Dtheta.dp <- rbind(cbind(diag(d*p+d2), matrix(0,d*p+d2,d)), Dlow)
  I.dp <- t(Dtheta.dp) %*% I.theta %*% Dtheta.dp                     # cf  (14)
  I.dp <- force.symmetry(I.dp, tol=1e3)
  #
  # psi<- c(mu, vSigma, mu0)
  Sigma <- cp$var.cov
  sigma <- sqrt(diag(Sigma))
  Sigma.inv <- pd.solve(Sigma)
  mu0 <- c1* omega * Obar.alpha
  beta0.sq <- as.vector(t(mu0) %*% Sigma.inv %*% mu0)
  beta0 <- sqrt(beta0.sq)
  q1 <- 1/(c1*(1+beta0.sq))
  q2 <- 0.5*q1*(2*c1-q1)
  Dplus <- pd.solve(t(D) %*% D) %*% t(D)
  D23 <- Dplus %*% (diag(d) %x% mu0 + mu0 %x% diag(d))
  a <- as.vector(Sigma.inv %*% mu0)
  D32 <- t(-a) %x% (q1 * Sigma.inv - q1*q2*outer(a,a)) %*% D
  D33 <- q1 * Sigma.inv - 2*q1*q2*outer(a,a)
  one00 <- c(1,rep(0,p-1))
  Dtheta.psi <- rbind(
        cbind(diag(p*d),  matrix(0,p*d,d2), -diag(d) %x% one00),
        cbind(matrix(0,d2,p*d),  diag(d2),   D23),
        cbind(matrix(0,d,p*d),    D32,       D33))                # cf  (22a)
  mu0. <- mu0/(sigma*beta0)  # \bar{\mu}_0
  D32. <- matrix(0, d, d2)   # \tilde{D}_{32}
  for(i in 1:d)  {
    Eii <- matrix(0,d,d)
    Eii[i,i] <- 1
    D32. <- D32. + (1/sigma[i])*((t(mu0.) %*% Eii) %x% Eii) %*% D
    }
  D32. <- 0.5* beta0 * D32.
  D33. <- (2/(4-pi)) * diag(sigma/mu0.^2, d, d)/(3*beta0.sq)
  Dpsi.cp <- rbind(cbind(diag(p*d+d2), matrix(0,p*d+d2,d)), 
                   cbind(matrix(0,d,p*d), D32., D33.))            # cf  (22b)
  jacob <- Dtheta.psi %*% Dpsi.cp
  I.cp <- t(jacob) %*% I.theta %*% jacob                          # cf  (17)
  I.cp <- if(any(is.na(I.cp))) NULL else force.symmetry(I.cp)  
  asyvar.dp <- pd.solve(I.dp, silent=TRUE)
  if(is.null(asyvar.dp))  se.dp <- list(NULL) else {
    diags.dp <- sqrt(diag(asyvar.dp))
    se.beta <- matrix(diags.dp[1:(p*d)], p, d)
    se.diagOmega <- diags.dp[p*d + d2 +1 -rev(cumsum(1:d))]
    # se.omega <- se.Omega/(2*omega)
    se.alpha <- diags.dp[p*d +d2 +(1:d)]
    se.dp <- list(beta=se.beta, diagOmega=se.diagOmega, alpha=se.alpha)
    }
  asyvar.cp <- pd.solve(I.cp, silent=TRUE)
  if(is.null(asyvar.cp))  se.cp <- list(NULL) else {
    diags.cp <- sqrt(diag(asyvar.cp))
    se.beta <- matrix(diags.cp[1:(p*d)], p, d)
    se.diagSigma <- diags.cp[p*d + d2 +1 -rev(cumsum(1:d))]
    # se.sigma <- se.Sigma/(2*sigma)
    se.gamma1 <- diags.cp[p*d + d2 +(1:d)]
    se.cp <- list(beta=se.beta, var=se.diagSigma, gamma1=se.gamma1)
    }
  aux <- list(info.theta=I.theta, score.theta=score,
              Dtheta.dp=Dtheta.dp, Dpsi.cp=Dpsi.cp, Dtheta.psi=Dtheta.psi, 
              uaA.coef=uaA.coef)
  list(dp=dp, cp=cp, type=type, info.dp=I.dp, info.cp=I.cp, 
       asyvar.dp=asyvar.dp, asyvar.cp=asyvar.cp, 
       se.dp=se.dp, se.cp=se.cp, aux=aux)
}



msn.mle <- function(x, y, start=NULL, w, trace=FALSE, 
                opt.method=c("nlminb", "Nelder-Mead", "BFGS", "CG",  "SANN"),
                control=list() )
{
  if(trace) cat("[msn.mle] function is starting\n")
  y <- data.matrix(y)
  n <- nrow(y)
  if(missing(x)) x <- rep(1, n)
    else {if(!is.numeric(x)) stop("x must be numeric")}
  x <- data.matrix(x) 
  if(nrow(x) != n) stop("incompatible dimensions") 
  if(missing(w)) w <- rep(1, n)
  if(length(w) != n) stop("incompatible dimensions") 
  d <- ncol(y)  
  nw <- sum(w)
  p <- ncol(x)
  y.names <- dimnames(y)[[2]] 
  x.names <- dimnames(x)[[2]]
  opt.method <- match.arg(opt.method)

  if(is.null(start)) {
     fit0  <- lm.wfit(x, y, w, method="qr")
     beta  <- as.matrix(coef(fit0))
     res   <- resid(fit0)
     a     <- msn.moment.fit(res)
     Omega <- a$Omega
     omega <- a$omega
     alpha <- a$alpha
     if(!a$admissible) alpha<-alpha/(1+max(abs(alpha)))
     beta[1,] <- beta[1,]-omega*a$delta*sqrt(2/pi)  
     }
  else{
    beta  <- start[[1]] # start$beta
    Omega <- start$Omega
    alpha <- start$alpha
    omega <- sqrt(diag(Omega)) 
    }
  eta <-alpha/omega
  if(trace){ 
    cat("initial parameters:\n")
    print(cbind(t(beta),eta,Omega))
    }
  param <- c(beta,eta)
  dev <- msn.dev(param, x, y, w)    
  if(opt.method == "nlminb") {
    opt <- nlminb(param, msn.dev, msn.dev.grad, control=control, x=x, y=y, 
              w=w, trace=trace)      
    opt$value <- opt$objective 
    }
  else opt <- optim(param, fn=msn.dev, gr=msn.dev.grad, method=opt.method,
                  control=control, x=x, y=y, w=w, trace=trace)    
  logL <- opt$value/(-2) 
  beta <- matrix(opt$par[1:(p*d)],p,d)
  dimnames(beta)[2] <- list(y.names)
  dimnames(beta)[1] <- list(x.names)
  eta   <- opt$par[(p*d+1):(p*d+d)]
  xi    <- x %*% beta
  Omega <- t(y-xi) %*% (w*(y-xi))/n
  omega <- sqrt(diag(Omega))
  alpha <- eta*omega
  # param <- cbind(omega,alpha)
  dimnames(Omega) <- list(y.names,y.names)
  names(alpha) <- y.names
  alpha2 <- sum(eta * as.vector(Omega %*% eta))
  delta.star <- sqrt(alpha2/(1+alpha2))
  # dimnames(param)[1] <- list(y.names)
  dp  <- list(beta=beta, Omega=Omega, alpha=alpha)
  opt$method <- opt.method
  opt$called.by <- "msn.mle"
  aux <- list(alpha.star=sqrt(alpha2), delta.star=delta.star)
    if(trace) {
    cat("[msn.mle] function is completing\n")
    cat("message from ", opt.method, "(maybe empty):", opt$message,"\n")
    cat("final working parameters: " , format(opt$par), "\n")
    cat("log-likelihood:", format(logL, nsmall=2), "\n")
    }
  list(call=match.call(), dp=dp, logL=logL, aux=aux, opt.method=opt)
}

 
msn.dev <- function(param, x, y, w, trace=FALSE)
{
  d <- ncol(y)
  if(missing(w)) w <- rep(1,nrow(y))
  n <- sum(w)
  p <- ncol(x)
  beta <- matrix(param[1:(p*d)],p,d)
  eta <- param[(p*d+1):(p*d+d)]
  y0 <- y-x %*% beta
  Omega <- (t(y0) %*% (y0*w))/n  
  D <- diag(qr(2*pi*Omega)[[1]])
  logDet <- sum(log(abs(D)))
  dev <- n*logDet - 2*sum(zeta(0, y0 %*% eta) * w) + n*d
  if(trace) { 
    cat("\nmsn.dev:",dev,"\n","working parameters:\n"); 
    print(rbind(beta,eta))
    }
  dev
}

msn.dev.grad <- function(param, x, y, w, trace=FALSE)
{
  d <- ncol(y)
  if(missing(w)) w <- rep(1,nrow(y))
  n <- sum(w)
  p <- ncol(x)
  beta <- matrix(param[1:(p*d)],p,d)
  eta <- param[(p*d+1):(p*d+d)]
  y0 <- y-x %*% beta
  Omega <- (t(y0) %*% (w*y0))/n
  p1 <- zeta(1,as.vector(y0 %*% eta)) * w
  Omega.inv <- pd.solve(Omega, silent=TRUE)
  if(is.null(Omega.inv)) return(rep(NA, p*d+d))
  Dbeta <- (t(x) %*% (y0*w) %*% Omega.inv - outer(as.vector(t(x) %*% p1), eta))
  Deta <- as.vector(t(y0) %*% p1)
  if(trace){
    cat("[msn.dev.grad] gradient:\n")
    print(rbind(Dbeta,Deta))}
  -2*c(Dbeta,Deta)
}


msn.moment.fit <- function(y)
{# 31-12-1997: simple fit of MSN distribution usign moments
  y     <- as.matrix(y)
  k     <- ncol(y)
  m.y   <- apply(y, 2, mean)  
  var.y <- var(y)
  y0    <- (t(y) - m.y)/sqrt(diag(var.y))
  gamma1<- apply(y0^3, 1, mean)
  out   <- (abs(gamma1) > 0.99527)
  gamma1[out] <- sign(gamma1[out])*0.995
  a     <- sign(gamma1)*(2*abs(gamma1)/(4-pi))^0.33333
  delta <- sqrt(pi/2)*a/sqrt(1+a^2)
  m.z   <- delta * sqrt(2/pi) 
  omega <- sqrt(diag(var.y)/(1-m.z^2))
  Omega <- var.y + outer(omega*m.z, omega*m.z) 
  xi    <- m.y-omega*m.z
  O.cor <- cov2cor(Omega)
  O.inv <- pd.solve(O.cor)
  tmp   <- as.vector(1 - t(delta) %*% O.inv %*% delta)
  if(tmp<=0) {tmp <- 0.0001; admissible <- FALSE} 
        else admissible <- TRUE
  alpha <- as.vector(O.inv %*% delta)/sqrt(tmp)
  list(xi=xi, Omega=Omega, alpha=alpha, Omega.cor=O.cor, omega=omega, 
       delta=delta, skewness=gamma1, admissible=admissible) 
}

  
st.mple <- function(x, y, dp=NULL, w, fixed.nu=NULL, symmetr=FALSE, 
  penalty=NULL, trace=FALSE, 
  opt.method=c("nlminb", "Nelder-Mead", "BFGS", "CG", "SANN"), control=list())
{ # MLE of DP for univariate ST distribution, allowing case symmetr[ic]=TRUE
  if(trace) cat("[st.mple] function is starting\n")
  if(missing(y)) stop("required argument y is missing")
  y.name <- deparse(substitute(y))
  if(!is.vector(y)) y <- as.vector(y)
  if(!is.numeric(y)) stop("argument y must be a numeric vector")
  n <- length(y)
  x <- if(missing(x)) matrix(rep(1, n), ncol = 1) else data.matrix(x)
  x.name <- deparse(substitute(x))
  if(nrow(x) != n) stop("incompatible dimensions")
  if(any(x[,1] != 1)) stop("first column of x must have all 1's")
  if(symmetr && !is.null(penalty)) 
    stop("Penalized log-likelihood not allowed with constraint alpha=0")
  p <- ncol(x)
  if(missing(w)) w <- rep(1, n)
  if(length(w) != n) stop("incompatible dimensions")
  nw <- sum(w)
  verbose <- as.numeric(trace)*2
  if(trace) cat("st.mple running...")
  if(is.null(dp) | mode(dp)=="character") {
    Mx <- if(mode(dp) == "character") dp[1] else "M2" 
    if(!(Mx %in% c("M0", "M2", "M3"))) stop("invalid 'dp' initialization")
    if(Mx == 0) { # old method, not recommended
      ls <- lm.wfit(x, y, w)
      res <- ls$residuals
      s <- sqrt(sum(w*res^2)/nw)
      gamma1 <- sum(w*res^3)/(nw*s^3)
      gamma2 <- sum(res^4)/(nw*s^4) - 3 
      cp <- c(ls$coef, s, gamma1, gamma2)
      dp <- st.cp2dp(cp, silent=TRUE)
      if(is.null(dp)) dp <- rep(NA,length(cp))
      if(any(is.na(dp))) dp <- c(cp[1:(p+1)], 0, 10)
      }
    if(Mx == "M2") dp <- st.prelimFit(x, y, w, quick=TRUE, verbose=verbose)$dp
    if(Mx == "M3") dp <- st.prelimFit(x, y, w, quick=NULL, verbose=verbose)$dp
    if(!is.null(fixed.nu)) dp <- dp[-length(dp)]
    if(symmetr) dp <- dp[-length(dp)]
    if(trace) cat("starting dp values obtained from st.prelimFit\n")
    }
  else{ 
    if(length(dp) != (p+2-as.numeric(symmetr)+as.numeric(is.null(fixed.nu))))
       stop("arg 'dp' has wrong length")}
  if(trace) cat("[st.mple] dp (starting values):", format(dp), "\n")
  tiny <- (.Machine$double.eps)^(0.25) 
  low.dp <- c(rep(-Inf, p), tiny, if(symmetr) NULL else -Inf,   
              if(is.null(fixed.nu)) tiny)
  high.dp <- c(rep(Inf, length(dp)))
  opt.method <- match.arg(opt.method)
  penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE) 
  if(opt.method == "nlminb") {
    opt <- nlminb(dp, objective=st.pdev, gradient=st.pdev.gh, 
           # Note: do NOT set 'hessian=st.dev.hessian', much time-consuming 
           lower=low.dp, upper=high.dp, control=control,
           x=x, y=y, w=w, fixed.nu=fixed.nu, symmetr=symmetr, 
           penalty=penalty.fn, trace=trace)
    opt$value <-  opt$objective
    }
  else {
    opt <- optim(dp, fn=st.pdev, gr=st.pdev.gh,  method = opt.method,
             # arguments lower & upper not used to allow all opt.method
             control = control,
             x=x, y=y, w=w, fixed.nu=fixed.nu, symmetr=symmetr, 
             penalty=penalty.fn, trace=trace)   
    }               
  dp <- opt$par
  opt$method <- opt.method
  opt$called.by <- "st.mple"
  dp. <- if(is.null(fixed.nu)) dp else c(dp, fixed.nu)  
  if(symmetr) dp. <- c(dp.[1:(p+1)], 0, dp.[length(dp.)])
  rv.comp <- c(TRUE, !symmetr, is.null(fixed.nu))
  names(dp) <- param.names("DP", "ST", p=p, x.names=colnames(x)[-1], rv.comp)
  names(dp.) <- param.names("DP", "ST", p=p, x.names=colnames(x)[-1])
  logL <- (-opt$value)/2
  boundary <- FALSE
  if(!symmetr) boundary <- as.logical(abs(dp[p+2]) > 1000) 
  if(is.null(fixed.nu)) boundary <- (boundary | dp[length(dp)] > 1e3)
  # AA, must improve this rule
  if(trace) {
     cat("[st.mple] function is completing")
     cat("message from", opt.method, "(maybe none):", opt$message, "\n")
     cat("estimates (dp):", format(dp), "\n")
     cat("log-likelihood:", format(logL, nsmall=2), "\n")
     }
  list(call=match.call(), dp=dp, fixed.nu=fixed.nu, logL=logL, 
      dp.complete=dp., boundary=boundary, opt.method=opt)
}


st.pdev <- function(dp, x, y, w, fixed.nu=NULL, symmetr=FALSE, penalty=NULL, 
  trace=FALSE)
{ # computes "penalized deviance"=-2*(logL-Q) for ST
  p <- ncol(x)
  xi <- as.vector(x %*% matrix(dp[1:p],p,1))
  alpha <- if(symmetr) 0 else dp[p+2]
  nu <- if(is.null(fixed.nu)) dp[p+3-as.numeric(symmetr)] else fixed.nu
  if(dp[p+1] <= 0 | nu <= 0) return(NA)
  logL <- sum(w * dst(y, xi, dp[p+1], alpha, nu, log=TRUE))
  Q <- if(is.null(penalty)) 0 else penalty(dp[p+2], nu, der=0)
  if(trace) cat("st.pdev: (dp,pdev) =", format(c(dp, -2*(logL-Q))),"\n")
  return(-2 * (logL - Q))
}

st.pdev.gh <- function(dp, x, y, w, fixed.nu=NULL, symmetr=FALSE, 
   penalty=NULL, trace=FALSE, hessian=FALSE)
{ # computes gradient and hessian of (penalized) deviance for ST 
  p  <- ncol(x)
  n  <- nrow(x)
  beta  <- dp[1:p]
  omega <- dp[p+1]
  alpha <- if(symmetr) 0 else dp[p+2]
  j.nu <- p + 2 + as.numeric(!symmetr)
  nu <- if(is.null(fixed.nu)) dp[j.nu] else fixed.nu 
  npar <- p + 1 + as.numeric(!symmetr) + as.numeric(is.null(fixed.nu))
  score <- numeric(npar)
  xi <- as.vector(x %*% beta)
  z <- (y - xi)/omega
  nuz2 <- (nu + z^2)
  loro.tau <- sqrt((nu+1)/nuz2) 
  zt <- z * loro.tau
  log.pdf <- dt(alpha*zt, nu+1, log=TRUE)
  log.cdf <- pt(alpha*zt, nu+1, log.p=TRUE)
  cdf <- exp(log.cdf)
  loro.w <- exp(log.pdf - log.cdf)
  tw <- loro.tau * loro.w
  zwz2 <- z*(z^2-1)*loro.w/loro.tau
  wi.beta  <- z*loro.tau^2 - nu*alpha*tw/(nu+z^2)
  score[1:p] <- colSums(w*x*wi.beta)/omega
  score[p+1] <- sum(w * (-1 + zt^2 -alpha*nu*z*tw/(nu+z^2)))/omega
  if(!symmetr) score[p+2] <- sum(w*z*tw)
  if(is.null(fixed.nu)){
    # 2018-10-30 new coding, code computing int.g moved to 'hessian' section 
    logTwz <- function(nu, alpha, z) {
       r <- sqrt((nu+1)/(nu+z^2))
       pt(alpha*z*r, df=nu+1, log.p=TRUE)
       }   
    DlogTwz <- numDeriv::jacobian(logTwz, nu, z=z, alpha=alpha)   
    score[j.nu] <- 0.5* sum(w*(-1/nu + digamma((nu+1)/2) - digamma(nu/2)  
          -log(1+z^2/nu) + (nu+1)*z^2/(nu*(nu+z^2)) + 2*DlogTwz))
    }
  if(is.null(penalty)) { 
    Q <- 0
    attr(Q, "der1") <- rep(0,2)
    attr(Q, "der2") <- matrix(rep(0,4), 2, 2) }  
    else  {
    if(symmetr) stop("Penalized logL not allowed with constraint alpha=0") 
    Q <- penalty(alpha, nu, der=1+as.numeric(hessian))  
    }
  score[(p+2):(p+3)] <-  score[(p+2):(p+3)] - attr(Q, "der1") 
  score <- score[1:npar]
  gradient <- (-2)*score
  if(hessian){ 
    info <- matrix(NA, npar, npar) 
    fun.g <- function(x, nu1) dt(x,nu1) *
              (((nu1+1)*x^2)/(nu1*(nu1+x^2)) - log1p(x^2/nu1))
    int.g <- numeric(n)
    for (i in 1:n)
      int.g[i] <- integrate(fun.g, -Inf, alpha*zt[i], nu1=nu+1)$value
    # score[j.nu] <- 0.5 * sum(w * (digamma(1+nu/2) -digamma(nu/2)
    #  - (2*nu+1)/(nu*(nu+1)) -log1p(z^2/nu) + zt^2/nu 
    #   + alpha*zwz2/(nu+z^2)^2 + int.g/cdf))
    w.z  <- (-nu*(nu+2)*alpha^2*z*loro.w/((nu+z^2*(1+alpha^2))*nuz2)
             -nu*alpha*loro.tau*loro.w^2/nuz2)
    w.alpha <- (-(nu+2)* alpha*z^2*loro.w/(nu+z^2*(1+alpha^2)) -zt*loro.w^2)
    S.z  <- (-z*loro.tau^2 + alpha*nu*tw/nuz2) 
    S.zz <- (2*zt^2/nuz2 - loro.tau^2 -3*alpha*nu*z*tw/nuz2^2
             +alpha*nu*loro.tau*w.z/nuz2) 
    info[1:p,1:p] <- t(-S.zz *x) %*% (w*x)/omega^2
    info[1:p,p+1] <- info[p+1,1:p] <- colSums(-w*(S.zz*z + S.z)*x)/omega^2
    info[p+1,p+1] <- -sum(w*(1 + z^2*S.zz + 2*z*S.z))/omega^2
    S.za <- nu*loro.tau*(loro.w +alpha*w.alpha)/nuz2
    if(!symmetr) {
      info[1:p,p+2] <- info[p+2,1:p] <- colSums(w*S.za*x)/omega
      info[p+1,p+2] <- info[p+2,p+1] <- sum(w*z*S.za)/omega
      info[p+2,p+2] <- sum(-w*zt*w.alpha) + attr(Q,"der2")[1,1]
      }
    if(is.null(fixed.nu)) {
      w.nu <- (0.5*loro.w*((nu+2)*(alpha*z)^2/((nu+z^2*(1+alpha^2))*nuz2)
               - log1p((alpha*z)^2/nuz2) - int.g/cdf)
               - 0.5*alpha*zwz2*loro.w/nuz2^2)
      S.znu <- (z*(1-z^2)/nuz2^2 + alpha*nu*loro.tau*w.nu/nuz2
                + alpha*(nu*(3*z^2-1)+2*z^2)*loro.w/(2*loro.tau*nuz2^3))
      info[1:p,j.nu] <- info[j.nu,1:p] <- colSums(w* S.znu*x)/omega
      info[p+1,j.nu] <- info[j.nu,p+1] <- sum(w*z*S.znu)/omega
    
      fun.b <- function(x, nu1) dt(x,nu1) *
                 (((nu1+1)*x^2)/(nu1*(nu1+x^2)) - log1p(x^2/nu1))^2
      fun.d <- function(x, nu1) dt(x,nu1) *
                 x^2*((nu1-1)*x^2-2*nu1)/(nu1^2*(nu1+x^2)^2)
      int.b <- int.d <- numeric(n)
      for (i in 1:n) {
        int.b[i] <- integrate(fun.b, -Inf, alpha*zt[i], nu1=nu+1)$value
        int.d[i] <- integrate(fun.d, -Inf, alpha*zt[i], nu1=nu+1)$value
        }
      info[j.nu,j.nu] <- -sum(w*( (trigamma(nu/2+1) - trigamma(nu/2))/4 
        + (2*nu^2+2*nu+1)/(2*(nu*(nu+1))^2) + z^2/(2*nu*nuz2)
        - z^2*(nu^2+2*nu+z^2)/(2*nu^2*nuz2^2)
        - alpha*zwz2*(z^2+4*nu+3)/(4*(nu+1)*nuz2^3)
        + alpha*z*(1-loro.tau^2)*w.nu/(2*loro.tau*nuz2)
        - (int.g/(2*cdf))^2 - alpha*zwz2*int.g/(4*cdf*nuz2^2)
        + (2*int.d + int.b)/(4*cdf)
        + (alpha*zwz2/(4*nuz2^2))*
          ((nu+2)*alpha^2*z^2/((nu+1)*(nu+z^2*(1+alpha^2))) 
            - log1p((alpha*z)^2/nuz2)) ))
      info[j.nu,j.nu] <- info[j.nu,j.nu] + attr(Q,"der2")[2,2]
      if(!symmetr) { 
        info[p+2,p+3] <- info[p+3,p+2] <- -sum(w*(0.5*zwz2/nuz2^2 + zt*w.nu))
        info[p+2,p+3] <- info[p+2,p+3] + attr(Q,"der2")[1,2]
        info[p+3,p+2] <- info[p+3,p+2] + attr(Q,"der2")[2,1]
        }
      }
    attr(gradient,"hessian") <- force.symmetry(2*info)
    if(trace) cat("Hessian matrix has been computed\n")
    }
  if(trace) cat("st.pdev.gh: gradient = ", format(gradient),"\n")
  return(gradient)
}

st.pdev.hessian <- function(dp, x, y, w, fixed.nu=NULL, symmetr=FALSE,  
     penalty = NULL, trace=FALSE)
  attr(st.pdev.gh(dp, x, y, w, fixed.nu, symmetr, penalty, trace, 
     hessian=TRUE), "hessian")


st.infoUv <- function(dp=NULL, cp=NULL, x=NULL, y, w, fixed.nu=NULL, 
   symmetr=FALSE, penalty=NULL, norm2.tol=1e-06)
{# computes observed Fisher information matrix for univariate ST variates
  if(missing(y)) stop("y is missing")
  if(!is.numeric(y)) stop("y is non-numeric")
  type <- "observed"
  if(is.null(dp) & is.null(cp)) stop("either dp or cp must be set")
  if(!is.null(dp) & !is.null(cp)) stop("cannot set both dp and cp")
  # if(is.null(cp)) cp <- st.dp2cp(c(dp, fixed.nu)) # completa DP se necessario
  if(is.null(dp)) dp <- st.cp2dp(cp) # AA, CP deve essere comunque completo
  if(missing(w)) w <- rep(1, max(nrow(cbind(x, y)), 1))
  if(any(w != round(w)) | any(w<0))
    stop("weights must be non-negative integers")
  npar <- length(dp)
  n <- length(w)
  nw <- sum(w)
  nu <- if(is.null(fixed.nu)) dp[npar] else fixed.nu
  if(is.null(x)) {
    n <- if(is.null(y)) 1 else NROW(y) 
    p <- 1
    xx <- sum.x <- nw 
    x <- matrix(1, nrow=n, ncol=1)
    }
  else { 
    p <- NCOL(x)
    # x <- matrix(x, n, p)
    xx <- t(x) %*% (w * x)
    sum.x <- matrix(colSums(x))
    }
  penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE)   
  score <- st.pdev.gh(dp, x, y, w, fixed.nu, symmetr, penalty.fn, trace=FALSE, 
              hessian=TRUE)
  I.dp <- attr(score, "hessian")/2
  if((d2 <- sum(score * as.vector(solve(I.dp) %*% score))) > norm2.tol*npar) {
    warning("'dp' does not seem to be at MLE; score not quite 0")
    cat("score(dp): ", score, "\n")
    cat("norm(score)^2:", d2,"\n")
    }
  attr(score, "hessian") <- NULL
  dimnames(I.dp) <- list(names(dp), names(dp))
  asyvar.dp <- pd.solve(I.dp, silent=TRUE)
  aux <- list(score.dp=score)
  if(nu > 4) {
    dp0 <- c(dp[1:(p+1)], if(symmetr) 0 else dp[p+2], if(is.null(fixed.nu)) nu)
    cp <- st.dp2cp(dp=dp0, cp.type="proper", fixed.nu=fixed.nu,
            upto=if(is.null(fixed.nu)) 4 else 3, jacobian=TRUE)
    Dcp.dp <- attr(cp, "jacobian")
    attr(cp, "jacobian") <- NULL
    ind <- c(1:(p+1), if(symmetr) NULL else (p+2), if(is.null(fixed.nu)) p+3) 
    Dcp.dp <- Dcp.dp[ind, ind]
    cp <- cp[ind]
    Ddp.cp <- solve(Dcp.dp)
    I.cp <- force.symmetry(t(Ddp.cp) %*% I.dp %*% Ddp.cp)
    dimnames(I.cp) <- list(names(cp), names(cp))
    asyvar.cp <- pd.solve(I.cp, silent=TRUE)  # modified 2018-04-23
    if(!is.null(asyvar.cp)) { 
      aux$Dcp.dp <- Dcp.dp 
      aux$Ddp.cp <- Ddp.cp
    }}  
  else  {
    I.cp <- NULL
    asyvar.cp <- NULL
    aux <- NULL
    }
  list(dp=dp, cp=cp, type=type, info.dp=I.dp, info.cp=I.cp, 
       asyvar.dp=asyvar.dp, asyvar.cp=asyvar.cp, aux=aux)
}


param.names <- function(param.type, family="SN", p=1, x.names=NULL, rv.comp)
{# NB: x.names= names of covariates except intercept, having length (p-1); 
 # rv.comp = random variable components, those not in the linear predictor.
  param.type <- toupper(param.type)
  family <- toupper(family)
  if(!(param.type %in% c("DP","CP","PSEUDO-CP"))) stop("invalid param.type")
  if(!(family %in% c("SN", "ESN", "ST", "SC"))) stop("unknown family")
  if(p > 1  && (length(x.names) < (p-1)))
    x.names <- outer("x", as.character(1L:(p-1)), paste, sep=".")
  if(param.type == "DP"){
    name0 <-  if(p > 1) "(Intercept.DP)" else "xi"
    par.names <- c(name0, x.names, "omega", "alpha")
    if(family == "ESN") par.names <- c(par.names, "tau")
    if(family == "ST") par.names <- c(par.names, "nu")
    }
  if(param.type == "CP"){
    name0 <-  if(p > 1) "(Intercept.CP)" else "mean"
    par.names <- c(name0, x.names, "s.d.", "gamma1")
    if(family == "ESN") par.names <- c(par.names, "tau")
    if(family == "ST") par.names <- c(par.names, "gamma2")
    }
  if(param.type == toupper("pseudo-CP")){
    if(!(family %in% c("ST", "SC"))) 
      stop("pseudo-CP makes sense only for ST and SC families")
    name0 <-  if(p > 1) "(Intercept.CP~)" else "mean~"
    par.names <- c(name0, x.names, "s.d.~", "gamma1~")
    if(family == "ST") par.names <- c(par.names, "gamma2~")
    }
  if(missing(rv.comp)) rv.comp <- rep(TRUE, length(par.names)-p)
  par.names[c(rep(TRUE,p), rv.comp)]
}


mst.mple <- function (x, y, start=NULL, w, fixed.nu = NULL, symmetr=FALSE,
                penalty=NULL, trace = FALSE, 
                opt.method = c("nlminb", "Nelder-Mead", "BFGS", "CG", "SANN"),
                control = list()) 
{
  if(trace) cat("[mst.mple] function is starting\n")
  if(missing(y)) stop("required argument y is missing")
  y.name <- deparse(substitute(y))
  y <- data.matrix(y)
  n <- nrow(y)
  y.names <- dimnames(y)[[2]]
  if(missing(x)) x <- rep(1, n)
    else {if(!is.numeric(x)) stop("x must be numeric")}
  x.names <- dimnames(x)[[2]]
  x <- data.matrix(x)
  if(nrow(x) != n) stop("incompatible dimensions")
  if(missing(w)) w <- rep(1, n)
  if(length(w) != n) stop("incompatible dimensions")
  nw <- sum(w)
  d <- ncol(y)
  p <- ncol(x)
  opt.method <- match.arg(opt.method)
  verbose <- as.numeric(trace)*2
  if(is.null(start) | mode(start)=="character") {
    Mx <- if(mode(start) == "character") start[1] else "M3" 
    if(!(Mx %in% c("M0", "M2", "M3"))) stop("invalid 'start'")
    if(Mx == "M0") { # old method, superseded since version 1.6-0
      ls <- lm.wfit(x, y, w, singular.ok=FALSE)
      beta <- coef(ls)
	  Omega <-  var(resid(ls))
	  omega <- sqrt(diag(Omega))
	  alpha <- rep(0, d)
      nu <- if(is.null(fixed.nu)) 8 else fixed.nu
      dp <- list(beta=beta, Omega=Omega, alpha=alpha, nu=nu)
      }
    if(Mx == "M2") dp <- mst.prelimFit(x, y, quick=TRUE, verbose=verbose)$dp
    if(Mx == "M3") dp <- mst.prelimFit(x, y, quick=NULL, verbose=verbose)$dp  
    if(trace) cat("starting dp values obtained from mst.prelimFit\n")
  }
  else {
    if (all(dim(start[[2]]) == c(d,d), length(start[[3]]) == d))  dp <- start
    else stop("argument 'start' is not in the form that I expected")
    }
  beta <- dp[[1]] 
  Omega=dp[[2]]  
  alpha <- if(symmetr)  rep(0,d) else dp[[3]]
  nu <- if(!is.null(fixed.nu)) fixed.nu else dp[[4]]
  dp <- list(beta=beta, Omega=Omega, alpha=alpha, nu=nu)
  if (trace) cat("[mst.mple] starting values for dp: ",
       c(beta, Omega[!upper.tri(Omega)], alpha, nu),  "\n")
  param <- dplist2optpar(dp[1:3])
  if(symmetr) param <- param[-(p*d + d*(d+1)/2 + (1:d))]
  if(is.null(fixed.nu)) param <- c(param, log(nu))
  if(!is.null(penalty)) penalty <- get(penalty, inherits=TRUE)
  opt.method <- match.arg(opt.method)
  if(opt.method == "nlminb") {
    opt <- nlminb(param, objective = mst.pdev, gradient = mst.pdev.grad, 
             control = control,  x = x, y = y, w = w, fixed.nu = fixed.nu, 
             symmetr=symmetr,  penalty=penalty, trace = trace)
    # info <- num.deriv2(opt$par, FUN="mst.dev.grad", X=X, y=y,
    #           w=w, fixed.nu = fixed.nu)/2
    opt$value <-  opt$objective
    }
  else {
      opt <- optim(param, fn = mst.pdev, gr = mst.pdev.grad, 
               method = opt.method, control = control, hessian = TRUE,
               x = x, y = y, w = w,   fixed.nu = fixed.nu, 
               symmetr=symmetr, penalty=penalty, trace = trace)
      # info <- opt$hessian/2
      }
  dev   <- opt$value
  logL <- dev/(-2)
  param <- opt$par
  opt$method <- opt.method
  opt$called.by <- "mst.mple"
  par <- opt$par 
  npar0 <- (p*d + d*(d+1)/2)
  vp <- par[1:npar0]
  dp.comp <- (1:2)
  if(symmetr) vp <- c(vp, rep(0,d)) else {
    vp <- c(vp, par[npar0 + (1:d)]); dp.comp <- (1:3)}
  if(is.null(fixed.nu)) {
    vp <- c(vp, par[length(par)])
    dp.comp <- c(dp.comp,4)}
  dp.list <- optpar2dplist(vp, d, p, x.names, y.names)
  dp <- dp.complete <- dp.list$dp
  if(symmetr) dp.complete$alpha <- rep(0, d)
  if(!is.null(fixed.nu)) dp.complete$nu <- fixed.nu
  alpha2 <- sum(dp$alpha * as.vector(cov2cor(dp$Omega) %*% dp$alpha))
  delta.star <- sqrt(alpha2/(1+alpha2))
  dp <- dp[dp.comp]
  aux <- list(fixed.nu=fixed.nu, symmetr=symmetr, alpha.star=sqrt(alpha2), 
              delta.star=delta.star)
  boundary <- ((1 - delta.star) < .Machine$double.eps^(1/4))
  if(is.null(fixed.nu)) boundary <- (boundary | dp$nu > 1e3)  
  if (trace) {
    cat("[mst.mple] function is completing\n")
    cat("message from optimization routine (maybe empty):", opt$message, "\n")
    cat("(penalized) log-likelihood:", format(logL, nsmall=2), "\n")
  }   
  list(call=match.call(), dp=dp, dp.complete=dp.complete, logL=logL,
    boundary=boundary, aux=aux, opt.method = opt)
}


mst.pdev <- function(param, x, y, w, fixed.nu=NULL, symmetr=FALSE, 
   penalty=NULL, trace=FALSE)
{
  if(missing(w)) w <- rep(1,nrow(y))
  d <- ncol(y)
  p <- ncol(x)
  npar0 <- (p*d + d*(d+1)/2)
  param1 <- c(param[1:npar0], if(symmetr) rep(0, d) else param[npar0+(1:d)], 
    if(is.null(fixed.nu)) param[length(param)])
  dp.list <- optpar2dplist(param1, d, p)
  dp <- dp.list$dp
  nu <- if(is.null(fixed.nu)) dp$nu else fixed.nu
  logL <- sum(w * dmst(y, x %*% dp$beta, dp$Omega, dp$alpha, nu, log=TRUE))
  Q <- if(is.null(penalty)) 0 else 
       penalty(list(alpha=dp$alpha, Omega.bar=cov2cor(dp$Omega)), nu, der=0)
  pdev <- (-2) * (logL - Q)
  if(trace) cat("mst.pdev: ", pdev, "\nparam:", format(param), "\n")
  pdev
}


mst.pdev.grad <- function(param, x, y, w, fixed.nu=NULL, symmetr=FALSE, 
  penalty=NULL, trace=FALSE)
{ # based on Appendix B of Azzalini & Capitanio (2003, arXiv-0911.2342)
  # except for a few quite patent typos (transposed matrices, etc) 
  d <- ncol(y)
  p   <- ncol(x)
  beta<- matrix(param[1:(p*d)],p,d)
  D  <- exp(-2*param[(p*d+1):(p*d+d)])
  A  <- diag(d)
  i0 <- p*d + d*(d+1)/2
  if(d>1) A[!lower.tri(A,diag=TRUE)] <- param[(p*d+d+1):i0]
  eta  <- if(symmetr) rep(0,d) else param[(i0+1):(i0+d)]
  nu   <- if(is.null(fixed.nu)) exp(param[length(param)]) else fixed.nu
  Oinv <- t(A) %*% diag(D,d,d) %*% A
  u    <- y - x %*% beta
  u.w  <- u * w
  Q    <- as.vector(rowSums((u %*% Oinv) * u.w))
  L    <- as.vector(u.w %*% eta)
  sf   <- if(nu < 1e4) sqrt((nu+d)/(nu+Q)) else sqrt((1+d/nu)/(1+Q/nu))
  t.   <- L*sf                                     # t(L,Q,nu) in \S 5.1
  # dlogft<- (-0.5)*(1+d/nu)/(1+Q/nu)              # \tilde{g}_Q
  dlogft <- (-0.5)*sf^2                            # \tilde{g}_Q, again
  dt.dL <- sf                                      # \dot{t}_L
  dt.dQ <- (-0.5)*L*sf/(Q+nu)                      # \dot{t}_Q
  logT. <- pt(t., nu+d, log.p=TRUE)
  dlogT.<- exp(dt(t., nu+d, log=TRUE) - logT.)     # \tilde{T}_1
  Dbeta <- (-2* t(x) %*% (u.w*dlogft) %*% Oinv 
            - outer(as.vector(t(x) %*% (dlogT. * dt.dL* w)), eta)
            - 2* t(x) %*% (dlogT.* dt.dQ * u.w) %*% Oinv )
  Deta  <- colSums(dlogT.*sf*u.w)
  if(d>1) {
     M  <- 2*( diag(D,d,d) %*% A %*% t(u * dlogft
               + u * dlogT. * dt.dQ) %*% u.w)
     DA <- M[!lower.tri(M,diag=TRUE)]
     }
  else DA<- NULL
  M <- (A %*% t(u*dlogft + u*dlogT.*dt.dQ) %*% u.w %*% t(A))
  if(d>1) DD <- diag(M) + 0.5*sum(w)/D
     else DD <- as.vector(M + 0.5*sum(w)/D) 
  grad <- (-2) * c(Dbeta, DD*(-2*D), DA, if(!symmetr) Deta)
  if(is.null(fixed.nu)) {
    df0 <- min(nu, 1e8)
    if(df0 < 10000){
       diff.digamma <- digamma((df0+d)/2) - digamma(df0/2)
       log1Q<- log(1+Q/df0)
     }
    else
      {
       diff.digamma <- log1p(d/df0)
       log1Q <- log1p(Q/df0)
      }
    dlogft.ddf <- 0.5 * (diff.digamma - d/df0
                        + (1+d/df0)*Q/((1+Q/df0)*df0) - log1Q)
    ## eps   <- 1.0e-4
    ## df1 <- df0 + eps
    ## sf1 <- if(df0 < 1e4) sqrt((df1+d)/(Q+df1)) else sqrt((1+d/df1)/(1+Q/df1))
    ## logT.eps <- pt(L*sf1, df1+d, log.p=TRUE)
    ## dlogT.ddf <- (logT.eps-logT.)/eps
    funct.logT. <- function(nu, d, L, Q) {
      sf <- if(nu < 1e4) sqrt((nu+d)/(nu+Q)) else sqrt((1+d/nu)/(1+Q/nu))
      pt(L*sf, nu+d, log.p=TRUE)
      }
    dlogT.ddf <- numDeriv::jacobian(funct.logT., x=df0, d=d, L=L, Q=Q)[,1]
    Ddf   <- sum((dlogft.ddf + dlogT.ddf)*w)
    grad <- c(grad, -2*Ddf*df0)
    }
  if(!is.null(penalty)) { 
    if(symmetr) stop("penalized log-likelihood not allowed when alpha=0")
    Ainv <- backsolve(A, diag(d))
    Omega <- Ainv %*% diag(1/D,d,d) %*% t(Ainv)
    omega <- diag(Omega)
    alpha <- eta*omega
    Q <- Qpenalty(list(alpha, cov2cor(Omega)), nu, der=1)
    comp <-  1:(length(alpha)+is.null(fixed.nu))
    Qder <- attr(Q, "der1") * c(1/omega, 1)[comp] 
    # gradient for transformed variable (alpha --> eta)
    grad <- grad + 2*c(rep(0, p*d + d*(d+1)/2),  Qder)
    }
  if(trace) cat("mst.pdev.grad: norm is ", format(sqrt(sum(grad^2))), "\n")  
  return(grad)
}
 

mst.theta.jacobian <- function(theta, p, d, cp.type="proper") 
{ # jacobian matrices associated to transformations from
  # theta=c(beta, vech(Omega), eta, nu) to DP, CP and other parameterizations
  cp.type <- match.arg(cp.type, c("proper", "pseudo"))
  k1 <- p * d
  k2 <- k1 + d*(d+1)/2
  k3 <- k2 + d
  k4 <- k3 + 1
  if(length(theta) != k4) stop("mismatch in the arguments")
  block1 <- 1:k1
  block2 <- (k1+1):k2
  block3 <- (k2+1):k3
  block4 <- k4
  beta  <- matrix(theta[block1], p, d)
  Omega <- vech2mat(theta[block2]) 
  Omega.inv <- pd.solve(Omega)
  eta <- theta[block3]
  nu <- theta[block4]
  a.incr <- if(cp.type=="proper") rep(0,4) else 1:4
  omega <- sqrt(diag(Omega))
  alpha <- eta*omega
  # delta <- delta.etc(alpha, Omega)$delta
  D <- duplicationMatrix(d)
  P <- matrix(0, d^2, d^2)
  for (i in 1:d) {
    Eii <- matrix(0,d,d)
    Eii[i,i] <- 1
    P <- P + Eii %x% Eii
    }
  omega <- sqrt(diag(Omega))
  d <- length(omega)
  delta.plus <- delta.etc(alpha, Omega)
  delta <- delta.plus$delta
  delta.sq <- (delta.plus$delta.star)^2
  alpha.sq <- (delta.plus$alpha.star)^2
  a  <- function(nu) nu/(nu-2)
  u  <- function(nu) 0.5*(1/nu + digamma((nu-1)/2) - digamma(nu/2))
  c1 <- function(nu) b(nu)/sqrt(1 + alpha.sq)
  q1 <- function(nu) a(nu)/(c1(nu)*(1 + beta0.sq(nu)))
  q2 <- function(nu) q1(nu)*(2*c1(nu) - q1(nu))/(2*a(nu))
  beta0.sq <- function(nu) # beta0.sq = sum(mu0 * Sigma.inv_mu0) =
    b(nu)^2 * alpha.sq/(a(nu)+(a(nu)-b(nu)^2)*alpha.sq)
  #-- Dtheta.dp = D_{DP}\theta
  Dtheta.dp <- diag(k4)
  diag(Dtheta.dp)[block3] <- 1/omega
  Deta.vOmega <- (-0.5)* (t(eta) %x% diag(1/omega^2, d, d)) %*% P %*% D
  Dtheta.dp[block3, block2] <- Deta.vOmega
  #
  mu0 <- function(nu) omega * b(nu) * delta
  Sigma.etc <- function(nu) {
    mu0. <- mu0(nu)
    Omega.inv_mu0 <- as.vector(Omega.inv %*% mu0.)
    Sigma <- a(nu)*Omega - outer(mu0., mu0.)
    sigma <- sqrt(diag(Sigma))
    tmp <- a(nu) - sum(mu0. *Omega.inv_mu0)
    Sigma.inv_mu0 <- Omega.inv_mu0/tmp
    Sigma.inv <- (Omega.inv + outer(Omega.inv_mu0, Omega.inv_mu0)/tmp)/a(nu)
    list(Sigma=Sigma, Sigma.inv=Sigma.inv, Sigma.inv_mu0=Sigma.inv_mu0,    
         sigma=sigma)
    }
  Dq1.nu <- function(nu){
    beta0_sq <- beta0.sq(nu)
    (-2/(nu-2)^2 -a(nu)*(b(nu)^2*u(nu)+beta0_sq/((nu-2)^2*(1+beta0_sq)))
            /c1(nu)^2)/(c1(nu)*(1+beta0_sq))
    } 
  # blocks for D_{\Psi}\theta   
  Dplus <- solve(t(D)%*% D) %*% t(D)
  DvOmega.vSigma <- function(nu) diag(d*(d+1)/2)/a(nu)
  DvOmega.mu0 <- function(nu)
    Dplus %*% (diag(d) %x% mu0(nu) + mu0(nu) %x% diag(d))/a(nu) 
  DvOmega.nu <- function(nu){
    s <- Sigma.etc(nu)    
    2*vech(s$Sigma + outer(mu0(nu), mu0(nu)))/nu^2
    }
  Deta.vSigma <- function(nu) { 
    S <- Sigma.etc(nu)
    t(-S$Sigma.inv_mu0) %x%  (q1(nu)* S$Sigma.inv -
         q1(nu) * q2(nu) *outer(S$Sigma.inv_mu0, S$Sigma.inv_mu0)) %*% D
    }
  Deta.mu0 <- function(nu) {
    S <- Sigma.etc(nu)
    q1(nu) * (S$Sigma.inv - 2*q2(nu)*outer(S$Sigma.inv_mu0, S$Sigma.inv_mu0))
    } 
  Deta.nu <- function(nu) Dq1.nu(nu) * Sigma.etc(nu)$Sigma.inv_mu0   
  #-- Dtheta.phi(phi)= D_{\Psi}\theta
  one00 <- c(1,rep(0,p-1))
  Dtheta.phi <- diag(k4)
  Dtheta.phi[block1, block3] <- -diag(d) %x% one00
  Dtheta.phi[block2, block2] <- DvOmega.vSigma(nu+a.incr[2])
  Dtheta.phi[block2, block3] <- DvOmega.mu0(nu+a.incr[2])
  Dtheta.phi[block2, block4] <- DvOmega.nu(nu+a.incr[2])
  Dtheta.phi[block3, block2] <- Deta.vSigma(nu+a.incr[2])
  Dtheta.phi[block3, block3] <- Deta.mu0(nu+a.incr[2])
  Dtheta.phi[block3, block4] <- Deta.nu(nu +a.incr[2])
  #
  # blocks for D_{\Psi}CP    
  Dgamma2M.misc <- function(nu){
    beta0_sq <- beta0.sq(nu)
    s <- Sigma.etc(nu)
    nu.34 <- (nu-3)*(nu-4)
    tmp2 <- ( (d+2)/nu.34
      + beta0_sq * (2*nu/((nu-3)*b(nu)^2) - (3*(nu-3)^2-6)/nu.34 ))
    Dgamma2M.mu0 <- as.vector(8 * tmp2 *  t(s$Sigma.inv_mu0))
    Dgamma2M.vSigma <- (-4 * tmp2) * as.vector(( t(s$Sigma.inv_mu0) %x%  
                       t(s$Sigma.inv_mu0)) %*% D)
    R <- b(nu)^2*delta.sq*(nu-2)/nu
    R1R <- R/(1-R) 
    PDgamma2.nu <- (-2*d*(d+2)/(nu-4)^2 -4*((2*nu-7)/nu.34^2) *R1R*(2/(1-R)+d)
       +2*(2*((nu-3)-nu*(1+2*(nu-3)*u(nu)))/((nu-3)*b(nu))^2 
       +(3*nu^2-22*nu+41)/nu.34^2)*R1R^2)      #\ref{f:partial_gamma2.nu}  
    list(Dgamma2M.vSigma=Dgamma2M.vSigma, Dgamma2M.mu0=Dgamma2M.mu0,  
         PDgamma2.nu=PDgamma2.nu)    
  }
  Dgamma1.misc <- function(nu) {
    sigma <- Sigma.etc(nu)$sigma
    lambda <- mu0(nu)/sigma
    g.nu <- 3/(nu-3)
    h.nu <- 1 + nu*(1-1/b(nu)^2)/(nu-3)
    Q <- g.nu*diag(d) + 3*h.nu*diag(lambda^2)
    Dgamma1.vOmega <- (t(-lambda/2) %x% (Q %*% diag(1/sigma^2,d))) %*% P %*% D
    Dgamma1.mu0 <- Q %*% diag(1/sigma,d)                            # K_{33}
    Dgamma1.nu <- (-3*lambda/(nu-3)^2 + (-3*(1-1/b(nu)^2)/(nu-3)^2 
                       + 2*nu*u(nu)/((nu-3)*b(nu)^2))*lambda^3)     # K_{34}
    list(Dgamma1.vOmega=Dgamma1.vOmega, Dgamma1.mu0=Dgamma1.mu0, 
         Dgamma1.nu=Dgamma1.nu)                   
    }
  #
  #--
  # Dcp.phi(phi) = D_{\Psi}(CP) [in the notes] = D_{\phi}\bar\rho [paper]
  #
  Dcp.phi <- diag(k4)
  K3 <- Dgamma1.misc(nu+a.incr[3])
  K4 <- Dgamma2M.misc(nu+a.incr[4])
  Dcp.phi[block3,block2] <- K3$Dgamma1.vOmega 
  Dcp.phi[block3,block3] <- K3$Dgamma1.mu0 
  Dcp.phi[block3,block4] <- K3$Dgamma1.nu
  Dcp.phi[block4,block2] <- K4$Dgamma2M.vSigma
  Dcp.phi[block4,block3] <- K4$Dgamma2M.mu0 
  Dcp.phi[block4,block4] <- K4$PDgamma2.nu  
  #
  # Dtheta.cp <- Dtheta.phi %*% solve(Dcp.phi)
  list(Dtheta.dp=Dtheta.dp, Dtheta.cp= Dtheta.phi %*% solve(Dcp.phi),
       Dtheta.phi=Dtheta.phi, Dcp.phi=Dcp.phi)
  } 
# 
mst.vdp2vcp <- function(vdp, p, d, cp.type="proper") 
{ # vdp = c(betaDP, vech(Omega), alpha, nu), 
  # vcp=(betaCP, vech(Sigma), gamma1, gamma2M)
  # d=ncol(y), p=ncol(x)
  beta <- matrix(vdp[1:(p*d)], p, d)
  vOmega <- vdp[(p*d+1):(p*d+d*(d+1)/2)]
  Omega <- vech2mat(vOmega)
  # omega <- sqrt(diag(Omega))
  alpha <- vdp[(p*d+d*(d+1)/2+1):(p*d+d*(d+1)/2+d)]
  nu <- vdp[p*d+d*(d+1)/2+d+1]
  dp <- list(beta=beta, Omega=Omega, alpha=alpha, nu=nu)
  cp <- mst.dp2cp(dp, cp.type=cp.type)
  c(cp[[1]], vech(cp[[2]]), cp[[3]], cp[[4]])
}  
#
mst.logL <- function(vdp,  X, y, dp=TRUE, penalty=NULL) 
{ # calcola logL rispetto a DP (se dp=TRUE) oppure a theta (se dp=FALSE),
  # con eventuale inclusione del termine 'penalty' se presente;
  # funziona non solo per ST, ma anche per SN ponendo dp$nu=Inf
  n <- nrow(y)
  d <- ncol(y)
  if(missing(X)) X <- matrix(1,n,1)
  p <- ncol(X)
  beta <- matrix(vdp[1:(p*d)], p, d)
  vOmega <- vdp[(p*d+1):(p*d+d*(d+1)/2)]
  Omega <- vech2mat(vOmega)
  # if(any(eigen(Omega)$values <= 0)) return(NA)
  if(any(diag(Omega) <= 0)) return(-Inf)
  omega <- sqrt(diag(Omega))
  tmp <- vdp[(p*d+d*(d+1)/2+1):(p*d+d*(d+1)/2+d)]
  alpha <- if(dp) tmp else tmp*omega
  nu <- vdp[p*d+d*(d+1)/2+d+1]
  if(nu <= 0) return(-Inf)
  Q <- if(is.null(penalty)) 0 else penalty(list(alpha, cov2cor(Omega)), nu)
  sum(dmst(y, X %*% beta, Omega, alpha, nu, log=TRUE)) - Q
}


st.infoMv <- function(dp, x=NULL, y, w, fixed.nu=NULL, symmetr=FALSE, 
   penalty=NULL, norm2.tol=1e-06)
{# Computes observed Fisher information matrices for multiv.ST distribution
 # using expressions of score function of Arellano-Valle (2010, Metron),
 # followed by numerical differentiation. Expected info matrix not implemented.
 # Info matrices are computed for DP, CP and pseudo-CP
  if(missing(y)) stop("missing y")
  if(!is.matrix(y)) stop("y is not matrix")
  type <- "observed"
  d <- ncol(dp$Omega)
  d2 <- d*(d+1)/2
  if(missing(w)) w <- rep(1, nrow(cbind(x,y)))
  if(any(w != round(w)) || any(w<0))
    stop("weights must be non-negative integers")
  n <- length(w)
  nw <- sum(w)
  if(is.null(x)) {
    p <- 1
    xx <- sum.x <- nw
    x <- matrix(1, nrow=n, ncol=1)
    }
  else { 
    p <- NCOL(x)
    # x <- matrix(x, n, p)
    xx <- drop(t(x) %*% (w*x))
    sum.x <- drop(matrix(colSums(w*x)))
    }
  beta <- as.matrix(dp[[1]], p, d)
  Omega <- dp[[2]]
  omega <- sqrt(diag(Omega))
  alpha <- if(symmetr) rep(0,d) else dp$alpha
  eta   <- alpha/omega
  nu <- if(is.null(fixed.nu)) dp$nu else fixed.nu 
  dp.full <- dp1  <- list(beta=beta, Omega=Omega, alpha=alpha, nu=nu)
  Obar <- cov2cor(Omega)
  Obar.alpha <-  as.vector(Obar %*% alpha)
  alpha.star <- sqrt(sum(alpha * Obar.alpha)) # =\sqrt{\eta\T\Omega\eta}
  theta <- as.numeric(c(beta, vech(Omega), eta, nu))
  vdp <- as.numeric(c(beta, vech(Omega), alpha, nu))  # include fixed param
  penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE) 
  args <- list(eps=1e-4, d=0.01, zero.tol=sqrt(.Machine$double.eps/7e-7), 
               r=4, v=2, show.details=TRUE) # inserted 2021-11-23 for v.2.0.1
  H <- numDeriv::hessian(mst.logL, vdp, method.args=args, X=x, y=y, dp=TRUE, penalty=penalty.fn)
  J <- mst.theta.jacobian(theta, p=NCOL(x), d=NCOL(y))
  # identify fixed components of parameter vector
  fixed.comp <- if(symmetr) d*p+d2+(1:d) else NULL
  if(!is.null(fixed.nu)) fixed.comp <- c(fixed.comp, length(vdp))
  # free: the free components of vdp, i.e. those not in fixed.param
  free <- setdiff(1:length(vdp), fixed.comp)
  tmp <- try(force.symmetry(-H[free ,free]), silent=TRUE)
  if(inherits(tmp, "try-error")) {
    warning("Problems occurred with numerical differentian of the log-likelihood")
    message(attr(tmp,"condition")$message)
    message("The returned object does not include standard errors")
    asyvar.dp <- I.theta <- I.dp <- NULL
  } else {    
    I.dp <- tmp
    J1 <- solve(J$Dtheta.dp[free, free])
    I.theta <- force.symmetry(t(J1) %*% I.dp %*% J1)
    asyvar.dp <- pd.solve(I.dp, silent=TRUE)
  }  
  if(is.null(asyvar.dp)) { 
    warning("Condition 'information_matrix > 0' fails, no standard errors")
    se.dp <- list(NULL)  
    }
  else {
    diags.dp <- sqrt(diag(asyvar.dp))
    se.beta <- matrix(diags.dp[1:(p*d)], p, d)
    se.diagOmega <- diags.dp[p*d + d2 +1 - rev(cumsum(1:d))]
    se.dp <- list(beta=se.beta, diagOmega=se.diagOmega)
    se.dp$alpha <- if(!symmetr) diags.dp[p*d +d2 +(1:d)] else NULL
    se.dp$nu <- if(is.null(fixed.nu)) diags.dp[length(vdp)] else NULL
    }
  if(!is.null(asyvar.dp) & nu>4) {
    cp <- mst.dp2cp(dp, cp.type="proper", fixed.nu=fixed.nu, symmetr=symmetr)
    I.cp <- t(J$Dtheta.cp[free,free]) %*% I.theta %*% J$Dtheta.cp[free,free]
    I.cp <- force.symmetry(I.cp)
    asyvar.cp <- pd.solve(I.cp, silent=TRUE)
    if(is.null(asyvar.cp)) { 
      se.cp <- list(NULL)  
      }
    else {
      diags.cp <- sqrt(diag(asyvar.cp))
      se.beta <- matrix(diags.cp[1:(p*d)], p, d)
      se.diagSigma <- diags.cp[p*d + d2 +1 - rev(cumsum(1:d))]
      # se.sigma <- se.Sigma/(2*sigma)
      se.gamma1 <- if(!symmetr)  diags.cp[p*d + d2 +(1:d)] else NULL
      se.cp <- list(beta=se.beta, var=se.diagSigma, gamma1=se.gamma1)
      if(is.null(fixed.nu)) se.cp$gamma2 <- diags.cp[length(vdp)] 
      }} 
  else 
    I.cp <- asyvar.cp <- se.cp <- cp <- NULL  
  if(is.null(asyvar.dp)) { 
    asyvar.pcp  <-  NULL
    se.pcp <- list(NULL)  
    Jp <- NULL
    }
  else {
    Jp <- numDeriv::jacobian(mst.vdp2vcp, vdp, p=ncol(x), d=ncol(y), 
            cp.type="pseudo")
    asyvar.pcp <- (Jp[free,free]) %*% asyvar.dp %*% t(Jp[free,free])
    diags.pcp <- sqrt(diag(asyvar.pcp))
    se.beta <- matrix(diags.pcp[1:(p*d)], p, d)
    se.diagSigma <- diags.pcp[p*d + d2 +1 - rev(cumsum(1:d))]
    # se.sigma <- se.Sigma/(2*sigma)
    se.gamma1 <- if(!symmetr) diags.pcp[p*d + d2 +(1:d)] else NULL
    se.pcp <- list(beta=se.beta, var=se.diagSigma, gamma1=se.gamma1)
    if(is.null(fixed.nu)) se.pcp$gamma2 <- diags.pcp[length(vdp)] 
    }
  aux <- list(Info.theta=I.theta, Dpseudocp.dp=Jp[free,free]) 
  list(dp=dp, cp=cp, type=type, info.dp=I.dp, info.cp=I.cp, 
    asyvar.dp=asyvar.dp, asyvar.cp=asyvar.cp, asyvar.p_cp=asyvar.pcp,
    se.dp=se.dp, se.cp=se.cp, se.p_cp=se.pcp,  aux=aux)
}


sn.mple <- function(x, y, cp=NULL, w, penalty=NULL, trace=FALSE, 
  opt.method=c("nlminb", "Nelder-Mead", "BFGS", "CG", "SANN"), control=list()) 
{# MPLE for CP of univariate SN (not intendend for ESN)
  if(trace) cat("[sn.mple] function is starting\n")
  if(missing(y)) stop("required argument y is missing")
  y.name <- deparse(substitute(y))
  if(!is.vector(y)) y <- as.vector(y)
  if(!is.numeric(y)) stop("argument y must be a numeric vector")
  n <- length(y)
  x <- if(missing(x)) matrix(rep(1, n), ncol = 1) else data.matrix(x)
  if(nrow(x) != n)  stop("incompatible dimensions")
  y.name <- deparse(substitute(y))
  x.name <- deparse(substitute(x))
  if (missing(w)) w <- rep(1,n)
  if(length(w) != n)  stop("incompatible dimensions")
  x.name <- deparse(substitute(x)) 
  p <- ncol(x)
  opt.method <- match.arg(opt.method)  
  max.gamma1 <- 0.5*(4-pi)*(2/(pi-2))^1.5 - (.Machine$double.eps)^(1/4)
  if(is.null(cp)) {
    qr.x <- qr(x)
    s <- sqrt(sum(qr.resid(qr.x, y)^2)/n)
    gamma1 <- sum(qr.resid(qr.x, y)^3)/(n*s^3)
    if(abs(gamma1) > max.gamma1) gamma1 <- sign(gamma1)*0.9*max.gamma1
    cp1 <- as.numeric(c(qr.coef(qr.x, y), s, gamma1))
    dp1 <- cp2dp(cp1, family="SN")
    logL1 <- sum(dsn(y, x %*% dp1[1:p], dp1[p+1], dp1[p+2], log=TRUE))
    sn.prelim <- st.prelimFit(x, y, verbose=as.numeric(trace), SN=TRUE)
    logL2 <- sn.prelim$logLik
    if(logL2 > logL1) {dp <- sn.prelim$dp; type <- 2} else {dp <- dp1; type <-1}
    cp <- dp2cp(dp, family="SN")
    if(trace) 
      cat("[sn.mple] initial CP estimates, type", type, "=", format(cp), "\n")
    }
  else{ 
    if(length(cp)!= (p+2)) stop("ncol(x)+2 != length(cp)")}
  penalty.fn <- if(is.null(penalty)) NULL else get(penalty, inherits=TRUE)  
  if(opt.method == "nlminb") {  
    opt <- nlminb(cp, objective=sn.pdev, 
           gradient=sn.pdev.gh, hessian=sn.pdev.hessian, 
           lower=c(-rep(Inf,p), sqrt(.Machine$double.eps), -max.gamma1), 
           upper=c(rep(Inf,p), Inf, max.gamma1), control=control,
           x=x, y=y, w=w, penalty=penalty.fn, trace=trace)
    opt$value <- opt$objective
    }
  else {
    opt <- optim(cp, fn=sn.pdev, gr=sn.pdev.gh,  
             method = opt.method, control = control,
             # lower & upper not used to allow all opt.method             
             x=x, y=y, w=w, penalty=penalty.fn, trace=trace)   
    } 
  cp <- opt$par
  names(cp) <- param.names("CP", "SN", p, colnames(x)[-1])
  logL <- (-opt$value)/2
  boundary <- as.logical(abs(cp[p+2]) >= max.gamma1)
  if(trace) {
    cat("[sn.mple] function is closing\n")
    cat("message from optimizer", opt.method, "(maybe empty):", opt$message, "\n")
    cat("estimates (cp) =", format(cp), "\n")
    cat("(penalized) log-likelihood =", format(logL, nsmall=2), "\n")
    }
  opt$method <- opt.method
  opt$called.by <- "sn.mple"  
  list(call=match.call(), cp=cp, logL=logL, boundary=boundary, opt.method=opt)
}


sn.pdev <- function(cp, x, y, w, penalty=NULL, trace=FALSE)
{ # "penalized deviance"=-2*(logL-Q) for centred parameters of SN distribution
  p <- ncol(x)
  if(abs(cp[p+2])> 0.9952717) return(Inf)
  if(missing(w)) w <- rep(1, length(y))
  if(any(w < 0)) stop("weights must be non-negative")
  dp <- cp2dpUv(cp, "SN") 
  if(any(is.na(dp))) return(NA)
  if(dp[p+1] <= 0) return(NA)
  xi <- as.vector(x %*% as.matrix(dp[1:p]))
  logL <- sum(w * dsn(y, xi, dp[p+1], dp[p+2], log=TRUE))
  Q <- if(is.null(penalty)) 0 else penalty(dp[p+2], der=0)
  if(trace) cat("sn.pdev: (cp,pdev) =", format(c(cp, -2*(logL-Q))),"\n")
  return(-2 * (logL - Q))
}


sn.pdev.gh <- function(cp, x, y, w, penalty=NULL, trace=FALSE, hessian=FALSE)
{ # computes gradient and hessian of pdev=-2*(logL-Q) for centred parameters  
  p  <- ncol(x)
  n  <- nrow(x)
  if(abs(cp[p+2]) > 0.9952717) return(rep(NA,p+2))
  if(missing(w)) w <- rep(1,n)
  if(any(w < 0)) stop("weights must be non-negative")
  score <- rep(NA,p+2)
  info  <- matrix(NA,p+2,p+2)
  beta  <- cp[1:p]
  sigma <- cp[p+1]
  gamma1 <- cp[p+2]
  nw <- sum(w)
  dp <-  cp2dpUv(cp, "SN") 
  lambda <- dp[p+2]
  mu <- as.vector(x %*% as.matrix(beta))
  d  <- y-mu
  r  <- d/sigma
  mu.z<- lambda*sqrt(2/(pi*(1+lambda^2)))
  sd.z<- sqrt(1-mu.z^2)
  z  <- mu.z+sd.z*r
  p1 <- as.vector(zeta(1,lambda*z))
  p2 <- as.vector(zeta(2,lambda*z))
  omega<- sigma/sd.z
  af    <- lambda*p1-mu.z
  Dmu.z <- sqrt(2/pi)/(1+lambda^2)^1.5
  Dsd.z <- (-mu.z/sd.z)*Dmu.z
  Dz    <- Dmu.z + r*Dsd.z
  DDmu.z<- (-3)*mu.z/(1+lambda^2)^2
  DDsd.z<- -((Dmu.z*sd.z-mu.z*Dsd.z)*Dmu.z/sd.z^2+mu.z*DDmu.z/sd.z)
  DDz   <- DDmu.z + r*DDsd.z
  score[1:p] <- omega^(-2) * t(x) %*% as.matrix(w*(y-mu-omega*af))
  score[p+1] <- (-nw)/sigma + sd.z*sum(w*d*(z-p1*lambda))/sigma^2
  score.l <- nw*Dsd.z/sd.z - sum(w*z*Dz) + sum(w*p1*(z+lambda*Dz))
  if(!is.null(penalty)) {
    Q <- penalty(lambda, der=2)
    score.l <- (score.l - attr(Q, "der1"))
    }
  Dg.Dl <- 1.5*(4-pi)*mu.z^2 * (Dmu.z*sd.z - mu.z*Dsd.z)/sd.z^4
  R <- mu.z/sd.z
  T <- sqrt(2/pi-(1-2/pi)*R^2)
  Dl.Dg <- 2*(T/(T*R)^2+(1-2/pi)/T^3)/(3*(4-pi))
  R. <- 2/(3*R^2 * (4-pi))
  T. <- (-R)*R.*(1-2/pi)/T
  DDl.Dg <- (-2/(3*(4-pi))) * (T./(R*T)^2+2*R./(T*R^3)+3*(1-2/pi)*T./T^4)
  score[p+2] <- score.l/Dg.Dl  # convert deriv wrt lamda to gamma1 
  gradient <- (-2)*score
  if(hessian){ # info = -(second deriv of logL)
     info[1:p,1:p] <- omega^(-2) * t(x) %*% (w*(1-lambda^2*p2)*x)
     info[1:p,p+1] <- info[p+1,1:p] <- 
            sd.z* t(x) %*% as.matrix(w*(z-lambda*p1)+ w*d*(1-lambda^2*p2)*
            sd.z/sigma)/sigma^2
     info[p+1,p+1] <- (-nw)/sigma^2 + 2*sd.z*sum(w*d*(z-lambda*p1))/sigma^3 +
            sd.z^2*sum(w*d*(1-lambda^2*p2)*d)/sigma^4
     info[1:p,p+2] <- info[p+2,1:p] <- t(x) %*% (w*
            (-2*Dsd.z*d/omega+Dsd.z*af+sd.z*(p1+lambda*p2*(z+lambda*Dz)
            -Dmu.z)))/sigma 
     info[p+1,p+2] <- info[p+2,p+1] <- 
            -sum(w*d*(Dsd.z*(z-lambda*p1)+sd.z*(Dz-p1-p2*lambda*(z+lambda*Dz))
             ))/sigma^2
     info[p+2,p+2] <- (nw*(-DDsd.z*sd.z+Dsd.z^2)/sd.z^2+sum(w*(Dz^2+z*DDz)) -
            sum(w*p2*(z+lambda*Dz)^2)- sum(w*p1*(2*Dz+lambda*DDz)))
     if(!is.null(penalty)) info[p+2,p+2] <- info[p+2,p+2] + attr(Q, "der2")
     info[p+2,] <- info[p+2,]/Dg.Dl # convert info wrt lambda to gamma1 
     info[,p+2] <- info[,p+2]*Dl.Dg # an equivalent form of the above
     info[p+2,p+2] <- info[p+2,p+2] - score.l*DDl.Dg
     attr(gradient,"hessian") <- force.symmetry(2*info)
     }
  if(trace) cat("sn.pdev.gh: gradient = ", format(gradient),"\n")
  return(gradient)
}

sn.pdev.hessian <- function(cp, x, y, w, penalty=NULL, trace=FALSE)
{
  gh <- sn.pdev.gh(cp, x, y, w, penalty=penalty, trace=trace, hessian=TRUE)
  attr(gh, "hessian")
}    
 

Qpenalty <- function(alpha_etc, nu=NULL, der=0)
{# 'standard' penalty function of logL, possibly with derivatives
  e1 <- e1. <- 1/3
  e2 <- e2. <- 0.2854166
  if(!is.null(nu)) if(nu<Inf) { 
    g <- 0.57721
    e1 <- e1. * (nu+2)*(nu+3)/(nu+1)^2
    e2 <- e2. * (1 + 4/(nu+g))
    } else nu <- NULL
  c1 <- 1/(4*e2)
  c2 <- e2/e1 
  if(is.vector(alpha_etc) && length(alpha_etc)==1) {
    alpha<- alpha_etc
    Obar.alpha <- alpha
    alpha2 <- alpha^2
    }
  else {
    if(!is.list(alpha_etc)) stop("wrong argument alpha_etc")
    alpha <- alpha_etc[[1]]
    Omega.bar <- alpha_etc[[2]]
    if(any(dim(Omega.bar) != length(alpha))) stop("dimension mismatch")
    Obar.alpha <- as.vector(Omega.bar %*% alpha)
    alpha2 <- sum(alpha* Obar.alpha)
    }
  Q <- c1 * log(1 + c2* alpha2)
  if(der==0) return(Q)
  der1 <- 2*c1*c2*Obar.alpha/(1+ c2*alpha2)   
  if(!is.null(nu)) { 
    h <- (nu+g)*(nu+2)*(nu+3)
    dc1.dnu <- 1/(e2.*(nu+g+4)^2)
    tmp <- ((nu+1)^2 + 2*(nu+1)*(nu+g+4)) * h - (nu+1)^2*(nu+g+4)*(
            (nu+2)*(nu+3)+ (nu+2)*(nu+g)+(nu+3)*(nu+g))
    dc2.dnu <- 3*e2.*tmp/h^2
    der1 <- c(der1, Q*dc1.dnu/c1+ c1*alpha2*dc2.dnu/(1+c2*alpha2)) 
    }
  attr(Q, "der1") <- der1
  if(der==2) {
    attr(Q, "der2") <- if(is.null(nu)) 
      2*c1*c2*(1-c2*alpha^2)/(1+c2*alpha^2)^2   else 
    { 
      # Qdash <- function(x)  attr(Qpenalty(x[1], x[2], der=1), "der1") 
      # H <- jacobian(Qdash, c(alpha,nu))
      Q.fn <- function(x) Qpenalty(x[1], x[2], der=0)
      numDeriv::hessian(Q.fn, c(alpha, nu))
    }
  }
  return(Q)
}

MPpenalty <- function(alpha, der=0) 
{# penalty function associated to "matching prior" of Cabras et al.(SJS, 2012)
  a <- sn.infoUv(dp=c(0,1,alpha))$aux$a.coef
  a0 <- a[1]
  a1 <- a[2]
  a2 <- a[3]
  A <- 1+alpha^2
  num <- (a2*A^2*(pi*(1+a0*alpha^4) + alpha^2*(pi*(1+a0)-4))
          +2*sqrt(2*pi)*a1*alpha*A^1.5 - pi*a1^2*alpha^2*A^3 -2)
  den <- (pi*A^3*(2+alpha^2*(2*a0+a2)+ alpha^4*(a0*a2-a1^2))
         -2*(alpha+2*alpha^3)^2 
         -2*sqrt(2*pi)*a1*alpha^3*sqrt(A)*(1+3*alpha^2+2*alpha^4))
  prior <- sqrt(num/den)
  penalty <- -log(prior)
  if(der > 0) attr(penalty,"der1") <- numDeriv::grad(MPpenalty, alpha)
  if(der > 1) attr(penalty,"der2") <- numDeriv::hessian(MPpenalty, alpha)
  return(penalty)
}


msn.mple <- function(x, y, start=NULL, w, trace=FALSE, penalty=NULL,
                opt.method=c("nlminb", "Nelder-Mead", "BFGS", "CG",  "SANN"),
                control=list() )
{
  if(trace) cat("[msn.mple] function is starting\n")
  y <- data.matrix(y)
  n <- nrow(y)
  if(missing(x)) x <- rep(1, n)
    else {if(!is.numeric(x)) stop("x must be numeric")}
  x <- data.matrix(x)
  if(nrow(x) != n) stop("incompatible dimensions") 
  if(missing(w)) w <- rep(1,n)
  if(length(w) != n) stop("incompatible dimensions") 
  nw <- sum(w)
  d <- ncol(y)  
  p <- ncol(x)
  y.names <- dimnames(y)[[2]] 
  x.names <- dimnames(x)[[2]]
  opt.method <- match.arg(opt.method)
  if(is.null(start))  start <- msn.mle(x, y, NULL, w, trace=trace)$dp
  if(trace){  
    cat("[msn.mple] initial parameters:\n")
    print(cbind(t(start[[1]]), start$Omega, start$alpha))
    }
  param <- dplist2optpar(start) 
  if(!is.null(penalty)) penalty <- get(penalty, inherits=TRUE)
  opt.method <- match.arg(opt.method)
  if(opt.method == "nlminb"){
    opt <- nlminb(param, msn.pdev, # msn.pdev.grad, 
                control=control, x=x, y=y, w=w, penalty=penalty, trace=trace)
    opt$value<- opt$objective 
    }
  else{
   opt <- optim(param, fn=msn.pdev, method=opt.method,
               control=control, x=x, y=y, w=w, penalty=penalty, trace=trace)
   }
  logL <- opt$value/(-2) 
  dp.list <- optpar2dplist(opt$par, d, p)
  beta <- dp.list$beta
  dimnames(beta)[2] <- list(y.names)
  dimnames(beta)[1] <- list(x.names)
  Omega <- dp.list$Omega
  alpha <- dp.list$alpha
  dimnames(Omega) <- list(y.names,y.names)
  names(alpha) <- y.names
  alpha2 <- sum(alpha * as.vector(cov2cor(Omega) %*% alpha))
  delta.star <- sqrt(alpha2/(1+alpha2))
  dp  <- list(beta=beta, Omega=Omega, alpha=alpha)
  opt$method <- opt.method
  opt$called.by <- "msn.mple"
  aux <- list(penalty=penalty, alpha.star=sqrt(alpha2), delta.star=delta.star)
  if(trace) {
    if(trace) cat("[msn.mple] function is closing\n")
    cat("message from optimization routine (maybe empty):", opt$message,"\n")
    cat("(penalized) log-likelikood:", format(logL, nsmall=2), "\n")
    }
  list(call=match.call(), dp=dp, logL=logL, aux=aux, opt.method=opt)
}

msn.pdev <- function(param, x, y, w, penalty=NULL, trace=FALSE)
{ # -2*(profile.logL - Q)
  d <- ncol(y)
  if(missing(w)) w <- rep(1, nrow(y))
  n <- sum(w)
  p <- ncol(x)
  dp. <- optpar2dplist(param, d=ncol(y), p=ncol(x))
  logL <- sum(w * dmsn(y, x %*% dp.$beta, dp.$Omega, dp.$alpha, log=TRUE))
  Q <- if(is.null(penalty)) 0 else penalty(list(dp.$alpha,dp.$Omega), der=0)
  pdev <- (-2)*(logL-Q)
  if(trace) 
    cat("[msn.pdev] opt param:", format(param), "\nmsn.pdev:", format(pdev),"\n")
  return(pdev)
}
 
optpar2dplist <- function(param, d, p, x.names=NULL, y.names=NULL)
{# convert vector form of optimization parameters to DP list;
 # output includes inverse(Omega) and its log determinant 
  beta <- matrix(param[1:(p * d)], p, d)
  D <- exp(-2 * param[(p * d + 1):(p * d + d)])
  A <- diag(d)
  i0 <- p*d + d*(d+1)/2
  if(d>1)  A[!lower.tri(A,diag=TRUE)] <- param[(p*d+d+1):i0]
  eta <- param[(i0 + 1):(i0 + d)]
  nu <- if(length(param) == (i0 + d + 1)) exp(param[i0 + d + 1]) else NULL
  Oinv <- t(A) %*% diag(D,d,d) %*% A
  # Omega <- pd.solve(Oinv)
  Ainv <- backsolve(A, diag(d))
  Omega <- Ainv %*% diag(1/D,d,d) %*% t(Ainv)
  Omega <- (Omega + t(Omega))/2
  omega <- sqrt(diag(Omega))
  alpha <- eta * omega
  dimnames(beta) <- list(x.names, y.names)
  dimnames(Omega) <- list(y.names, y.names)
  if (length(y.names) > 0) names(alpha) <- y.names
  dp <- list(beta=beta, Omega=Omega, alpha=alpha)
  if(!is.null(nu)) dp$nu <- nu
  list(dp=dp, beta=beta, Omega=Omega, alpha=alpha, nu=nu, Omega.inv=Oinv,
     log.det=sum(log(D)))
}

dplist2optpar <- function(dp,  Omega.inv=NULL)
{# convert DP list to vector form of optimization parameters 
  beta <- dp[[1]]
  Omega <- dp[[2]]
  alpha <- dp[[3]]
  d <- length(alpha)
  nu <- if(is.null(dp$nu)) NULL else dp$null
  eta <- alpha/sqrt(diag(Omega))
  Oinv <- if(is.null(Omega.inv)) pd.solve(Omega) else Omega.inv
  if(is.null(Oinv)) stop("matrix Omega not symmetric positive definite")
  upper <- chol(Oinv)
  D <- diag(upper)
  A <- upper/D
  D <- D^2
  param <- if(d > 1)  c(beta, -log(D)/2, A[!lower.tri(A, diag = TRUE)], eta)
     else c(beta, -log(D)/2, eta)
  if(!is.null(dp$nu))  param <- c(param, log(dp$nu)) 
  param <- as.numeric(param)
  attr(param, 'ind') <- cumsum(c(length(beta), d, d*(d-1)/2, d, length(dp$nu)))
  return(param) 
}  


force.symmetry <- function(x, tol=10*sqrt(.Machine$double.eps)) 
{
  if(!is.matrix(x)) stop("x must be a matrix")
  # err <- abs(x-t(x))
  err <- abs(x-t(x))/(1+abs(x))
  max.err <- max(err/(1+err))
  if(max.err > tol) warning("matrix seems not symmetric")
  if(max.err > 100*tol) stop("this matrix really seems not symmetric")
  return((x + t(x))/2)
}
 
duplicationMatrix <- duplication_matrix <- function (n=1)
{# translated by AA from Octave code written by <Kurt.Hornik@wu-wien.ac.at>
  if ( (n<1) |  (round (n) != n) ) stop ("n must be a positive integer")
  d <- matrix (0, n * n, n * (n + 1) / 2)
  ## KH: It is clearly possible to make this a LOT faster!
  count = 0
  for (j in 1 : n){
    d [(j - 1) * n + j, count + j] = 1
    if(j<n) {
      for (i in (j + 1) : n){
       d [(j - 1) * n + i, count + i] = 1
       d [(i - 1) * n + j, count + i] = 1
    }}
    count = count + n - j
  }
  return(d)
}

vech <- function(A) if(is.matrix(A)) A[lower.tri(A, diag=TRUE)] else 
  stop("argument 'A' must be a matrix")

vech2mat <- function(v) 
{# inverse function of vech(A)
  if(mode(v) != "numeric" | !is.vector(v)) stop("wrong type of argument")
  n <- round((-1 + sqrt(1 + 8*length(v)))/2)
  if(length(v) != n*(n+1)/2) stop("wrong length of vector 'v'") 
  A <- matrix(0, n, n)
  A[lower.tri(A,TRUE)] <- v
  return(A + t(A) - diag(diag(A), n))
}

#-------
# source("/Users/aa/SN/Pkg-sn/R/Code_bits/plot_SEC_Uv.R")

plot.SECdistrUv <- function(x, range, probs, main, npt=251, data=NULL, ...)
{# plot density of object "SECdistrUv"
  obj <- x
  lc.family <- tolower(slot(obj, "family"))
  if(lc.family == "esn") lc.family <- "sn"
  dp <- slot(obj, "dp")
  d.fn <- get(paste("d", lc.family, sep=""), inherits = TRUE)
  q.fn <- get(paste("q", lc.family, sep=""), inherits = TRUE)
  if(missing(probs)) probs <- c(0.05, 0.25, 0.5, 0.75, 0.95)
  if(!is.numeric(data)) data <- NULL
  q <- if(is.null(probs)) NULL else q.fn(probs, dp=dp)
  if(missing(range)) {
    q0 <- q.fn(c(0.05, 0.2, 0.8, 0.95), dp=dp)
    dq <- diff(q0)
    range <- c(q0[1]- 1.5*dq[1], q0[length(q0)] +  1.5*dq[length(dq)])
    if(!is.null(q)) {
      range[1] <- min(range[1], min(q))
      range[2] <- max(range[2], max(q))
      }
    if(!is.null(data)) {
      range[1] <- min(range[1], min(data))
      range[2] <- max(range[2], max(data))
    }}
  dots <- list(...)
  nmdots <- names(dots)  
  topline <- if(obj@name == "") "" else	
    paste("Probability density of ", obj@name, "\n", sep="")
  if(missing(main))  main <- paste(topline, obj@family," distribution",
     ", dp = (",paste(format(dp), collapse=", "),")",sep="") 
  mar <- if ("mar" %in% nmdots) dots$mar else NULL
  if (is.null(mar)) {
    mar <- c(4.5, 4.5, 4, 2)
    if (is.null(main))  mar[3L] <- 2
    }     
  omar <- par()$mar
  on.exit(par(omar))
  par(mar=mar)    
  x <- seq(min(range), max(range), length=npt)
  pdf <- d.fn(x, dp=dp)
  xLab <- if("xlab" %in% nmdots) dots$xlab else slot(obj, "name")
  yLab <- if("ylab" %in% nmdots) dots$ylab else "probability density"
  yLim <- if("ylim" %in% nmdots) dots$ylim else c(0, max(pdf))
  plot(x, pdf, type="n", xlab=xLab, ylab=yLab, ylim=yLim)
  lines(x, pdf, ...)
  abline(h=0, lty=2, col="gray50")
  if(!is.null(q)) {
    points(q, rep(max(pdf)/100,length(q)), cex=0.75, col="gray50", pch=6)
    text(q, par()$usr[3]/2, format(probs), cex=0.75, col="gray50")
    }
  if(!is.null(data)) {
    side <- if(is.null(probs)) 1 else 3  
    rug(data, side=side, ticksize = 0.02, col="gray50")
    }
  if (!is.null(main)) {
    font.m <- if("font.main" %in% nmdots) dots$font.main else par("font.main") 
    cex.m <- if("cex.main" %in% nmdots) dots$cex.main else par("cex.main") 
    title(main, line=2, cex.main=cex.m, font.main=font.m)
    }    
  invisible(list(object=obj, x=x, density=pdf))
}
      
plot.SECdistrMv <- function(x, range, probs, npt, landmarks="auto",
       main, comp, compLabs, data = NULL, data.par=NULL, gap = 0.5, ...) 
{# plot density of object of class "SECdistrMv"  
  obj <- x
  if(slot(obj, "class") != "SECdistrMv") stop("object of wrong class")
  dp <- slot(obj, "dp")
  d <- length(dp$xi)
  if(missing(comp)) comp <- seq(1, d) 
  if(!all(comp %in% seq(1,d))) stop("illegal 'comp' value(s)")
  pd <- length(comp) # actual plotting dimension
  pobj <- if(pd == d) obj else marginalSECdistr(obj, comp=comp, drop=FALSE)
  name.pobj <- slot(obj, "name")
  if(pd < d) name.pobj <- paste(name.pobj,"[", paste(comp, collapse=","), "]", sep="")
  if(missing(probs)) probs <- c(0.25, 0.50, 0.75, 0.95)    
  if(any(probs <= 0) | any(probs >= 1)) stop("probs must be within (0,1)") 
  if(missing(npt)) npt <- rep(101, pd)
  if(missing(main))  { main <- if(pd == 1 | pd == 2)
      paste("Density function of", name.pobj) else
      paste("Bivariate densities of", name.pobj) 
      }
  compNames <- slot(pobj, "compNames")
  if(missing(compLabs)) compLabs <- compNames
  if(length(compLabs) != pd) stop("wrong length of 'compLabs' vector")
  family <- toupper(obj@family)
  lc.family <- tolower(family)
  if(lc.family == "esn") lc.family <- "sn"
  if(missing(range)) {
    range <- matrix(NA, 2, pd)
    q.fn <- get(paste("q", lc.family, sep=""), inherits=TRUE)
    for(j in 1:pd) {
      marg <- marginalSECdistr(pobj, comp=j, drop=TRUE)
      q <- q.fn(c(0.05, 0.25, 0.75, 0.95), dp=marg@dp)
      dq <- diff(q)
      range[,j] <- c(q[1] - 1.5*dq[1], q[length(q)] + 1.5*dq[length(dq)])
      # 2019-01-13: next lines have been modified 
      if(!is.null(data)) {
        q <- quantile(data[,j], probs=c(0.05, 0.25, 0.75, 0.95))
        dq <- diff(q)
        range[1,j] <- min(range[1,j], q[1] - 2.5*dq[1])
        range[2,j] <- max(range[2,j], q[length(q)] + 2.5*dq[length(dq)])
        }
      }
    }
  dots <- list(...)
  nmdots <- names(dots)  
  if(pd == 1) {  
    message("Since dimension=1, plot as a univariate distribution")
    objUv <- marginalSECdistr(pobj, comp=comp, drop=TRUE)
    out <- plot(objUv, data=data, ...)
    }
  if(pd == 2) {
    p <- plot.SECdistrBv(pobj, range, probs, npt, compNames,  
                           compLabs, landmarks, data, data.par, main, ...)
    out <- list(object=pobj, plot=p)
    } 
  if(pd > 2) {
    textPanel <- function(x = 0.5, y = 0.5, txt, cex, font) 
      text(x, y, txt, cex = cex, font = font)
    localAxis <- function(side, x, y, xpd, bg, main,  oma, ...) {
      if (side%%2 == 1) Axis(x, side = side, xpd = NA,  ...) else 
      Axis(y, side = side, xpd = NA, ...)
      }
    localPlot <- function(..., oma, font.main, cex.main) plot.SECdistrBv(...)
    text.diag.panel <- compLabs
    oma <- if ("oma" %in% nmdots) dots$oma else NULL
    if (is.null(oma)) {
      oma <- c(4, 4, 4, 4)
      if (!is.null(main))  oma[3L] <- 6
      }    
    opar <- par(mfrow = c(length(comp), length(comp)), 
                mar = rep(c(gap,gap/2), each=2), oma=oma)
    on.exit(par(opar))
    out <- list(object=pobj)
    count <- 1
    for (i in comp) 
      for (j in comp) {
        count <- count + 1
        if(i == j) {
          plot(1, type="n", xlab="", ylab="", axes=FALSE)
          text(1, 1, text.diag.panel[i], cex=2)
          box()
          out[[count]] <- list()
          names(out)[count] <- paste("diagonal component", compNames[i])
        } else {
        ji <- c(j,i) 
        marg <- marginalSECdistr(pobj, comp=ji) 
        out[[count]] <- localPlot(x=marg, range=range[,ji], probs=probs,
             npt=npt[ji], compNames= compNames[ji], compLabs=compLabs[ji], 
             landmarks=landmarks, data=data[,ji],  data.par=data.par, 
             main="", yaxt="n", xaxt="n", ...)   
        names(out)[count] <- paste("plot of components (", j, ",", i, ")")
        # if(i==comp[1]) {axis(3); if(j==length(comp)) axis(4)}
        # if(j==comp[1]) {axis(2); if(i==length(comp)) axis(1)}
        if(i==comp[1]) axis(3) ; if(j==length(comp)) axis(4)
        if(j==comp[1]) axis(2) ; if(i==length(comp)) axis(1)    
        box() }
      }
    par(new = FALSE)
    if (!is.null(main)) {
      font.main <- if ("font.main" %in% nmdots) 
         dots$font.main else par("font.main") 
      cex.main <- if ("cex.main" %in% nmdots) 
         dots$cex.main  else par("cex.main") 
      mtext(main, side=3, TRUE, line=5, outer = TRUE, at=NA, cex=cex.main, 
            font=font.main, adj=0.5)
      }}
  invisible(out)
}

plot.SECdistrBv <- function(x, range, probs, npt=rep(101,2), compNames,   
                            compLabs, landmarks, data=NULL, data.par, main, ...)
{# plot BiVariate SEC distribution
  obj <- x
  dp <- slot(obj, "dp")
  family <- slot(obj, "family")
  lc.family <- tolower(family)
  if(lc.family == "esn") lc.family <- "sn"
  d.fn <- get(paste("dm", lc.family, sep=""), inherits=TRUE) # density funct
  n1 <- npt[1]
  n2 <- npt[2]
  x1 <- seq(min(range[,1]), max(range[,1]), length=n1)
  x2 <- seq(min(range[,2]), max(range[,2]), length=n2)
  x1.x2 <- cbind(rep(x1, n2), as.vector(matrix(x2, n1, n2, byrow=TRUE)))
  X <- matrix(x1.x2, n1 * n2, 2, byrow = FALSE)
  pdf <- matrix(d.fn(X, dp=dp), n1, n2)
  Omega <- dp[[2]]
  Omega.bar <- cov2cor(Omega)
  alpha <- dp[[3]]
  alpha.star <- sqrt(sum(alpha * as.vector(Omega.bar %*% alpha)))
  if(missing(probs) | is.null(probs)) probs <- c(0.25, 0.50, 0.75, 0.95)    
  omega <- sqrt(diag(Omega))
  if(lc.family == "sn") {
    k.tau <- if (length(dp) == 4) (zeta(2,dp[[4]])*pi)^2/4 else 1
    log.levels <- (log(1-probs) - log(2*pi)- 0.5*log(1-Omega.bar[1,2]^2)
                   + k.tau * log(1+exp(-1.544/alpha.star))) - sum(log(omega))
    }
  if(lc.family == "st" | lc.family == "sc") {
    nu <- if(lc.family == "st") obj@dp[[4]] else 1
    l.nu <- (-1.3/nu - 4.93)
    if(alpha.star > 0) {
      h <- 100 * log(exp(((1.005*alpha.star-0.045)* l.nu -1.5)/alpha.star)+1) 
      K <-  h *(1.005*alpha.star-0.1)*(1+nu)/(alpha.star * nu) }  else K <- 0
    qF <- qf(probs, 2, nu)
    log.levels <- (lgamma(nu/2+1) -lgamma(nu/2) - log(pi*nu) 
           -0.5*log(1-Omega.bar[1,2]^2) - (nu/2+1)*log(2*qF/nu + 1)  + K
           -sum(log(omega)))
    } 
  oo <- options()
  options(warn=-1)
  d.levels <- exp(log.levels)  
  names(d.levels) <- as.character(probs)
  contour(x1, x2, pdf, levels=d.levels, 
    labels=paste("p=", as.character(probs), sep=""),
    main=main, xlab=compLabs[1], ylab=compLabs[2], ...)
  if(!is.null(data)) {
    col <- if(!is.null(data.par$col)) data.par$col else par()$col
    pch <- if(!is.null(data.par$pch)) data.par$pch else par()$pch
    cex <- if(!is.null(data.par$cex)) data.par$cex else par()$cex
    points(data, col=col, pch=pch, cex=cex)
    if(!is.null(id.i <- data.par$id.i)) 
      text(data[id.i,1], data[id.i,2], id.i, cex=cex/1.5, pos=1)
    }
  if(landmarks != "") {
    if(landmarks == "auto") { 
      mean.type <-  "proper"  
      if(lc.family == "sc") mean.type <- "pseudo"      
      if(lc.family == "st") { if(dp[[4]] <= 1)  mean.type <- "pseudo"}
     }
    else 
    mean.type <- landmarks
    landmarks.label <- 
       c("origin", "mode", if(mean.type == "proper")  "mean" else "mean~")
    cp <- dp2cpMv(dp, family, cp.type=mean.type, upto=1)
    mode <- modeSECdistrMv(dp, family)
    x.pts <- c(dp$xi[1], mode[1], cp[[1]][1])
    y.pts <- c(dp$xi[2], mode[2], cp[[1]][2])
    points(x.pts, y.pts, ...)
    col <- if(!is.null(list(...)$col)) list(...)$col else par()$col
    text(x.pts, y.pts, landmarks.label, pos=2, offset=0.3, col=col)
    lines(x.pts, y.pts, lty=2, col=col)
    }  
  options(oo) 
  cL <- contourLines(x1, x2, pdf, levels=d.levels)
  for(j in 1:length(probs)) cL[[j]]$prob <- probs[j]
  return(list(x=x1, y=x2, names=compNames, density=pdf, contourLines=cL))
}    

plot.selm <- function(x, param.type="CP", which = c(1:4), caption, 
    panel = if (add.smooth) panel.smooth else points, main = "", 
    # sub.caption = NULL, 
    ask = prod(par("mfcol")) < length(which) && dev.interactive(), ..., 
    id.n = 3, labels.id = names(x@residuals.dp), 
    cex.id = 0.75, identline = TRUE, add.smooth = getOption("add.smooth"), 
    label.pos = c(4, 2), cex.caption = 1) 
{
    if(!(is(x, "selm"))) stop("object not of class 'selm'")
    show <- rep(FALSE, 4)
    show[which] <- TRUE
    dots <- list(...)
    nmdots <- names(dots)  
    p <- slot(x, "size")["p"]
    if(missing(caption))  { caption <-  if(p> 1) 
      c("Residuals vs Fitted Values", 
       "Residual values and fitted error distribution", 
       "Q-Q plot of (scaled DP residuals)^2",
       "P-P plot of (scaled DP residuals)^2") else
      c("Boxplot of observed values", 
       "Empirical values and fitted distribution", 
       "Q-Q plot of (scaled DP residuals)^2",
       "P-P plot of (scaled DP residuals)^2")}
    all.par <- slot(x, "param")
    param.type <- tolower(param.type)  
    param <- all.par[[param.type]]
    if(is.null(param)) { message(paste(
        "Requested param.type='", param.type, "' evaluates to NULL.", sep=""))
      if(param.type == "pseudo-cp" & x@family== "SN") 
        message("Pseudo-CP makes no sense for SN family")
      if(param.type == "cp" & x@family== "SC")
        message("CP makes no sense for SC family")
      if(param.type == "cp" & x@family== "ST")
        message("CP of ST family requires nu>4")  
      stop("Consider another choice of param.type (DP or pseudo-CP)")
      }
    r <- residuals(x, param.type)
    r.lab <- paste(toupper(param.type), "residuals")
    dp <- if(length(all.par$fixed) > 0) all.par$dp.complete else all.par$dp
    nu. <- switch(x@family, ST = dp[p+3], SN = Inf, SC=1)  
    rs <- slot(x,"residuals.dp")/dp[p+1]
    rs2 <- rs^2
    n <- slot(x, "size")["n.obs"]
    yh <- fitted(x, param.type)    
    w <- weights(x)
    if (!is.null(w)) {
        wind <- (w != 0)
        r <- r[wind]
        yh <- yh[wind]
        w <- w[wind]
        labels.id <- labels.id[wind]
    }
    else w <- rep(1,n)
    rw <- n*w/slot(x,"size")["nw.obs"]
    cex.pts <- rw * if("cex" %in% nmdots) dots$cex else par("cex")
    if (is.null(id.n)) 
        id.n <- 0
    else {
        id.n <- as.integer(id.n)
        if (id.n < 0 || id.n > n) 
            stop(gettextf("'id.n' must be in {1,..,%d}", n), domain = NA)
    }
    if (id.n > 0) {
        if (is.null(labels.id)) 
            labels.id <- paste(1:n)
        iid <- 1:id.n
        # show.r <- sort.list(abs(r), decreasing = TRUE)[iid]        
        show.rs <- sort.list(rs2, decreasing = TRUE)[iid]
        # rs2.lab <- paste("(scaled DP residuals)^2")
        text.id <- function(x, y, ind, adj.x = TRUE) {
            labpos <- if (adj.x) 
                label.pos[1 + as.numeric(x > mean(range(x)))]
            else 3
            text(x, y, labels.id[ind], cex = cex.id, xpd = TRUE, 
                pos = labpos, offset = 0.25)
        }
    }
    one.fig <- prod(par("mfcol")) == 1
    if (ask) {
        oask <- devAskNewPage(TRUE)
        on.exit(devAskNewPage(oask))
    }
    if (show[1]) {
      if(all(is.na(r)) & p>1)  message(paste("CP residuals not available;",
         "consider param.type='DP' or 'pseudo-CP'"))
      else { 
        if(p == 1){ 
          y <-  (x@residuals.dp + x@fitted.values.dp) 
          boxplot(y, plot=TRUE, col="gray85", border="gray60")
          }
        else { # p>1
        # if (id.n > 0) 
        #    ylim <- extendrange(r = ylim, f = 0.08)        
        ylim <- range(r, na.rm = TRUE)
        plot(yh, r, xlab = "Fitted values", ylab = r.lab, main = main, 
            ylim = ylim, type = "n")
        panel(yh, r, ...)  # previously it included 'cex=cex.pts'
        # if (one.fig) title(sub = sub.caption, ...)
        if (id.n > 0) {
          y.id <- r[show.rs]
          y.id[y.id < 0] <- y.id[y.id < 0] - strheight(" ")/3
          text.id(yh[show.rs], y.id, show.rs)
          }
        abline(h = 0, lty = 2, col = "gray")
        } }
    mtext(caption[1], 3, 0.5, cex = cex.caption) }
    if (show[2]) {
      if(all(is.na(r)) & p>1) message(
        "CP residuals not available; consider param.type='DP' or 'pseudo-CP'")
      else {
        if (p == 1){
          y <-  (x@residuals.dp + x@fitted.values.dp) 
          dp0 <- dp
          xlab="observed variable"}
        else {
          y <- r
          dp0 <- as.numeric(c(dp[1]-param[1], dp[-(1:p)]))
          xlab=r.lab
        }
        h <- hist(rep(y, w), plot=FALSE)
        extr <- extendrange(x=h$breaks)
        x.pts <- seq(max(extr), min(extr), length=501) 
        d.fn <- get(paste("d", tolower(x@family), sep=""), inherits = TRUE)
        pdf <- d.fn(x.pts, dp=dp0)
        plot(c(h$mids, x.pts), c(h$density, pdf), type="n", main=main, 
          xlab=xlab,  ylab="probability density")
        hist(rep(y, w), col="gray95", border="gray60", probability=TRUE, 
          freq=FALSE, add=TRUE)
        lines(x.pts, pdf, ...)
        rug(y, ticksize=0.02, ...)
        # if (id.n > 0) {     rug(y, ticksize=0.015, ...)
        #   text(y[show.rs], 0, labels.id[show.rs], srt=90, cex=0.5, pos=1, 
        #   offset=0.2) } 
        mtext(caption[2], 3, 0.25, cex = cex.caption)
      }}
    if (show[3]) {
      ylim <- c(0, max(pretty(rs2)))
      q <- qf((1:n)/(n+1), 1, nu.)
      plot(q, sort(rs2), xlab="Theoretical values", ylab="Empirical values", 
        ylim=ylim, type="p", main=main, ...)   # cex=cex.pts
      if(identline) abline(0, 1, lty = 2, col = "gray50")
      # if (one.fig) title(sub = sub.caption, ...)
      mtext(caption[3], 3, 0.25, cex = cex.caption)
      if (id.n > 0) text.id(q[n+1-iid], rs2[show.rs], show.rs) 
    }
    if (show[4]) {
      p <- (1:n)/(n+1)
      pr <- pf(sort(rs2), 1, nu.)
      plot(p, pr, xlab="Theoretical values", ylab="Empirical values",
         xlim=c(0,1), ylim=c(0,1), main=main, ...) # cex=cex.pts,
      if(identline) abline(0, 1, lty = 2, col = "gray50")
      # if (one.fig)  title(sub = sub.caption, ...)
      mtext(caption[4], 3, 0.25, cex = cex.caption)
      if(identline) abline(0, 1, lty = 2, col = "gray50")
      if (id.n > 0)  text.id(p[n+1-iid], pr[n+1-iid], show.rs)
    } 
    # if (!one.fig && par("oma")[3] >= 1) 
    #     mtext(sub.caption, outer = TRUE, cex = 1.25)
    invisible()
  }


print.summary.selm <- function(object)
{
    obj <- object
    digits = max(3, getOption("digits") - 3)
    cat("Call: ")
    print(slot(obj, "call"))
    n <- obj@size["n.obs"]
    cat("Number of observations:", n, "\n")
    if(!is.null(slot(obj,"aux")$weights))
      cat("Weighted number of observations:", obj@size["nw.obs"], "\n")
    cat("Family:", slot(obj,"family"), "\n")
    fixed <- slot(obj, "param.fixed") 
    if(length(fixed) > 0) { fixed.char <-
         paste(names(fixed), format(fixed), sep=" = ", collapse=", ")
         cat("Fixed parameters:", fixed.char, "\n") }
    method <- slot(obj, "method")
    u <- if(length(method)==1) NULL else paste(", penalty function:", method[2])
    cat("Estimation method: ", method[1], u, "\n", sep="")
    logL.name <- paste(if(method[1] == "MLE") "Log" else "Penalized log",
         "likelihood:", sep="-")
    cat(logL.name, format(slot(obj,"logL"), nsmall=2), "\n")
    param.type <- slot(obj, "param.type")
    cat("Parameter type:", param.type,"\n") 
    if((note <- slot(object,"note")) != "") cat(paste("Note:", note, "\n"))
    if(obj@boundary) 
      cat("Estimates on/near the boundary of the parameter space\n")
    resid <- slot(obj, "resid")
    if(n > 5) {
      nam <- c("Min", "1Q", "Median", "3Q", "Max")
      rq <- if (length(dim(resid)) == 2) 
            structure(apply(t(resid), 1, quantile), dimnames = list(nam, 
                dimnames(resid)[[2]]))
      else structure(quantile(resid), names = nam)
      cat("\n", param.type, " residuals:\n", sep="")
      print(rq, digits = digits)
    }
    param <- slot(obj, "param.table")
    p <- obj@size["p"]
    cat("\nRegression coefficients\n")
    printCoefmat(param[1:p, ,drop=FALSE], digits = digits,  
      signif.stars = getOption("show.signif.stars"), na.print = "NA")
    cat("\nParameters of the SEC random component\n")
    printCoefmat(param[(p+1):nrow(param), 1:2, drop=FALSE], digits = digits,  
      signif.stars = FALSE, na.print = "NA")  
    if(!is.null(obj@aux$param.cor)) {
      cat("\nCorrelations of parameter estimates:\n")
      print(obj@aux$param.cor)
      }
    if(!is.null(obj@aux$param.cov)) {
      cat("\nCovariances of parameter estimates:\n")
      print(obj@aux$param.cov)
      } 
  invisible(object)
}


plot.mselm <- function (x, param.type="CP", which, caption, 
    panel = if (add.smooth) panel.smooth else points, main = "", 
    # sub.caption = NULL, 
    ask = prod(par("mfcol")) < length(which) && dev.interactive(), ..., 
    id.n = 3, labels.id = names(x@residuals.dp), 
    cex.id = 0.75, identline = TRUE, add.smooth = getOption("add.smooth"), 
    label.pos = c(4, 2), cex.caption = 1) 
  { 
    p <- slot(x,"size")["p"]
    if(missing(which)) which <- if(p == 1) c(1,3,4) else 2:4
    show <- rep(FALSE, 4)
    show[which] <- TRUE
    if(!show[2]) param.type <- "DP"   # CP-residuals only used for show[2]
    lc.param.type <- tolower(param.type)
    param.type <- switch(lc.param.type, 
      "dp"="DP", "op"="OP", "cp"="CP", "pseudo-cp"="pseudo-CP")  
    if(param.type == "OP") stop("this method does not support OP option")
    if(missing(caption)) caption <- 
       c("Observed values and fitted distribution", 
       paste("Distribution of", param.type, "residual values"),
       "Q-Q plot of Mahalanobis distances",
       "P-P plot of Mahalanobis distances")
    all.par <- slot(x, "param")
    param <- all.par[[lc.param.type]]
    dots <- list(...)
    if(is.null(param)) { message(paste(
        "Requested param.type='", param.type, "' evaluates to NULL.", sep=""))
      if(param.type == "pseudo-cp" & x@family== "SN") 
        message("Pseudo-CP makes no sense for SN family")
      if(param.type == "cp" & x@family== "SC")
        message("CP makes no sense for SC family")
      if(param.type == "cp" & x@family== "ST")
        message("CP of ST family requires nu>4")  
      stop("Consider another choice of param.type, e.g. param.type='DP'")
      }
    r <- residuals(x, lc.param.type)
    r.lab <- paste(param.type, "residuals")
    # family <- x@family 
    dp <- if(length(all.par$fixed) > 0) all.par$dp.complete else all.par$dp
    cp <- dp2cpMv(dp, family=x@family, cp.type="auto") 
    nu. <- switch(x@family, ST = dp$nu, SN = Inf, SC=1)     
    n <- slot(x,"size")["n.obs"]
    d  <- x@size["d"]
    yh <- fitted(x, param.type)    
    w <- weights(x)
    if (!is.null(w)) {
        wind <- w != 0
        r <- r[wind]
        yh <- yh[wind]
        w <- w[wind]
        labels.id <- labels.id[wind]
    }
    else w <- rep(1,n)
    rw <- n*w/slot(x,"size")["nw.obs"]
    if (is.null(id.n)) 
        id.n <- 0
    else {
        id.n <- as.integer(id.n)
        if (id.n < 0 || id.n > n) 
            stop(gettextf("'id.n' must be in {1,..,%d}", n), domain = NA)
    }
    Omega.inv <- pd.solve(dp$Omega, silent=TRUE)
    r.dp <- t(slot(x, "residuals.dp"))
    rs2 <- colSums((Omega.inv %*% r.dp) * r.dp)
    if (id.n > 0) {
		if (is.null(labels.id)) labels.id <- paste(1:n)
		iid <- 1:id.n
		show.r <- sort.list(abs(r), decreasing = TRUE)[iid]		
		show.rs <- sort.list(rs2, decreasing = TRUE)[iid]
		text.id <- function(x, y, ind, adj.x = TRUE) {
			labpos <- if (adj.x) 
				label.pos[1 + as.numeric(x > mean(range(x)))]
			else 3
			text(x, y, labels.id[ind], cex = cex.id, xpd = TRUE, 
				pos = labpos, offset = 0.25)
		} 
      } else show.rs <- NULL
    one.fig <- prod(par("mfcol")) == 1
    if (ask) {
        oask <- devAskNewPage(TRUE)
        on.exit(devAskNewPage(oask))
    }
    if (show[1]) { # data scatter matrix and fitted curves (only if p=1)
      if(p == 1) {
        y <- (x@residuals.dp + x@fitted.values.dp)  
        fitted.distr <- makeSECdistr(dp, family=x@family, 
          name="fitted distribution", compNames=colnames(x@param$dp[[1]]))
        data.par <- list(col=dots$col, pch=dots$pch, cex=dots$cex,
          id.i=show.rs)
        plot(fitted.distr, landmarks="", data=y, main=main, data.par=data.par,
             ...)   # previously it included cex=sqrt(rw)
        # text.id(..) se d=1, ma se d>1 si deve fare per ogni pannello (?!)
        mtext(caption[1], 3, 1.5, cex = cex.caption)
        } else  
      message(paste("plot of (observed data, fitted distribution)",
              "makes no sense if covariates 'x' exist", 
              "and fitted distribution varies with 'x'"))
      }
    if (show[2]) { # scatter matrix of residuals and fitted curves
      dp0 <- dp
      dp0[[1]] <-  as.numeric((dp[[1]]-param[[1]])[1,])
      data.par <- list(col=dots$col, pch=dots$pch, cex=dots$cex,
            id.i=show.rs)
      resid.distr <- makeSECdistr(dp0, family=x@family, 
         name="Residual distribution", compNames=colnames(x@residuals.dp))
      plot(resid.distr, landmarks="", data=residuals(x, param.type), 
         main=main, data.par=data.par)
      # mtext(caption[2], 3, 0.25, cex = cex.caption)
      mtext(caption[2], 3, 1.5, cex = cex.caption)
      }
    if (show[3]) { # QQ-plot
      # ylim <- c(0, max(pretty(rs2)))
      q <- qf((1:n)/(n+1), d, nu.) * d
      plot(q, sort(rs2), xlab="theoretical values", ylab="empirical values",
           main=main, ...)  # cex=sqrt(rw) now dropped
      if(identline) abline(0, 1, lty = 2, col = "gray70")
      # if (one.fig) title(sub = sub.caption, ...)
      mtext(caption[3], 3, 0.25, cex = cex.caption)
      if (id.n > 0)  text.id(q[n+1-iid], rs2[show.rs], show.rs)
      }
    if (show[4]) { # PP-plot
      p <- pf(rs2/d, d, nu.)
      p0 <- (1:n)/(n+1) 
      plot(p0, sort(p),  xlab="theoretical values", ylab="empirical values",
         xlim=c(0,1), ylim=c(0,1), main=main, ...)  # cex=sqrt(rw) now dropped
      if(identline) abline(0, 1, lty = 2, col = "gray70")
      # if (one.fig) title(sub = sub.caption, ...)
      mtext(caption[4], 3, 0.25, cex = cex.caption)
      if (id.n > 0) text.id(p[show.rs], p0[n+1-iid], show.rs)
      } 
    # if (!one.fig && par("oma")[3] >= 1) 
    #    mtext(sub.caption, outer = TRUE, cex = 1.25)
    invisible()
  }


print.summary.mselm <-  function(object)
{
    obj <- object
    digits = max(3, getOption("digits") - 3)
    # cat("Obj: ", deparse(substitute(obj)),"\n")
    cat("Call: ")
    print(slot(obj,"call"))
    n <- obj@size["n.obs"]
    d <- obj@size["d"]
    # p <- obj@size["p"]
    cat("Number of observations:", n, "\n")
    nw <- obj@size["nw.obs"]
    if(n != nw)  cat("Weighted number of observations:", nw, "\n")
    family <- slot(obj, "family")
    cat("Family:", family, "\n")
    method <- slot(object, "method") 
    u <- if(length(method)==1) NULL else 
         paste(", penalty function:", method[2])
    cat("Estimation method: ", method[1], u, "\n", sep="")
    fixed <- slot(obj, "param.fixed")
    if(length(fixed) > 0) {fixed.char <- 
        paste(names(fixed), format(fixed), sep=" = ", collapse=", ")
      cat("Fixed parameters:", fixed.char, "\n") }
    cat("Log-likelihood:", format(slot(obj,"logL"), nsmall=2), "\n")
    cat("Parameter type:", obj@param.type,"\n") 
    if((note <- slot(object, "note")) != "") cat(paste("Note:", note, "\n"))
    if(obj@boundary) 
      cat("Estimates on/near the boundary of the parameter space\n")
    names <- dimnames(obj@scatter$matrix)[[1]]
    for(j in 1:d) {
      param <- obj@coef.tables[[j]]
      cat("\n--- Response variable No.", j, ": ", names[j],"\n",sep="")
      resid <- obj@resid[,j]
      if(n>5) {
        nam <- c("Min", "1Q", "Median", "3Q", "Max")
        rq <- if (length(dim(resid)) == 2) 
              structure(apply(t(resid), 1, quantile), dimnames = list(nam, 
                  dimnames(resid)[[2]]))
        else structure(quantile(resid), names = nam)
        cat(obj@param.type, "residuals\n")
        print(rq, digits = digits)
      }
      cat("\nRegression coefficients\n")
      printCoefmat(param[, ,drop=FALSE], digits = digits,  
        signif.stars = getOption("show.signif.stars"), na.print = "NA")
    }
    cat("\n--- Parameters of the SEC random component\n")
    cat("Scatter matrix: ", obj@scatter$name,"\n", sep="")
    print(obj@scatter$matrix) 
    if(length(obj@slant) > 0) {
      cat("\nSlant parameter: ", obj@slant$name, "\n", sep="")
      print(cbind(estimate=obj@slant$param, std.err=obj@slant$se))
      }
    if(length(obj@tail) > 0) {
       cat("\nTail-weight parameter: ", obj@tail$name, "\n", sep="")
       print(c(estimate=obj@tail$param, std.err=obj@tail$se))
      }
    if(!is.null(obj@aux$param.cor)) {
      cat("\nCorrelations of parameter estimates:\n")
      print(obj@aux$param.cor)
      }
    if(!is.null(obj@aux$param.cov)) {
      cat("\nVar-covariance matrix of parameter estimates:\n")
      print(obj@aux$param.cov)
      }
}

dp2op <- function(dp, family)
{
  nt <- switch(tolower(family), "sn" = 3, "esn" = 4, "st" = 4, "sc" = 3, NULL)
  if(is.null(nt)) stop("unknown family")
  op <- dp
  if (is.list(dp))  { # multivariate case 
    if(length(dp) != nt) stop("wrong length of 'dp'")
    Omega <- dp[[2]] 
    alpha <- dp[[3]]
    d <- length(alpha)
    tmp <- delta.etc(alpha, Omega)
    delta <- tmp$delta
    Omega.cor <- tmp$Omega.cor
    D.delta <- sqrt(1 - delta^2)    # (5.18) of SN book, but as vector
    lambda <- delta/D.delta         # (5.20)
    omega <- sqrt(diag(as.matrix(Omega)))
    Psi <- Omega - outer(omega*delta, omega*delta)  # four lines before (5.30)
    op[[2]] <- Psi
    op[[3]] <- lambda
    names(op)[2:3] <- c("Psi", "lambda")
    }
  else {  # univariate case
    p <- length(dp) - nt + 1
    if(p < 1) stop("wrong length of 'dp'")
    delta <- delta.etc(dp[p+2])
    op[p+1] <- dp[p+1] * sqrt(1 - delta^2)
    names(op)[(p+1):(p+2)] <- c("psi", "lambda")
    } 
  op
}

op2dp <- function(op, family) 
{
  nt <- switch(tolower(family), "sn" = 3, "esn" = 4, "st" = 4, "sc" = 3, NULL)
  if(is.null(nt)) stop("unknown family")
  dp <- op
  if(is.list(op)) { # multivariate case 
    if(length(op) != nt) stop("wrong length of 'op'")
    Psi <- op[[2]] 
    psi <- sqrt(diag(Psi))
    lambda <- op[[3]]
    delta <- lambda/sqrt(1 + lambda^2)
    D.delta <- sqrt(1 - delta^2)
    Psi.bar <- cov2cor(Psi)
    omega <- psi/D.delta
    tmp <- as.vector(pd.solve(Psi.bar) %*% lambda)
    dp[[2]] <- Psi + outer(psi*lambda, psi*lambda)  # four lines before (5.30)
    dp[[3]] <- (tmp/D.delta)/sqrt(1 + sum(lambda*tmp))  # (5.22)
    names(dp)[2:3] <- c("Omega", "alpha")
    } 
  else { # univariate case
    p <- length(op) - nt + 1
    if(p < 1) stop("wrong length of 'dp'")
    delta <- delta.etc(dp[p+2])
    dp[p+1] <- op[p+1]/sqrt(1 - delta^2)
    names(dp)[(p+1):(p+2)] <- c("omega", "alpha")
    }  
  dp
}
 
coef.selm <- function(object, param.type="CP", ...) {
    param <- slot(object,"param")[[tolower(param.type)]]
    if(is.null(param) & tolower(param.type)=="cp") {
        message("CP not defined, consider param.type='DP' or 'pseudo-CP'")
        return(NULL)}
    param} 
 
coef.mselm <- function(object, param.type="CP", vector=TRUE, ...) 
{
    list <- slot(object,"param")[[tolower(param.type)]]
    if(is.null(list) & tolower(param.type)=="cp") {
        message("CP not defined, consider param.type='DP' or 'pseudo-CP'")
        return(NULL)}
    if(!vector) return(list)
    as.vector(c(list[[1]], vech(list[[2]]), unlist(list[3:length(list)])))
}

extractSECdistr <- function(object, name, compNames) 
{
  obj.class <- class(object)
  if(!(obj.class %in% c("selm", "mselm")))
    stop(gettextf("wrong object class: '%s'", obj.class), domain = NA)
  param <- slot(object, "param")   
  dp <- if(length(param$dp.complete) > 0) param$dp.complete else param$dp
  p <- slot(object, "size")[2]
  if(obj.class == "selm")  {
    lead <- if(p > 1) 0 else dp[1]
    dp0 <- c(lead, dp[-(1:p)])  
    names(dp0)[1] <- "xi"
    }
  else { # class = "mselm"
    dp0 <- dp
    names(dp0)[1] <- "xi"
    dp0[[1]] <- if(p == 1) as.vector(dp0[[1]]) else 
                rep(0, slot(object, "size")[1])
    }
  if((obj.class == "mselm") & missing(compNames)) compNames <- names(dp$alpha)  
  if(missing(name)) {
    name <- paste("SEC distribution of", deparse(substitute(object))) 
    name <- if(p > 1) paste("Residual", name) else paste("Fitted", name)
    }   
  if(obj.class == "selm")  
    new("SECdistrUv", dp=dp0, family=slot(object, "family"), name=name) else 
    new("SECdistrMv", dp=dp0, family=slot(object, "family"), name=name,
        compNames=compNames)   
}


# introduce sd generic function, in the same fashion of package circular
#
sd <- function(x, ...) UseMethod("sd")
sd.default <- function(x, na.rm = FALSE, ...) stats::sd(x=x, na.rm=na.rm)

mean.SECdistrUv <- function(x) dp2cp(object=x, upto=1)
mean.SECdistrMv <- function(x) dp2cp(object=x, upto=1)[[1]]
sd.SECdistrUv <- function(x) dp2cp(object=x, upto=2)[2]
vcov.SECdistrMv <- function(object) dp2cp(object=object, upto=2)[[2]]

#----------------------------
# profile.selm updated version 1.6-0
profile.selm <- function(fitted, param.type, param.name, param.values, npt,
  opt.control=list(), plot.it=TRUE, log=TRUE, levels, trace=FALSE, ...)
{ obj <- fitted
  if(!is(obj, "selm"))
    stop(gettextf("wrong object class: '%s'", class(obj)), domain = NA)
  param.type <- match.arg(toupper(param.type), c("DP", "CP"))
  family <- slot(obj, "family")
  obj.par <- slot(obj, "param")
  dp.full <- if(length(obj.par$fixed)==0) obj.par$dp else obj.par$dp.complete
  if(param.type == "CP") { 
    cp.full <- mle.full <- dp2cpUv(dp.full, family)
    profile.comp <- match(param.name, names(cp.full))
    }
  else {
    mle.full <- dp.full
    profile.comp <- match(param.name, names(dp.full))
    }
  fixed.names <- setdiff(names(obj.par$dp.complete), names(obj.par$dp))
  if(length(fixed.names) > 0) {
    fixed.comp <- match(fixed.names, names(dp.full))
    fixed.values <- mle.full[fixed.comp] 
    }
    else fixed.comp <- fixed.values <- NULL
  clash <- intersect(fixed.comp, profile.comp)
  if(length(clash) > 0)  stop(paste("parameter component No.", clash,
       "is fixed in the model, it cannot be profiled"))
  p <- slot(obj, "size")["p"]
  method <- slot(obj, "method")
  penalty <- if(method[1] == "MPLE") method[2] else NULL
  constr.comp <- c(profile.comp, fixed.comp)
  free.comp <- setdiff(1:length(dp.full), constr.comp)
  if(anyNA(profile.comp)) stop("some wrong item in param.name")
  npc <- length(profile.comp) # number of terms in profile.comp (either 1 or 2)
  if(!(npc %in% (1:2))) stop("wrong length(param.name)")
  if(missing(npt)) npt <- rep((50+npc) %/% npc, npc) else
     if(length(npt) != npc) npt <- rep(npt[1], npc)
  log.comp <- if(!log) rep(NA, npc) else { 
    if(param.type == "DP") match(c("omega", "nu"), param.name, NULL)   
    else match(c("s.d.", "gamma2"), param.name, NULL) }  
  logScale <- (1:2) %in% which(!is.na(log.comp))   
  m <- slot(obj, "input")$model
  x <- model.matrix(attr(m, "terms"), data=m)
  w <- slot(obj, "input")$model$"(weights)"
  weights <- if(is.null(w)) rep(1, nrow(x)) else w
  opt.control$fnscale <- (-1)
  par.val <- param.values
  if(npc == 1) { # one-parameter profile logLik
    par.val <- as.vector(par.val)
    if(any(diff(par.val) <= 0)) stop("param.values not an increasing sequence")
    logScale <- logScale[1]
    if(length(par.val) == 2)
      par.val <- seqLog(par.val[1], par.val[2], length=npt, logScale)
    n.values <- length(par.val)  
    if(n.values>1 & (prod(range(par.val) - mle.full[profile.comp]) > 0)) {
       message(gettextf(
         "Note: param range does not bracket the MLE/MPLE point: '%s'",
         format(mle.full[profile.comp])), domain=NA)
       bracket <- FALSE 
       fail.confint <- TRUE
       } else bracket <- TRUE
    logL <- numeric(n.values)
    for(k in 1:n.values) {
      constr.values <- c(par.val[k], fixed.values)
      free.values <- mle.full[-constr.comp] 
      opt <- optim(free.values, constrained.logLik,  method="BFGS",
        control=opt.control, param.type=param.type, x=x, y=m[[1]],
        weights=weights, family=family, constr.comp=constr.comp, 
        constr.values=constr.values, penalty=penalty, trace=trace)
      logL[k] <- opt$value  
      }
    out <- list(call=match.call(), param=par.val, logLik=logL)
    names(out)[2] <- param.name  
    if(n.values > 1){
    deviance <- 2*(slot(obj, "logL") - logL)
    out$deviance <- deviance
    if(any(deviance + sqrt(.Machine$double.eps) < 0)) warning(paste(
      "A relative maximum of the (penalized) likelihood seems to have been",
      "taken as\n the MLE (or MPLE).",
      "Re-fit the model with starting values suggested by the plot."))
    s <- diff((sign(diff(deviance))))
    if(length(which(s != 0)) > 1) {
       warning(paste("The log-likelihood function appears to have multiple",
        "maxima.\n", "Confidence intervals may be handled improperly.\n"))
       # readline("Press <Enter> to continue<cr>")
       # browser()
       }}
    if(missing(levels)) levels <- 0.95
    levels <- levels[1]
    if(is.na(levels) | levels <= 0 | levels >= 1) {
      message("illegal levels value is reset to default value")
      levels <- 0.95 }
    if(obj.par$boundary) {message(paste(
      "estimates at the boundary of the parameter space,", 
      "no confidence interval"))
      levels <- NULL
      } 
    if(!is.null(levels) & n.values>1 & bracket) {
      q <- qchisq(levels[1], 1)
      if(deviance[1] < q | deviance[n.values] < q) warning(
        "parameter range seems short; confidence interval may be inaccurate")
      dev.fn <- splinefun(par.val, deviance - q, method="monoH.FC")
      rootL <- try(uniroot(dev.fn, lower=min(par.val),  check.conv=TRUE, 
                   upper=mle.full[profile.comp],  extendInt="downX"))
      rootH <- try(uniroot(dev.fn, lower=mle.full[profile.comp], 
                   upper=max(par.val), check.conv=TRUE, extendInt="upX")) 
      fail.confint <- (class(rootL)=="try-error" | class(rootH)=="try-error")                   
      out$confint <- if(fail.confint) rep(NULL,2) else c(rootL$root, rootH$root)   
      out$levels <- levels                         
      }
    if(plot.it & n.values>1) {  
      if(logScale) { 
        par.val <-  log(par.val)
        param.name <- paste("log(", param.name, ")", sep="")
        }
      plot(par.val, deviance, type="l", xlab=param.name,
          ylab="2*{max(logLik) - logLik}", ...)
      if(bracket) {     
      if(logScale) {
          rug(log(mle.full[profile.comp]), ticksize = 0.02)
          if(is.null(levels) | fail.confint) low <- hi <- NULL else { 
            low <- log(rootL$root)
            hi <- log(rootH$root) }}
        else {
          rug(mle.full[profile.comp], ticksize = 0.02)
          if(is.null(levels)| fail.confint) low <- hi <- NULL else { 
            low <- rootL$root
            hi <- rootH$root
          }}
      if(!is.null(levels) & !fail.confint) { 
        abline(h=q, lty=3, ...)
        lines(rep(low, 2), c(par()$usr[3], q), lty=3, ...)
        lines(rep(hi, 2), c(par()$usr[3], q), lty=3, ...)
        }}
      }
    }
  else { # npc==2, two-parameter profile logLik
    if(length(par.val) != 2) stop("wrong dimension of param.values")
    u <- unlist(lapply(par.val, length))
    param1 <- par.val[[1]]
    param2 <- par.val[[2]]
    if(all(u>1)) 
      if(prod(range(param1) - mle.full[profile.comp][1]) > 0 |
        prod(range(param2) - mle.full[profile.comp][2]) > 0) {
          message(gettextf(
            "Note: parameter range does not bracket the MLE/MPLE point: '%s'",
            paste(format(mle.full[profile.comp]), collapse=",")), domain=NA)
          bracket <- FALSE} else bracket <- TRUE    
    if(u[1] > 2) npt[1] <- u[1] else if(u[1] == 2) 
      param1 <- seqLog(param1[1], param1[2], length=npt[1], logScale[1])
    if(u[2] > 2) npt[2] <- u[2] else if(u[2] == 2) 
      param2 <- seqLog(param2[1], param2[2], length=npt[2], logScale[2]) 
    n.values <- c(length(param1), length(param2))
    logL <- matrix(NA, n.values[1], n.values[2])
    if(any(diff(param1) <= 0)) 
       stop("param.values[[1]] not an increasing sequence")
    if(any(diff(param2) <= 0)) 
       stop("param.values[[2]] not an increasing sequence")
    mle.profile <- mle.full[profile.comp]   
    fn.dist <- function(p1, p2, q, h=1) sqrt(h*(p1-q[1])^2 + (p2-q[2])^2)
    dist <- matrix(0, n.values[1], n.values[2])   
    for(k1 in 1:n.values[1]) for(k2 in 1:n.values[2])
       dist[k1,k2] <- fn.dist(param1[k1], param2[k2], mle.profile, h=1)
    # dist <- outer(param1, param2, fn.dist, q=mle.profile, h=1)  
    s <- which(dist==min(dist), arr.ind=TRUE)
    s <- matrix(s, ncol=2)[1,] 
    spiral <- discreteSpiral(s, n.values[1], n.values[2])
    pts <- spiral$path[spiral$feasible,]
    logL <- matrix(NA, n.values[1], n.values[2])
    last.estimate <- mle.full
    for(k in 1:prod(n.values)) {
      pt <- pts[k,]
      k1 <- pt[1]
      k2 <- pt[2]
      constr.values <- c(param1[k1], param2[k2], fixed.values)
      free.values <- last.estimate[-constr.comp] 
      opt.control <- list()
      opt <- nlminb(free.values, constrained.logLik, negative=TRUE,
        control=opt.control, param.type=param.type, x=x, y=m[[1]],
        weights=weights, family=family, constr.comp=constr.comp, 
        constr.values=constr.values, penalty=penalty, trace=trace)  
      logL[k1,k2] <- (-opt$objective) 
      last.estimate[-constr.comp] <- opt$par 
      }
    out <- list(call=match.call(), param1=param1, param2=param2, logLik=logL)
    names(out)[2:3] <- param.name  
    if(missing(levels)) levels <- c(0.25, 0.5, 0.75, 0.9, 0.95, 0.99)
    if(anyNA(levels) | any(levels<=0) | any(levels>=1)) {
      message("illegal levels values; vector 'levels' reset to default value")
      levels <- c(0.25, 0.5, 0.75, 0.9, 0.95, 0.99) }
    if(obj.par$boundary) {message(
      "MLE/MPLEs at the boundary of the parameter space, no confidence regions")
      levels <- NULL
      } 
    q <- if(is.null(levels)) 
         c(0.5, 1, 2, 5, 10, 20, 40, 80) else qchisq(levels, 2) 
    deviance <- 2*(slot(obj, "logL") - logL)
    if(any(deviance + sqrt(.Machine$double.eps) < 0)) message(paste(
      "A relative maximum, or a minimum, of the (penalized) log-likelihood",
      "seems to have been taken as the MLE/MPLE. Unless the global maximum",
      "is divergent, consider refitting the model with starting values",
      "suggested by the plot.", sep="\n"))
    if(all(n.values>1)) {
      cL <- contourLines(param1, param2, deviance, levels=q)
      if(length(cL) > 0) {
        out$deviance.contour <- cL
        if(!is.null(levels)) for(j in 1:length(cL)) {
          k <- which(q == cL[[j]]$levels)
          out$deviance.contour[[j]]$prob <- levels[k]
        }} else {
        message(paste(
          "There appears to be something odd with the fitted MLE/MPLE.",
          "The contour levels denote logLik values, not confidence levels.", 
          sep="\n"))
        contour(param1, param2, out$logLik, xlab=param.name[1], 
          ylab=param.name[2], ...)   
        return(out) 
        }}
    if(plot.it & all(n.values>1)) {
      if(logScale[1]) { 
            param1 <-  log(param1)
        param.name[1] <- paste("log(", param.name[1], ")", sep="")
        }                 
      if(logScale[2]) {
            param2 <-  log(param2)
            param.name[2] <- paste("log(", param.name[2], ")", sep="")
            }      
      contour(param1, param2, deviance, levels=q, labels=levels,
         xlab=param.name[1], ylab=param.name[2], ...)
      if(bracket) {     
        mark <- mle.full[profile.comp]
        mark[logScale] <- log(mark[logScale])
        points(mark[1], mark[2], pch=3, col=2)  
        }
      }
    }
  invisible(out)
}


#
discreteSpiral <- function(s, maxX, maxY)
{# spiralling around s=c(sx, sy) in rectangle (1,...,maxX) \times (1,...,maxY)
  outside <- function(pt)
     if(any(pt < 1) | pt[1] > maxX | pt[2] > maxY) TRUE else FALSE
  if(outside(s)) stop("invalid starting point 's'")
  heading <- 0  #   0=N, 1=E, 2=S, 3=W
  h.add <- rbind(c(0,1), c(1,0), c(0,-1), c(-1,0))
  step <- 0L
  path <- pt <- s
  feasible <- TRUE
  repeat {
    step <- step + 1L
    for(j in 1:2) {
      for(k in 1:step) {
        pt <- pt + h.add[heading+1, ]
        feasible <- c(feasible, !outside(pt))
        path <- rbind(path, pt)
        }
      heading <- (heading + 1L) %% 4L
      }
   if(sum(feasible) == maxX*maxY) break
   }
  return(list(path=path, feasible=feasible))
}

constrained.logLik <- function(free.param, param.type, x, y, weights, family, 
  constr.comp=NA, constr.values=NA, penalty=NULL, trace=FALSE, negative=FALSE)
{
  if(trace) cat("[constrained.logLik] free.param:", format(free.param))
  n <- sum(weights)
  p <- ncol(x)
  param <- numeric(length(free.param) + length(constr.values))
  param[constr.comp] <- constr.values
  param[-constr.comp] <- free.param
  bad <- if(negative) Inf else -Inf
  par0 <- c(0, param[-(1:p)])
  if(par0[2] <= 0) return(bad)
  if(family=="ST" & par0[4] <= 0) return(bad) 
  if(family=="ST" & par0[4] > 1e4) par0[4] <- Inf
  dp0 <- if(param.type =="DP") par0 else 
    cp2dpUv(par0, family, tol=1e-7, silent=TRUE)
  if(anyNA(dp0)) {
    if(is.null(dp0)) {message("null dp0, please report"); browser()}
    excess <- attr(dp0, "excess")
    if(length(excess) == 0) {message("0-length excess, please report"); browser()}
    if(is.null(excess) | is.na(excess) | abs(excess)==Inf ) 
        excess <- (.Machine$double.xmax)^(1/3)
        # {message("bad excess"); browser()}
    return(-1e9 * (1+ excess)^2)
    } 
  d.fn <- get(paste("d", tolower(family), sep=""), inherits = TRUE)
  logL <- try(d.fn((y - x %*% param[1:p]), dp=dp0, log=TRUE))
  if(inherits(logL, "try-error"))  browser()
  Q <- if(is.null(penalty)) 0 else {
    penalty.fn <- get(penalty, inherits = TRUE)
    nu <- if(family=="ST") par0[4] else NULL
    penalty.fn(dp0[3], nu)
    } 
  out <- if(anyNA(logL)) -Inf else sum(logL * weights) - Q 
  if(trace) cat(", logL:", format(out), "\n")
  if(negative) out <- (-out)
  return(out)
}

seqLog <- function(from, to, length, logScale=FALSE) {
  if(logScale & any(c(from, to) <= 0)) 
    stop("logScale requires positive arguments 'from' and 'to'")
  if(logScale)  exp(seq(log(from), log(to), length.out=length)) else
    seq(from, to, length.out=length) 
  }

predict.selm <- function(object, newdata, param.type = "CP", 
     interval = "none", level = 0.95, na.action = na.pass, ...) 
{
  model <- slot(object, "input")$model
  interval <- match.arg(interval, c("none", "confidence", "prediction"))
  tt <- terms(model)
  if (missing(newdata) || is.null(newdata)) {
      response <- attr(attr(model, "terms"), "response")
      intercept <- attr(attr(model, "terms"), "intercept")
      mm <- X <- cbind(intercept, data.matrix(model)[, -response])
      mmDone <- TRUE
      offset <- model$offset
  }
  else {
      Terms <- delete.response(tt)
      m <- model.frame(Terms, newdata, na.action = na.action, 
          xlev = model$xlevels)
      X <- model.matrix(Terms, m, contrasts.arg = model$contrasts)
      offset <- rep(0, nrow(X))
      if (!is.null(off.num <- attr(tt, "offset"))) 
          for (i in off.num) offset <- offset + eval(attr(tt, 
              "variables")[[i + 1]], newdata)
      if (!is.null(model$offset)) 
          offset <- offset + eval(mode$offset, newdata)
      mmDone <- FALSE
  }
  size <- slot(object, "size")
  n <- size["n.obs"]
  nw <- size["nw.obs"]
  p <- size["p"]
  one..p <- seq_len(p)
  beta <- coef(object, param.type=param.type)[one..p]
  out <- predictor <- drop(X[, one..p, drop = FALSE] %*% beta)
  if(!is.null(offset))  predictor <- predictor + offset
  family <- slot(object, "family")
  V <- vcov(object, param.type=param.type)[one..p,one..p]
  var.conf <- rowSums((X %*% V) * X)
  if(family == "SN" & param.type=="DP") {
    alpha.interv <- confint(object, "alpha", param.type="DP")
    if(prod(alpha.interv) <=- 0) var.conf <- rep(NA, nrow(X))
    } 
  if(interval == "confidence") {
    hwid <- qnorm((1 - level)/2) * sqrt(var.conf) 
    lwr <- predictor + hwid
    upr <- predictor - hwid 
    out <- cbind(predictor, lwr, upr)
    colnames(out) <- c("fit", "lwr", "upr")       
    }
  if(interval == "prediction") {
    if(missing(newdata)) 
      warning("predictions on current data refer to _future_ responses\n")
    probs <- c((1-level)/2, (1+level)/2)
    npt <- nrow(X)
    lwr <- upr <- rep(NA, npt)
    if(family == "SN") {
      # convolve SN+Normal
      betaCP <- coef(object, param.type="CP")[one..p]
      predictorCP <- drop(X[, one..p, drop = FALSE] %*% betaCP)
      if(!is.null(offset))  predictorCP <- predictorCP + offset
      Vcp <- vcov(object, param.type="CP")[one..p,one..p]
      var.pred <- rowSums((X %*% Vcp) * X)
      omega <- coef(object, param.type="DP")[p+1]
      alpha <- coef(object, param.type="DP")[p+2]
      mu.eps <- as.numeric(omega*sqrt(2/pi)*alpha/sqrt(1+alpha^2))
      alpha.tilde <- alpha/sqrt(1+(1+alpha^2)*var.pred/omega^2)
      for(j in 1:npt) {
        q <- if(is.na(var.pred[j])) rep(NA,2) else
          qsn(probs, -mu.eps, sqrt(var.pred[j]+omega^2), alpha.tilde[j])
        lwr[j] <- predictorCP[j] + q[1]
        upr[j] <- predictorCP[j] + q[2]
      } }
  if(family %in% c("ST", "SC")) {
    # approximate ST+normal convolution
    dp <- coef(object, param.type="DP")
    betaDP <- dp[one..p]
    nu <- if(family =="ST") dp[length(dp)] else 1
    predictorDP <- drop(X[, one..p, drop = FALSE] %*% betaDP)
    if(!is.null(offset))  predictorDP <- predictorDP + offset
    Vdp <- vcov(object, param.type="DP")[one..p,one..p]
    var.pred <- rowSums((X %*% Vdp) * X)
    cp.type <- if(nu>4) "proper" else "pseudo"
    cp <- st.dp2cp(dp, cp.type=cp.type)
    for(j in 1:npt) {
      if(!is.na(var.pred[j])) {
        r <- sqrt(cp[p+1]^2/(cp[p+1]^2 +var.pred[j]))
        cp.pred <- c(cp[one..p], cp[p+1]/r, cp[p+2]*r^3, cp[p+3]*r^4)  
        dp.pred <- st.cp2dp(cp.pred, cp.type, silent=TRUE, tol=1e-4, start=dp)
        dp.pred <- c(0, dp.pred[-one..p])
        q <- if(!anyNA(dp.pred)) qst(probs, dp=dp.pred) else rep(NA,2)
        }
      else q <- rep(NA,2)
      lwr[j] <- predictorDP[j] + q[1]
      upr[j] <- predictorDP[j] + q[2]
      } }  
  out <- cbind(predictor, lwr, upr)
  colnames(out) <- c("fit", "lwr", "upr")
  }
  out
}
 
confint.selm <- function(object, parm, level=0.95, param.type, tol=1e-3, ...) 
{
  family <- slot(object, "family")
  object.name <- as.character(deparse(substitute(object)))
  if(missing(param.type)) {
    if(family=="ST") {
      nu <- slot(object,"param")$dp["nu"]
      if(is.na(nu) | is.null(nu)) nu <- slot(object, "param")$fixed$nu
      ptype <- if(nu>4) "CP" else "pseudo-CP"
      }
    param.type <- switch(family, "SN" = "CP", "ST"=ptype, "SC"="pseudo-CP")
    }
  p <- slot(object, "size")["p"] 
  param <- coef(object, param.type)
  npar <- length(param)
  x.names <- if(p>1)  names(param)[2:p] else NULL
  par.names <- param.names(param.type, family, p, x.names)  
  fixed.comp <- slot(object, "param")$fixed.terms$fixed.comp
  names(param) <- if(is.null(fixed.comp)) par.names else par.names[-fixed.comp] 
  pnames <- names(param)
  if(missing(parm)) 
    {par.comp <- (1:npar); parm <- pnames}
  else {if(is.numeric(parm)) {par.comp <- parm; parm <- pnames[parm]} else
    par.comp <- match(parm, pnames)}
  if(slot(object, "param")$boundary) 
    stop("parameter estimates on the boundary of the parameter space")
  namesCP <- c("(Intercept.CP)", "s.d.", "gamma1", "gamma2")
  namesDP <- c("(Intercept.DP)", "omega", "alpha", "nu")
 
  if(param.type=="DP" & length(intersect(parm, namesCP))>0 )
    stop("incompatible 'parm' and 'param.type'")
  if(param.type=="CP" & length(intersect(parm, namesDP))>0 )
    stop("incompatible 'parm' and 'param.type'")
  if(family=="SN" & param.type=="pseudo-CP")   
     stop("'param.type' incompatible with 'SN' family object")
  lev2 <- (1 - level)/2
  lev2 <- c(lev2, 1 - lev2)
  intervals <- matrix(0, length(parm), 2,
    dimnames=list(parm,  paste(as.character(lev2*100), "%", sep="")))
  max.logL <- slot(object, "logL")
  if(family=="SN") {
    slant <- intersect(c("alpha", "gamma1"), parm)
    # check.alpha <- (length(slant) > 0 | param.type=="DP" & (1 %in% par.comp)) 
    if(length(slant) > 0) {
      alpha.interv <- slot(object, "param")$alpha.interv
      if(is.null(alpha.interv) | length(which(alpha.interv[,1]==level))==0) {
        q <- qchisq(level, 1)
        alpha.mle <- alpha.sx <- alpha.dx <- coef(object, "DP")["alpha"]
        fn.alpha <- function(alpha)  (max.logL - q/2 -
            profile.selm(object, "DP", "alpha", alpha, plot.it=FALSE)$logL) 
        step <- 1
        repeat {
          alpha.sx <- alpha.sx - step
          if(fn.alpha(alpha.sx) > 0) break
          step <- 2*step
        }
        alpha.sx <- uniroot(fn.alpha, c(alpha.sx, alpha.mle), tol=tol)$root 
        step <- 1
        repeat {
          alpha.dx <- alpha.dx + step
          if(fn.alpha(alpha.dx) > 0) break
          step <- 2*step
        }
        alpha.dx <- uniroot(fn.alpha, c(alpha.mle, alpha.dx), tol=tol)$root
        alpha.interv <- rbind(alpha.interv, c(level, alpha.sx, alpha.dx))
        slot(object, "param")$alpha.interv <- alpha.interv
        # assign(object.name, object, pos=".GlobalEnv")
      } else { 
        k <- min(which(alpha.interv[,1] == level))
        alpha.sx <- alpha.interv[k,2]
        alpha.dx <- alpha.interv[k,3]
      }
      gamma1.sx <- dp2cpUv(c(0, 1, alpha.sx), "SN")[3]
      gamma1.dx <- dp2cpUv(c(0, 1, alpha.dx), "SN")[3]
      intervals[slant,] <- if(param.type == "DP")  
        c(alpha.sx, alpha.dx) else c(gamma1.sx, gamma1.dx)
      }   
    e <- rep(1, npar)
    e[p+1] <- 1/param[p+1]
    # v <- diag(e) %*% vcov(object, param.type) %*% diag(e)
    vcov <- slot(object, "param.var")[[tolower(param.type)]]
    v <- diag(e) %*% vcov %*% diag(e)              # avoid vcov() method
    drop.last <- 1:(p+1)
    se <- sqrt(diag(v))[drop.last]
    if(param.type=="DP" & (prod(intervals[slant,]) < 0)) se[1]<- NA
    par0 <- param[drop.last]
    par0[p+1] <- log(par0[p+1])
    interv <- par0 + outer(se[drop.last], qnorm(lev2))
    interv[p+1,] <- exp(interv[p+1,])
    if(length(slant) == 0) intervals[1:length(parm),] <- interv[par.comp,] 
      else { if(length(par.comp) > 1)
       intervals[1:(length(parm)-1),] <- interv[par.comp[-length(par.comp)],]}
    }
  if(family %in% c("ST", "SC")) {
    par0 <- param
    fixed.comp <- slot(object, "param")$fixed.terms$fixed.comp
    free.comp <- setdiff(1:(p+3), fixed.comp)
    positive.comp <- intersect(p + c(1,3) , free.comp)
    free.pos <- which(free.comp %in% positive.comp)
    par0[free.pos] <- log(par0[free.pos]) # log scale & tailweight
    e <- rep(1, length(param))
    e[free.pos] <- 1/param[free.pos]
    # v <- diag(e) %*% vcov(object, param.type) %*% diag(e)   
    vcov <- slot(object, "param.var")[[tolower(param.type)]]
    v <- diag(e) %*% vcov  %*% diag(e)              # avoid vcov() method
    se <- sqrt(diag(v))
    interv <- par0 + outer(se, qnorm(lev2))
    interv[free.pos,] <- exp(interv[free.pos,])
    intervals[,] <- interv[par.comp,]
    }
  intervals[,,drop=FALSE]
}
#--------------------
# Feb.2017
#
dSymmModulated <- function(x, xi=0, omega=1, f0, G0, w, par.f0, par.G0, 
  odd="check", log=FALSE, ...) 
{# density of univariate modulated-symmetry distributions, Feb.2017
  dsbeta <- function(x, shape, log) {
     u <- dbeta((x+1)/2, shape, shape, log=log)
     if(log) u-logb(2) else u/2
     }
  psbeta <- function(x, shape, log.p) pbeta((x+1)/2, shape, shape, log.p=log.p)
  dsunif <- function(x, log) dunif(x, -1, 1, log=log)
  psunif <- function(x, log.p) punif(x, -1, 1, log.p=log.p)
  if(omega <= 0) stop("omega must be positive")
  z <- as.numeric((x-xi)/omega)
  f0 <- switch(f0, "norm"="normal", "logis"="logistic", f0)
  pdf <- switch(f0, 
    beta=dsbeta(z, par.f0, log=log), cauchy=dcauchy(z, log=log),
    logistic=dlogis(z, log=log), normal=dnorm(z, log=log),  
    t=dt(z, par.f0, log=log), uniform=dsunif(z, log=log), NULL)     
  if(is.null(pdf)) stop("unsupported 'f0' density")
  odd <- match.arg(odd, c("check", "assume", "force"))
  w.z <- w(z, ...)
  if(odd == "check") { 
    if(!isTRUE(all.equal(-w.z, w(-z, ...))) || w(0,...) != 0) 
    stop("function 'w' is not odd")  } 
  if(odd == "force") {
    w.z[z < 0] <-  -w(-z[z<0], ...)
    w.z[z == 0] <- 0
    }
  G0 <- switch(G0, "norm"="normal", "logis"="logistic", G0)  
  cdf <- switch(G0, 
    beta=psbeta(w.z, par.G0, log.p=log), cauchy=pcauchy(w.z, log.p=log),
    logistic=plogis(w.z, log.p=log),  normal=pnorm(w.z, log.p=log),
    t=pt(w.z, par.G0, log.p=log), uniform=psunif(w.z, log.p=log), NULL)
  if(is.null(cdf)) stop("unsupported 'G0' distribution") 
  if(log) (pdf + cdf + logb(2/omega)) else (2 * pdf * cdf/omega)
}
#----
rSymmModulated <- function(n=1, xi=0, omega=1, f0, G0, w, par.f0, par.G0, 
  odd="check", ...) 
{# random numbers from modulated-symmetry distributions, use (1.11a) of SN book
  rsbeta <- function(n=1, shape) rbeta(n, shape, shape)*2 + 1
  rsunif <- function(n=1) runif(n, -1, 1)
  if(omega < 0) stop("omega must be non-negative")
  f0 <- switch(f0, "norm"="normal", "logis"="logistic", f0)
  Z0 <- switch(f0, beta=rsbeta(n, par.f0), cauchy=rcauchy(n),
           logistic=rlogis(n), normal=rnorm(n),  
           t=rt(n, par.f0), uniform=rsunif(n), NULL)     
  if(is.null(Z0)) stop("unsupported 'f0' density")
  odd <- match.arg(odd, c("check", "assume", "force"))
  w.Z0 <- w(Z0, ...)
  if(odd == "check") { 
    if(!isTRUE(all.equal(-w.Z0, w(-Z0, ...))) || w(0,...) != 0)
    stop("function 'w' is not odd")  } 
  if(odd == "force")  {
    w.Z0 <- ifelse(Z0>0, w(Z0, ...), -w(-Z0, ...))  
    w.Z0[Z0 == 0] <- 0 }
  G0 <- switch(G0, "norm"="normal", "logis"="logistic", G0)   
  T <- switch(G0, beta=rsbeta(n, par.G0), cauchy=rcauchy(n),
           logistic=rlogis(n),  normal=rnorm(n),
           t=rt(n, par.G0), uniform=rsunif(n), NULL)
  if(is.null(T)) stop("unsupported 'G0' distribution")
  as.numeric(xi + omega*Z0*sign(w.Z0-T))
}
#
dmSymmModulated <- function(x, xi, Omega, f0, G0, w, par.f0, par.G0, 
  odd="check", log=FALSE, ...) 
{# density of multivariate modulated-symmetry distributions, Feb.2017
  psbeta <- function(x, shape) pbeta((x+1)/2, shape, shape)
  psunif <- function(x) punif(x, -1, 1)
  if(!is.matrix(Omega)) stop("Omega must be a matrix")
  d <- ncol(Omega)
  x <- matrix(as.vector(x), ncol=d)
  zero <- rep(0, d)
  omega <- sqrt(diag(Omega))
  Omega <- cov2cor(Omega)
  z <- (x - outer(rep(1,nrow(x)), xi)) %*%  diag(1/omega, d, d)
  f0 <- switch(f0, "norm"="normal", f0)
  pdf <- switch(f0, cauchy=mnormt::dmt(z, zero, Omega, 1, log=log), 
    normal=mnormt::dmnorm(z, zero, Omega, log=log), 
    t=mnormt::dmt(z, zero, Omega, par.f0, log=log), NULL)     
  if(is.null(pdf)) stop("unsupported 'f0' density")
  odd <- match.arg(odd, c("check", "assume", "force"))
  w.z <- w(z, ...)
  if(odd == "check") { 
    if(!isTRUE(all.equal(-w.z, w(-z, ...))) || w(matrix(zero, 1, d), ...) != 0)
    stop("function 'w' is not odd")  } 
  if(odd == "force") {
    neg <- (z[,1] < 0) 
    w.z[neg] <- -w(-z[neg,], ...)
    i0 <- apply(z, 1, all.equal, current=zero, check.attr=FALSE) == "TRUE"
    w.z[i0] <- 0
    }
  G0 <- switch(G0, "norm"="normal", "logis"="logistic", G0)   
  cdf <- switch(G0, 
    beta=psbeta(w.z, par.G0, log.p=log), cauchy=pcauchy(w.z, log.p=log),
    logistic=plogis(w.z, log.p=log),  normal=pnorm(w.z, log.p=log),
    t=pt(w.z, par.G0, log.p=log), uniform=psunif(w.z, log.p=log), NULL)
  if(is.null(cdf)) stop("unsupported 'G0' distribution") 
  logDet <- sum(log(omega))
  if(log) as.vector(pdf + cdf + logb(2) - logDet) else  
    as.vector(2 * pdf * cdf)/exp(logDet)
}
#----
rmSymmModulated <- function(n=1, xi, Omega, f0, G0, w, par.f0, par.G0, odd="check", ...)
{# random numbers from modulated-symmetry distributions, use (1.11a) of SN book
  rsbeta <- function(n=1, shape) rbeta(n, shape, shape)*2 + 1
  rsunif <- function(n=1) runif(n, -1, 1)
  if(!is.matrix(Omega)) stop("Omega must be a matrix")
  d <- ncol(Omega)
  zero <- rep(0, d)
  omega <- sqrt(diag(Omega))
  Omega <- cov2cor(Omega)
  f0 <- switch(f0, "norm"="normal", f0)
  Z0 <- switch(f0, cauchy=mnormt::rmt(n, zero, Omega, 1), 
          normal=mnormt::rmnorm(n, zero, Omega), 
          t=mnormt::rmt(n, zero, Omega, par.f0),  NULL)     
  if(is.null(Z0)) stop("unsupported 'f0' density")
  odd <- match.arg(odd, c("check", "assume", "force"))
  w.Z0 <- w(Z0, ...)
  if(odd == "check") { 
    if(!isTRUE(all.equal(-w.Z0, w(-Z0, ...))) || w(matrix(zero,1,d) ,...) != 0)
    stop("function 'w' is not odd")}
  if(odd == "force") {
    neg <- (Z0[,1] < 0) 
    w.Z0[neg] <- -w(-Z0[neg,], ...)
    i0 <- apply(Z0, 1, all.equal, current=zero, check.attr=FALSE) == "TRUE"
    w.Z0[i0] <- 0
    } 
  G0 <- switch(G0, "norm"="normal", "logis"="logistic", G0)     
  T <- switch(G0, beta=rsbeta(n, par.G0), cauchy=rcauchy(n),
         logistic=rlogis(n),  normal=rnorm(n),
         t=rt(n, par.G0), uniform=rsunif(n), NULL)
  if(is.null(T)) stop("unsupported 'G0' distribution") 
  drop(outer(rep(1,n), xi) + drop(sign(w.Z0-T)) * Z0 %*% diag(omega))
}

plot2D.SymmModulated <- function(range, npt=rep(101,2), xi=c(0,0), Omega, f0, 
  G0, w, par.f0, par.G0, odd="check", ...)
{
  if(ncol(Omega)!=2 || nrow(Omega) != 2 || length(xi) !=2) 
    stop("Wrong dimension(s) of xi and/or Omega")
  n1 <- npt[1]
  n2 <- npt[2]
  x1 <- seq(min(range[,1]), max(range[,1]), length=n1)
  x2 <- seq(min(range[,2]), max(range[,2]), length=n2)
  x1.x2 <- cbind(rep(x1, n2), as.vector(matrix(x2, n1, n2, byrow=TRUE)))
  X <- matrix(x1.x2, n1 * n2, 2, byrow = FALSE)
  dots <- list(...)
  nw <- names(formals(w))[-1]
  if(missing(par.f0)) par.f0 <- NULL
  if(missing(par.G0)) par.G0 <- NULL
  pdf <- do.call(dmSymmModulated, c(list(x=X, xi=xi, Omega=Omega, f0=f0, 
    G0=G0, w=w, par.f0=par.f0, par.G0=par.G0, odd=odd, log=FALSE), dots[nw])) 
  pdf <- matrix(pdf, n1, n2)
  dots[nw] <- NULL
  do.call(contour, c(list(x=x1, y=x2, z=pdf), dots))
  invisible(list(x=x1, y=x2, pdf=pdf))
  }
#----
# functions added in v.1.6-0

fournum <- function(x, na.rm = TRUE, ...) 
{
  x <- as.vector(x)
  if(!is.numeric(x)) stop("x must be a numeric vector") 
  na <- is.na(x)
  if (any(na)) {if (na.rm)  x <- x[!na]  else x <- NULL }
  if (length(x) < 8)  m <- rep.int(NA, 4)
  else {
    oct <- quantile(x, probs=(1:7)/8, ...)
    q.deviation <- (oct[6]-oct[2])/2   # terminology from ESS2, vol.10, p.6743
    GaltonBowley <- (oct[6]-2*oct[4]+oct[2])/(oct[6]-oct[2])
    Moors <- (oct[7]-oct[5]+oct[3]-oct[1])/(oct[6]-oct[2]) 
    m <- c(oct[4], q.deviation, GaltonBowley, Moors)
    }
  names(m) <- c("median", "q.deviation", "GaltonBowley", "Moors")
  return(m)
}
#---------
galton_moors2alpha_nu <- 
  function(galton, moors, quick=TRUE, move.in=TRUE, verbose=0, abstol=1e-4) 
{# given (galton, moors) values, finds matching ST parameters (alpha, nu)
  deltaV <- c(seq(0, 0.9, by=0.1), 0.95, 0.99, 1)
  npt1 <- length(deltaV)
  nuV <- c(0.3, 0.32, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 
           1.5, 2, 3, 4, 5, 7, 10, 15, 20, 30, 40, 50, 100, Inf)
  npt2 <- length(nuV)     
  log.nuV <- log(nuV)
  moors0 <- c( # Moors values at alpha=0, from moorsM[1,]:
    9.9456, 8.5883, 7.1096, 5.5251, 4.5430, 3.8879, 3.0876, 2.6296, 
	2.3393, 2.1417, 2.0000, 1.6522, 1.5167, 1.4033, 1.3542, 1.3269, 
	1.2977, 1.2771, 1.2618, 1.2544, 1.2471, 1.2436, 1.2414, 1.2372, 1.2331)
  galtonInf <- c(# Galton-Bowley values at nu=Inf, from galtonM[,npt2]
    0, 2.4746e-05, 2.0388e-04, 7.2391e-04, 1.8496e-03, 4.0097e-03, 7.9865e-03, 
    1.5413e-02, 3.0388e-02, 6.6491e-02, 0.10594, 0.14343, 0.144292171045)
  moorsInf <- c(# Moors values at nu=Inf, from moorsM[,npt2]
    1.2331, 1.2331, 1.2331, 1.2332, 1.2333, 1.2338, 1.2347, 1.2367, 
    1.2408, 1.2462, 1.2375, 1.1889, 1.1764)  
  approx.invNu <- splinefun(moors0, 1/nuV, method="hyman")
  bound.GB <- c(0.84423, 0.82327, 0.79244, 0.74352, 0.69838, 0.65727, 0.58661, 
    0.52943, 0.48311, 0.44533, 0.41421, 0.31849, 0.27109, 0.22551, 0.20376, 
    0.19113, 0.17712, 0.16694, 0.15921, 0.15541, 0.15166, 0.14980, 0.14869, 
    0.14648, 0.14429)
  bound.Moors <- c(10.0810, 8.7251, 7.2457, 5.6544, 4.6611, 3.9927, 3.1645, 
    2.6812, 2.3698, 2.1553, 2.0000, 1.6161, 1.4677, 1.3464, 1.2953, 1.2676, 
    1.2384, 1.2182, 1.2035, 1.1964, 1.1896, 1.1862, 1.1842, 1.1803, 1.1764)
  min.GB <- min(bound.GB)  
  boundary1 <- splinefun(bound.GB, bound.Moors, method="hyman")
  boundary0 <- approxfun(galtonInf, moorsInf)
  boundary <- function(x, deriv = 0L)
    ifelse(x < min.GB, boundary0(x),  boundary1(x, deriv))
  eta <- matrix(c(
       2.213831, -0.315418,  -0.007641,
       2.022665, -0.240821,  -0.012001,
       1.790767, -0.164193,  -0.021492,
       1.506418, -0.090251,  -0.047034,
       1.305070, -0.050702,  -0.087117,
       1.156260, -0.028013,  -0.143526,
       0.952435, -0.005513,  -0.307509,
       0.819371,  0.004209,  -0.536039,
       0.724816,  0.008992,  -0.818739,
       0.653206,  0.011596,  -1.142667,
       0.596276,  0.013136,  -1.495125,
       0.417375,  0.015798,  -3.365100,
       0.314104,  0.016371,  -5.011929,
       0.192531,  0.016274,  -7.304089,
       0.123531,  0.015682,  -8.676470,
       0.080123,  0.014987,  -9.546498,
       0.030605,  0.013674, -10.561206,
      -0.003627,  0.012113, -11.335506,
      -0.024611,  0.010334, -11.977601,
      -0.030903,  0.009149, -12.343369,
      -0.031385,  0.007650, -12.789281,
      -0.027677,  0.006721, -13.074983,
      -0.023285,  0.006079, -13.284029,
      -0.005288,  0.004478, -13.874691
	  ),
      nrow=npt2-1, ncol=3, byrow=TRUE)
  invert.GM <- function(galton, moors, alpha, log.nu, verbose=0, abstol=1e-4) {
    # invert (galton, moors) starting from initial (alpha, log.nu)
    if(galton*alpha < 0) stop("unfeasible initial alpha") 
    loss.GM <- function(param, galton, moors, verbose=0) {
      if(verbose > 2) cat("param:", param)
      oct <- qst((1:7)/8, 0, 1, param[1], exp(param[2]), tol=abstol)
      g <- as.numeric((oct[6]-2*oct[4]+oct[2])/(oct[6]-oct[2]))       
      m <- as.numeric((oct[7]-oct[5]+oct[3]-oct[1])/(oct[6]-oct[2]))
      loss <- sqrt(64*(g-galton)^2 + (m-moors)^2)
      if(verbose > 2) cat(" loss:", loss, "\n")
      loss
      }
    optim(c(alpha,log.nu), loss.GM, galton=galton, moors=moors, verbose=verbose,
      method="Nelder-Mead", control=list(abstol=abstol, maxit=200))
    } 
  if(moors < 0) stop("moors < 0 is not admissible")  
  abs.galton <- abs(galton)      
  note <- NULL
  feasible <- ( (moors > boundary(abs.galton)) & (abs.galton < 1) )
  if(!feasible) { 
    if(!move.in) return(c(NA,NA))  
    if(verbose > 0) message("unfeasible (galton, moors) reset to feasible area") 
    if(abs.galton >= 1) {# note: GaltonBowley=1 for alpha=Inf, nu-->0
      galton.new <- sign(galton)*0.95
      if(verbose > 0) message(paste("'galton' reset to:", format(galton.new)))
      return(galton_moors2alpha_nu(galton.new, moors, quick, move.in, verbose))
    }
    dist <- sqrt(64*(abs.galton - bound.GB)^2 + (moors - bound.Moors)^2)
    k <- which(dist == min(dist))
    galton.new <- sign(galton)* 0.95 * bound.GB[k]
    moors.new <- if(k < length(dist)) 1.05*bound.Moors[k] 
                 else moors.new <- max(moorsInf) + 0.01
    note <- paste("(galton, moors) reset to:", format(galton.new), ",",
        format(moors.new))
    if(verbose > 0) cat("[galton_moors2alpha_nu]", note)  
    out <- galton_moors2alpha_nu(galton.new, moors.new, quick, move.in, verbose)
    attr(out, "note") <- paste("unfeasible input values,", note)
    return(out)
    }
  log.nu <- if(moors > min(moors0)) log(1/approx.invNu(moors)) else Inf
  if(abs(galton) < (.Machine$double.eps)^(1/4) ) alpha <- 0 
    else {
	  pos <- (log.nu >= log.nuV)
	  if(all(pos) | all(!pos)) {
		 # message("all(pos) | all(!pos)")
		 eta.f <- if(all(pos)) eta[npt2-1, ] else eta[1, ]
		 # browser()
		} else { 
		 k <- max(which(pos)) 
		 f <- (log.nu-log.nuV[k])/(log.nuV[k+1] + log.nuV[k])
		 eta.f <- if( k < (npt2-1)) (1-f)*eta[k,] + f*eta[k+1,] else eta[k,]
		 }
     x <- log(abs(galton))
	 alpha <- as.numeric(sign(galton)) * exp(sum(eta.f * c(x, x^3, 1/x^3)))
	 }   
  out <- c(alpha=alpha, nu=exp(log.nu))
  attr(out, "method") <- "quick match"
  if(verbose > 0) cat("[galton_moors2alpha_nu] quick match:", format(out), "\n")
  if(quick) return(out)  
  log.nu <- min(log.nu, 5)  # avoid huge log.nu at start, especially Inf 
  if(verbose > 1) 
    cat("[galton_moors2alpha_nu] second step of (GaltonBowley, Moors) inversion")
  opt <- invert.GM(abs.galton, moors, abs(alpha), log.nu, verbose, abstol)
  if(verbose > 1) { 
    cat("[galton_moors2alpha_nu] outcome from invert.GM")
    cat("opt$(message, convergence, par, value):")
    cat(opt$message,", ")
    cat(opt$convergence,", ")
    cat("(", opt$par,"), ")
    cat(opt$value,"\n")
    # browser()
    }
  out <- c(alpha=as.numeric(sign(galton)*opt$par[1]), nu=exp(opt$par[2]))  
  attr(out, "method") <- "two-step match"
  return(out)
  }
#---------
galton2alpha <- function(galton, move.in=TRUE) {
  max.GB <- 0.144292171 # 0.144292171045
  deltaV <- c(seq(0, 0.9, by=0.1), 0.95, 0.99, 0.99999)
  alphaV <- deltaV/sqrt(1-deltaV^2) 
  galtonV <- c(# Galton-Bowley values for SN distributions
    0, 2.4746e-05, 2.0388e-04, 7.2391e-04, 1.8496e-03, 4.0097e-03, 7.9865e-03, 
    1.5413e-02, 3.0388e-02, 6.6491e-02, 0.10594, 0.14343, max.GB)
  interp.alpha <- splinefun(galtonV, alphaV, method="hyman")
  alpha0 <- if(abs(galton) < max.GB) interp.alpha(abs(galton)) 
        else { if(move.in) 10 else Inf}
  alpha <- sign(galton) * alpha0
  return(alpha)
}  
#---------
st.prelimFit <- function(x, y, w, quick=TRUE, verbose=0, max.nu=30, SN=FALSE)
{# inserted in version 1.6-0 (2020-03-28); updated in v.2.1.0
  y <- c(y)
  n <- length(y)
  if(missing(x)) x <- rep(1, n)
  x <- data.matrix(x)
  p <- ncol(x) 
  if(n != nrow(x)) stop("dimension mismatch of x,y")
  if(any(x[,1] != 1)) stop("x[,1] not all 1's") 
  if(missing(w)) w <- rep(1, n)
  if(n != length(w)) stop("dimension mismatch of w,y")
  if(p==1) {
    beta <- stats::median(rep(y, w), na.rm=TRUE)   
    resid <- (y-beta)
    } else { 
    beta.fit <- quantreg::rq.wfit(x, y, tau=0.5, weights=w, method="br")
    beta <- coef(beta.fit)
    resid <- c(residuals(beta.fit))
    }
  q.measures <- fournum(rep(resid, w))  
  if(is.null(quick)) {
    alpha <- 0
    nu <- 10
    }
  else {
    galton <- q.measures[3]
    moors <- q.measures[4]
    if(SN) {
        alpha <- galton2alpha(galton, move.in=TRUE)
        nu <- Inf
      } else {
        alpha_nu <- galton_moors2alpha_nu(galton, moors, quick=quick, 
                    move.in=TRUE, verbose=verbose, abstol=1e-4)     
        alpha <- alpha_nu[1]
        nu    <- min(alpha_nu[2], max.nu)  
      }  
    }   
  if(verbose > 0) cat("[st.prelimFit] c(alpha, nu) = ", alpha, nu, "\n")      
  omega <- 2 * q.measures[2]/diff(qst(c(0.25, 0.75), 0, 1, alpha, nu))
  shift <- qst(0.5, 0, omega, alpha, nu)
  beta[1] <- beta[1] - shift
  resid <- resid + shift
  dp <- c(beta, omega, alpha, nu)
  names.x <- colnames(x) 
  if(is.null(names.x)) names.x <- paste("x", 1:p, sep=".")
  if(p == 1)  names.x <- "xi"   
  names(dp) <- c(names.x, "omega", "alpha", "nu")
  logL <- sum(dst(resid, 0, omega, alpha, nu, log=TRUE))
  if(SN) dp <- dp[-length(dp)]
  if(verbose > 1) cat("[st.prelimFit] c(dp, logL) = ", dp, logL, "\n") 
  return(list(dp=dp, residuals=resid, logLik=logL))
}
#----
mst.prelimFit <- function(x, y, w, quick=TRUE, verbose=0, max.nu=30, SN=FALSE) 
{# inserted in version 1.6-0 (2020-03-28), updated in version 2.1.0
  matchMedian <- function(omega.bar, nu, obs.median) {
    if(any(abs(omega.bar) >= 1)) return(NA)
    pprodt2(obs.median, omega.bar, nu) - 0.5
    }  
  y <- data.matrix(y)  
  d <- ncol(y)
  n <- nrow(y)
  if(missing(x)) x <- matrix(1, n, 1)
  if(missing(w)) w <- rep(1, n)
  p <- ncol(x)
  dp.marg <- matrix(NA, p+3, d)
  z <- matrix(NA, n, d)
  for(j in 1:d)  {
    fit <- st.prelimFit(x, y=y[,j], w, quick, verbose, max.nu, SN=SN)
    dp.marg[,j] <- fit$dp
    z[,j] <- fit$residuals/dp.marg[p+1,j]
    } 
  omega <- as.vector(dp.marg[p+1,])  
  lambda <- c(dp.marg[p+2,])
  delta <- lambda/sqrt(1 + lambda^2)  
  nu <- median(dp.marg[p+3,])
  # wd <- max(5, 1000/(nu + (.Machine$double.eps)^0.25))
  if(d > 1) { 
    Omega.bar <- diag(d)
    for(j in 1:(d-1)) for(k in (j+1):d) {
	  w <- as.vector(z[,j] * z[,k])
	  w. <- median(w)
	  rho.max <- 0.999999
	  nu.work <- nu
	  repeat{
	    f1 <-  matchMedian(-rho.max, nu.work, w.) 
	    f2 <-  matchMedian(rho.max, nu.work, w.) 
	    if(f1*f2 < 0) break
	    nu.work <- 0.9 *nu.work
	    }
	  r <- uniroot(matchMedian, interval=c(-rho.max, rho.max), nu=nu.work,
				 obs.median=w.)
	  Omega.bar[j,k] <- Omega.bar[k,j]  <- r$root  
    }
    Omega.star <- rbind(cbind(Omega.bar, delta), c(delta, 1))
    k <- 0
    repeat {
	  m <- mnormt::pd.solve(Omega.star, silent=TRUE)
	  if(!is.null(m)) break
	  k <- k+1
	  Omega.star <-  0.95 * Omega.star
	  Omega.star[cbind(1:(d+1),1:(d+1))] <- 1
	  }
    Omega <- diag(omega, d) %*% Omega.star[1:d,1:d] %*% diag(omega, d)
    Omega <- force.symmetry(Omega)
  } else {# case d=1
    Omega.star <- rbind(c(1, delta), c(delta,1))
    Omega <- matrix(omega^2, 1, 1)
    k <- NA
  }  
  delta <- as.vector(Omega.star[d+1, 1:d]) 
  tmp <- as.vector(solve(Omega.star[1:d,1:d]) %*% delta)
  alpha <- tmp/sqrt(1 - sum(delta*tmp))
  beta <- dp.marg[1:p,]
  logL <- sum(dmst(y, x %*% beta, Omega, alpha, nu, log=TRUE))
  dp.fit <- if(p==1) list(xi=dp.marg[1,], Omega=Omega, alpha=alpha, nu=nu)
    else list(beta=beta, Omega=Omega, alpha=alpha, nu=nu)
  return(list(dp=dp.fit, shrink.steps=k, dp.marginals=dp.marg, logLik=logL))
}
  
#---------------------------------------------------------------------------- 
# from ~aa/SN/ST-various/St-start_MLE/SW/cdf_prod_t2.R
# 2019-01-07
# Function pprodt2 computes CDF of product of components of bivariate Student's
# (central) t variables, via Theorem 1 of Wallgren (1980, JASA, 75, 996-1000).
#
# For nu=2, the results have been checked agains those in Table 2 of
# Nadarajah & Kotz (2006, Math. Proceed. Royal Irish Academy, 106A, 149-162). 
# The results are essentially in agreement, although with some differences,
# typically of order <1%, often around 0.1%. These differences can reasonably 
# be attributed to rounding errors. Notice that their computations involve the
# hypergeometric function, which is notoriously numerically hard to compute.
#------------------  
pprodt2 <- function(x, rho, nu)
{# implements formulae in Theorem 1 of Wallgren (1980, JASA, 75, 996-1000)
  if(abs(rho) >= 1) { warning("abs(rho)<1 required");  return(NaN) }
  if(rho < 0) return(1 - pprodt2(-x, -rho, nu)) # see text following Theorem 1
  sinA <- sqrt(1-rho^2)
  cosA <- rho
  alpha <- atan(-sinA/cosA)    
  A <- atan2(sinA, cosA)
  piQ <- function(theta, A, x, nu) { 
    # see (2.5) of Wallgren (1980)
    z <- nu*sin(theta)*sin(theta+A)
    (z/(x+z))^(nu/2)
    }
  neg <- (x<0)  
  p <- rep(NA, length(x))
  if(sum(neg)>0) {
    # see (2.4) of Wallgren (1980)
    m <- sum(neg)
    pneg <- rep(NA, m)
    for(j in 1:m) pneg[j] <-
      integrate(piQ, alpha, 0,  A=A, x=x[neg][j], nu=nu)$value/pi
    p[neg] <- pneg 
    }
  if(sum(!neg)>0) { 
    # see (2.3) of Wallgren (1980)
    m <- sum(!neg)
    ppos <- rep(NA, m)
    for(j in 1:m) ppos[j] <-
      (1 - integrate(piQ, 0, pi+alpha, A=A, x=x[!neg][j], nu=nu)$value/pi)
    p[!neg] <- ppos
    }
  return(p)
  }
#
qprodt2 <- function(p, rho, nu, tol=1e-5, trace=0)
{
  shiftedCDF <- function(x, prob, rho, nu) pprodt2(x, rho, nu) - prob
  m <- length(p)
  q <- rep(NA, m)
  if(nu <= 0) stop("nu>0 required")
  w <- max(5, 20/(nu^2 + sqrt(.Machine$double.eps)))
  for(j in 1:m) {
    if(p[j] == 0) q[j] <- -Inf
    else if(p[j] == 1) q[j] <- Inf
    else if(p[j] < 0 | p[j] >1) q[j] <- NaN
    else if(is.na(p[j])) q[j] <- NA
    else {
      r <- uniroot(shiftedCDF, interval=c(-w, w), prob=p[j], rho=rho, nu=nu,
             extendInt="yes", tol=tol, trace=trace)
      q[j] <- r$root         
    }}
  return(q)    
}  
# 
pprodn2 <- function(x, rho)
{# central case of Theorem 1 of Aroian et al. (1978, Comm.Stat A, 7, 165-172)
  if(abs(rho) >= 1) {warning("condition abs(rho)<1 fails"); return(NaN)}
  if(rho < 0) return(1 - pprodn2(-x, -rho)) 
  fn.Phi <- function(t, y, rho) {
    cr2 <- 1-rho^2
    G2 <- (1+cr2*t^2)^2 + (2*rho*t)^2
    G <- sqrt(G2)
    I <- 1 + cr2*t^2   
    u <- (sqrt((G+I)/2) *sin(t*y) - sqrt((G-I)/2)*cos(t*y))
    return(u/(t*G))
    }
  m <- length(x)
  p <- numeric(m)
  for (j in 1:m){ 
    int <- integrate(fn.Phi, 0, Inf, y=x[j], rho=rho, subdivisions=1000)
    p[j] <- 0.5 + int$value/pi
    }
  return(p)
}
#----------------------------------------------------------------------------
# 2022-07-21, introduce fitdistr.grouped and related methods
fitdistr.grouped <- function (breaks, counts, family, weights, 
   trace = FALSE, wpar = NULL) 
{
  if(!missing(weights)) {if(missing(counts)) counts <- weights else 
    stop("you cannot set both counts and weights")} # (counts = weights)  
  nf <- length(counts)
  if(any(counts < 0)) stop("negative counts")
  if(any(counts != round(counts))) stop("non-integer counts")
  if(any(is.na(c(breaks, counts)))) stop("NAs in breaks or counts")
  if(any(diff(breaks) <= 0)) stop("'breaks' not in increasing order") 
  if(length(breaks) != (nf+1)) stop('mismatch of the input vector sizes')
  if(tolower(family) == "gaussian") family <- "normal"
  fam.rv <- c("normal", "logistic", "t", "Cauchy", "SN", "ST", "SC") # real-valued families  
  fam.pv <- c("gamma", "Weibull")  # positive-valued families
  fam <- c(fam.rv, fam.pv)
  fam.funct <-  c("norm", "logis", "t", "cauchy", "sn", "st", "sc", "gamma", "weibull")
  family <- match.arg(family, fam, several.ok=FALSE)
  if((family %in% fam.pv) & any(breaks < 0)) stop('negative breaks')
  fam.npar <- c(2, 2, 3, 2, 3, 4, 3, 2, 2)
  which.fam <- which(family==fam)[1]
  family.bn <- fam.funct[which.fam]  # family function basename
  npar <- fam.npar[which.fam]
  pos <- # TRUE for intrinsically-positive parameter components  
         if(family %in% fam.pv) rep(TRUE, npar)  
            else {if(family=='t') c(FALSE, TRUE, TRUE) 
              else c(FALSE, TRUE, FALSE, TRUE)[1:npar]}
  br <- breaks
  width <- diff(br)
  if(is.infinite(breaks[1]) | is.infinite(breaks[nf+1])) {
    br <- c(br[2] - 3*width[2], br[2:nf], br[nf] + 3*width[nf-1])
    if(family %in% fam.pv) br[1] <- max(br[1], 0)
    }
  if(is.null(wpar)) {
    # midpts <- (br[-1]+ br[-(nf+1)])/2
    spread.x <- spread.grouped(br, counts, "centre")
    if(trace) cat("[fitdistr.grouped] obtaining initial working parameters:\n")
    if(family %in% c("SN", "ST", "SC")) {
      fit <- st.prelimFit(y=spread.x, max.nu=20, verbose=2*as.numeric(trace))
      dp <- fit$dp
      wpar <-  c(dp[1], log(dp[2]), dp[3])
      if(family=="ST") wpar <- c(wpar, log(dp[4])) 
      }
    else {
      m <- mean(spread.x)
      s <- sd(spread.x)
      wpar <- switch(family,
        "normal"= c(m, log(s)),
        "logistic"=  c(m, log(sqrt(3)* s/pi)),
        "t" = c(m, log(s*sqrt(5/3)), log(5)),  
        "Cauchy" = {mq <- mqCauchy(spread.x); c(mq[1], log(mq[2]))},
        "gamma" = {a <- (m/s)^2; log(c(a, a/m))},
        "Weibull"= log(mmWeibull(m, s))
        )   
      }  
    if(trace) 
      cat("[fitdistr.grouped] initial working parameters:", format(wpar),"\n")  
    } 
    else {if(length(wpar) != npar) stop("wrong number of 'wpar' components")}
  breaks.full <- breaks  
  counts.full <- counts  
  id.orig <- rep(TRUE, length(counts))
  if((family %in% fam.pv) & (breaks[1] > 0)) { 
    breaks.full <- c(0, breaks)
    counts.full <- c(0, counts)
    id.orig <- c(FALSE, rep(TRUE, length(counts)))
    } 
  if((family %in% fam.rv) & (breaks[1] > -Inf)) { 
    breaks.full <- c(-Inf, breaks)
    counts.full <- c(0, counts)
    id.orig <- c(FALSE, rep(TRUE, length(counts)))
    }
  if(breaks[length(breaks)] < Inf) {
    breaks.full <- c(breaks.full, Inf)
    counts.full <- c(counts.full, 0)
    id.orig <- c(id.orig, FALSE)
    }  # range(breaks.full) now spans the entire support of 'family'  
  if(!(length(breaks.full) > npar)) stop("too few intervals for this family")     
  opt <- optim(wpar, logL.grouped, method="Nelder-Mead",  
     control=list(fnscale=-1), breaks = breaks.full, counts = counts.full,  
     family=family, trace = trace, fitted=FALSE, hessian=TRUE)
  wpar <- opt$par  
  dp <- ifelse(pos, exp(wpar), wpar)
  u <- ifelse(pos, 1/dp, 1)
  names(dp) <- { if(family == "t") c("location", "scale", "df") else 
    formalArgs(paste("d", family.bn, sep=""))[2:(npar+1)] }
  logL <- logL.grouped(wpar, breaks.full, counts.full, family, fitted=TRUE)
  fitted <- attr(logL, "fitted")
  info <- diag(u) %*% (-opt$hessian) %*% diag(u)
  dimnames(info) <- list(names(dp), names(dp))
  v <- try(solve(info))
  vcov <- if(inherits(v, "try-error")) NULL else v
  input <- list(counts=counts, breaks=breaks, family=family, family.bn=family.bn, 
     breaks.plot=br, breaks.full=breaks.full, id.orig=id.orig)
  structure(
    list(call=match.call(), family=family, logL=logL, param=dp, vcov=vcov,  
    fitted=fitted, input=input, opt=opt), class="fitdistr.grouped")
}
#
logL.grouped <- function(wpar, breaks, counts, family, trace = FALSE, fitted=FALSE) 
{
  br <- breaks[-c(1, length(breaks))] # assume outer breaks are support boundaries
  cdf <- switch(family, 
     "normal" = pnorm(br, wpar[1], exp(wpar[2])),
     "logistic" = plogis(br, wpar[1], exp(wpar[2])),
     "t" = pt((br - wpar[1])/exp(wpar[2]), exp(wpar[3])),
     "Cauchy" = pcauchy(br, wpar[1], exp(wpar[2])),
     "SN" = psn(br, wpar[1], exp(wpar[2]), wpar[3]),
     "ST" = pst(br, wpar[1], exp(wpar[2]), wpar[3], exp(wpar[4])),
     "SC" = psc(br, wpar[1], exp(wpar[2]), wpar[3]),
     "gamma" = pgamma(br, exp(wpar[1]), exp(wpar[2])),
     "Weibull" = pweibull(br, exp(wpar[1]), exp(wpar[2]))
     )
  prob <- pmax(diff(c(0, cdf, 1)), 0)
  n <- sum(counts)
  if(any(is.na(prob))) return(NA)
  logL <- try(dmultinom(counts, n, prob, log=TRUE))
  if(inherits(logL, "try-error")) return(NA)
  if (trace) cat("[logL.grouped] (working parameters, logLik):", 
     format(c(wpar, logL)),"\n") 
  if(fitted) attr(logL, "fitted") <- prob * n
  logL
}
#---
coef.fitdistr.grouped <- function(object, ...) object$param
vcov.fitdistr.grouped <- function(object,  ...) object$vcov
logLik.fitdistr.grouped <- function(object, ...) {
  logL <- object$logL 
  attr(logL,"df") <- length(object$param)
  class(logL) <- "logLik"
  return(logL)
  }
  
fitted.fitdistr.grouped <- function(object, full=FALSE, ...) 
  if(full) object$fitted else object$fitted[object$input$id.orig]   
  
summary.fitdistr.grouped <- function(object, cor=FALSE, ...){
  obj.name <- deparse(substitute(object))
  cat(obj.name, "- fitted", object$family, "distribution from grouped data\n")
  param <- coef.fitdistr.grouped(object)
  vcov <- vcov.fitdistr.grouped(object)
  std.err <- sqrt(diag(vcov))
  input <- object$input
  cat("number of observed counts:", length(input$counts), "\n")
  cat("number of full-range intervals:", length(input$breaks.full) -1 , "\n")
  cat("total number of observations:", sum(input$counts), "\n")
  logL <- object$logL
  cat("log-likelihood:", format(logL, nsmall=2), "\n")
  print(cbind(param, std.err, "z-value"=param/std.err))
  if(cor) {cat("correlation matrix of the estimates:\n"); print(cov2cor(vcov))}
  invisible(list(param=param, std.err=std.err, vcov=vcov, logL=logL))
}

print.fitdistr.grouped <- function(x, ...){
  object <- x
  print(object$call)
  # cat("family:", object$family, "\n")
  cat("fitted parameters:", format(object$param, ...), "\n")
  cat("log-likelihood:", format(object$logL, nsmall=2), "\n")
}
#---
plot.fitdistr.grouped <- function(x, freq=FALSE,   
   col="grey90", border="grey80", pdfcol="blue", main, sub=NULL,
   xlab, ylab, xlim, ylim, axes=TRUE, labels=FALSE, ...) {
  x.name <- deparse(substitute(x)) 
  object <- x
  input <- object$input
  breaks <- if(all(is.finite(input$breaks))) input$breaks 
    else {
      warning("Inf(s) in 'breaks' are replaced by constructed values")
      input$breaks.plot }
  widths <- diff(breaks)     
  if(freq & var(widths) > 0 ) 
    stop("Arguments not suitable for plot.histogram; rather use 'freq=FALSE'")  
  width <- if(var(widths) == 0) widths[1] else NA
  dp <- object$param
  if(missing(xlim)) xlim <- range(pretty(breaks))  
  x <- seq(xlim[1], xlim[2], length=201)
  if(missing(main)) main <- paste(
    x.name, ": histogram and fitted", object$family, "family for grouped data")
  if(missing(xlab)) xlab <- "" 
  if(missing(ylab)) ylab <- if(freq) "frequencies" else "density function" 
  pdf <- if(input$family == "t") dt((x - dp[1])/dp[2], dp[3]) 
    else {
      dp.char <- paste(paste("dp[", 1:length(dp), "]", sep=""),  collapse=", ")
      pdf.char <- paste("d", input$family.bn, "(x, ", dp.char, ")", sep="")
      eval(parse(text=pdf.char))
      }
  counts <- input$counts
  n <- sum(counts)
  rel.freq <- counts/(n*widths) 
  if(missing(ylim)) ylim=c(0, max(rel.freq, pdf) * if(freq) n*width else 1)   
  # see graphics:::plot.histogram, hist.default
  r <- structure(list(breaks = breaks, counts = counts, density = rel.freq, 
        mids = NULL, xname = NULL), class = "histogram")
  plot(r, freq=freq, col = col, border = border, 
        angle = NULL, density = NULL, main = main, xlim = xlim, ylim = ylim, 
        xlab = xlab, ylab = ylab, axes = axes, labels = NULL, ...)
  y <- if(freq) n*width*pdf else pdf       
  lines(x, y, col=pdfcol)   
  invisible(list(hist=r, x=x, y=y))
}
#---
mmWeibull <- function(mu, sigma, ...) {
# estimate Weibull parameters with the method of moments 
  fn <- function(a, r2) gamma(1+2/a)/gamma(1+1/a)^2 -1 - r2
  root <- uniroot(fn, interval=c(0.5, 5), extendInt="yes", r2=(sigma/mu)^2, ...)
  a <- root$root
  b <- sigma/sqrt(gamma(1+2/a) - gamma(1+1/a)^2)
  c(shape=a, scale=b)
}
#---
mqCauchy <- function(x, p=0.25) {
# estimate Cauchy parameters from selected quantiles
  tiny <- 1/length(x)
  if((p <= tiny) | (p >= 0.5-tiny)) stop("unfeasible 'p'")
  probs <- c(p, 0.5, 1-p)
  qCauchy <- qcauchy(probs, 0, 1)
  q <- quantile(x, probs)
  s <- (q[3] - q[1])/(qCauchy[3] - qCauchy[1])
  c(q[2], s)
} 
#---
spread.grouped <- function(breaks, counts, shift="centre") {
  if(any(is.na(c(breaks, counts)))) stop("NA in breaks or counts")
  if(any(is.infinite(c(breaks, counts)))) stop("Inf in breaks or counts")
  if(any(counts != round(counts))) stop("non-integer counts")
  n <- length(counts)
  if(length(breaks) != (n+1)) stop("incompatible size of (breaks, counts)")
  shift <- match.arg(shift, c("left", "centre", "right"), several.ok=FALSE)
  step <- switch(shift, "left" = 0, "centre" = 0.5, "right" = 1)
  width <- diff(breaks)
  if(any(width <= 0)) stop("breaks not (strictly) increasing")
  x <- NULL
  for(j in 1:n) {
    x.j <- breaks[j]+ (seq_len(counts[j]) - 1 + step)*width[j]/counts[j]
    x <- c(x, x.j)
    }
  return(x)
}

Try the sn package in your browser

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

sn documentation built on April 5, 2023, 5:15 p.m.