"rqss.fit" <-
function (x, y, tau = 0.5, method = "sfn", rhs = NULL, control, ...)
{
    if(is.null(rhs)) rhs <- (1 - tau) * t(x) %*% rep(1,length(y))
    else tau <- 0.5
    fit <- switch(method,
	sfn = rq.fit.sfn(x, y, tau = tau,  rhs = rhs, control = control, ...),
	lasso = rq.fit.sfn(x, y, tau = tau,  rhs = rhs, control = control, ...),
        sfnc = rq.fit.sfnc(x, y, tau = tau,  rhs = rhs, control = control, ...), {
            what <- paste("rq.fit.", method, sep = "")
            if (exists(what, mode = "function"))
                (get(what, mode = "function"))(x, y, ...)
            else stop(paste("unimplemented method:", method))
        })
    fit$contrasts <- attr(x, "contrasts")
    fit$resid <- c(y - x %*% fit$coef)

    fit
}
"untangle.specials" <-
function (tt, special, order = 1)
{
    spc <- attr(tt, "specials")[[special]]
    if (length(spc) == 0)
        return(list(vars = character(0), terms = numeric(0)))
    facs <- attr(tt, "factor")
    fname <- dimnames(facs)
    ff <- apply(facs[spc, , drop = FALSE], 2, sum)
    list(vars = (fname[[1]])[spc], terms = seq(ff)[ff & match(attr(tt,
        "order"), order, nomatch = 0)])
}

"qss" <-
function (x, constraint = "N", lambda = 1, ndum = 0, dummies = NULL, 
    Dorder = 1, w = rep(1, length(x))) 
{
    if (is.matrix(x)) {
        if (ncol(x) == 2) 
            qss <- qss2(x, constraint = constraint, dummies = dummies, 
                lambda = lambda, ndum = ndum, w = w)
        else if (ncol(x) == 1) 
            x <- as.vector(x)
        else stop("qss objects must have dimension 1 or 2")
    }
    if (is.vector(x)) {
	if(Dorder == 1)
	    qss <- qss1(x, constraint = constraint, lambda = lambda, 
		dummies = dummies, ndum = ndum, w = w)
	else if(Dorder == 0){
	    if(!(constraint %in% c("N","I","D")))
		stop("This constraint not implemented for Dorder = 0")
	    else
		qss <- qts1(x, constraint = constraint, lambda = lambda, 
		    dummies = dummies, ndum = ndum, w = w)
	}
	else
	    stop("Dorder must be either 0 or 1")
    }
    qss
}

"qss2" <-
function(x, y, constraint = "N", lambda = 1, ndum= 0, dummies = NULL, w=rep(1,length(x))){
#
# Sparse Additive Quantile Smoothing Spline Models - Bivariate (Triogram) Module
#
# This function returns a structure intended to make model.matrix for a bivariate
# nonparametric component of a model formula specified by a call to rqss().  A sparse form
# of the Frisch Newton algorithm is eventually called to compute the estimator.
# An optional convexity/concavity constraint can be specified.  If
# the formula consists of a single qss component then the estimator solves the
# following variational problem:
#
#       min sum rho_tau (y_i - g(x_i)) + lambda V(grad(g))
#
# where V(f) denotes the total variation of the function f.  The solution is a piecewise
# linear function on the Delaunay triangulation formed by the observed (x_i,y_i) points.
# Additive models can consist
# of several components of this form plus partial linear and univariate qss components.
# To resolve the identifiability problem we delete the first column of the qss design
# components.  On return F contains the fidelity portion of the design, A the penalty
# contribution of the design, R the constraint portion, and r the rhs of the constraints.
#
# Constraints are specified by the constraint argument:
#
#	N	none
#	V	convex
#	C	concave
#
# Author:  Roger Koenker   April 2, 2003
#
# For a prototype see triogram in ~roger/projects/tv/cobar/.RData on ysidro.
#
# Warning:   Under development...todo:
#
#       o  weights
#       o  dummy x's
#       o  tau's
#       o  lambda's
#       o  ...
#
   stopifnot(requireNamespace("tripack"))
#
    y <- x[,2]
    x <- x[,1]
    n <- length(x)
    if (n != length(y))
        stop("xy lengths do not match")
    f <- triogram.fidelity(x, y, ndum = ndum, dummies = dummies)
    F <- f$F
    A <- triogram.penalty(f$x, f$y)
    switch(constraint, V = {
        R <- A
        r <- rep(0, nrow(R))
    }, C = {
        R <- -A
        r <- rep(0, nrow(R))
    }, N = {
        R = NULL
        r = NULL
    })
    list(x = list(x = f$x, y = f$y), F = F[, -1], dummies = f$dummies,
        lambda = lambda, A = A[, -1], R = R[, -1], r = r)
}

qts1 <- function (x, constraint = "N", lambda = 1, dummies = dummies, 
    ndum = 0, w = rep(1, length(x))) 
{ # This is the taut string TV(g) not TV(g') penalty option
    xun <- unique(x[order(x)])
    h <- diff(xun)
    nh <- length(h)
    nx <- length(x)
    p <- nh + 1
    makeD <- function(p) {
        new("matrix.csr", ra = c(rbind(rep(-1, (p - 1)), rep(1,
            (p - 1)))), ja = as.integer(c(rbind(1:(p - 1), 2:p))),
            ia = as.integer(2 * (1:p) - 1), dimension = as.integer(c(p -
                1, p)))
    }
    A <- new("matrix.csr", ra = c(rbind(-1/h, 1/h)), ja = as.integer(c(rbind(1:nh, 
        2:(nh + 1)))), ia = as.integer(2 * (1:(nh + 1)) - 1), 
        dimension = as.integer(c(nh, nh + 1)))
    if (length(xun) == length(x)) {
        F <- new("matrix.csr", ra = rep(1, nx), ja = as.integer(rank(x)), 
            ia = 1:(nx + 1), dimension = as.integer(c(nx, nx)))
    }
    else {
        F <- new("matrix.csr", ra = rep(1, nx), ja = as.integer(factor(x)), 
            ia = 1:(nx + 1), dimension = as.integer(c(nx, length(xun))))
    }
    switch(constraint, 
	   I = {R <- makeD(p); r <- rep(0, p-1)},
	   D = {R <- -makeD(p); r <- rep(0, p-1)},
	   N = {R <- NULL; r <- NULL})
    list(x = list(x = xun), F = F[, -1], lambda = lambda, A = A[,
        -1], Dorder = 0, R = R[,-1], r = r)
}

"qss1" <-
function (x, constraint = "N", lambda = 1, dummies = dummies,
        ndum = 0, w = rep(1, length(x))){
#
# Sparse Additive Quantile Smoothing Spline Models - Univariate Module
#
# This function returns a structure intended to make model.matrix for a univariate
# nonparametric component of a model formula specified by a call to rqss().  A sparse form
# of the Frisch Newton algorithm is eventually called to compute the estimator.
# Optional monotonicity and/or convexity/concavity constraints can be specified.  If
# the formula consists of a single qss component then the estimator solves the
# following variational problem:
#
#       min sum rho_tau (y_i - g(x_i)) + lambda V(g')
#
# where V(f) denotes the total variation of the function f.  The solution is a piecewise
# linear function with "knots" at the observed x_i points.  Additive models can consist
# of several components of this form plus partial linear and triogram components.
# To resolve the identifiability problem we delete the first column of the qss design
# components.  On return F contains the fidelity portion of the design, A is the penalty
# contribution of the design, R the constraint portion, and r the rhs of the constraints.
#
# Constraints are specified by the constraint argument:
#
#	N	none
#	I	monotone increasing
#	D	monotone decreasing
#	V	convex
#	C	concave
#	CI	concave and monotone increasing
#	...	etc
#
# Author:  Roger Koenker   February 27, 2003
#
# Warning:   Under development...todo:
#
#       o  weights
#       o  dummy x's
#       o  tau's
#       o  lambda's
#       o  ...
#
#
    xun <- unique(x[order(x)])
    h <- diff(xun)
    nh <- length(h)
    nx <- length(x)
    p <- nh + 1
    B <- new("matrix.csr", ra = c(rbind(-1/h, 1/h)), ja = as.integer(c(rbind(1:nh,
        2:(nh + 1)))), ia = as.integer(2 * (1:(nh + 1)) - 1),
        dimension = as.integer(c(nh, nh + 1)))
    makeD <- function(p) {
        new("matrix.csr", ra = c(rbind(rep(-1, (p - 1)), rep(1,
            (p - 1)))), ja = as.integer(c(rbind(1:(p - 1), 2:p))),
            ia = as.integer(2 * (1:p) - 1), dimension = as.integer(c(p -
                1, p)))
    }
    D <- makeD(nh)
    A <- D %*% B
    if (length(xun) == length(x)){
        F <- new("matrix.csr", ra = rep(1, nx), ja = as.integer(rank(x)),
            ia = 1:(nx + 1), dimension = as.integer(c(nx, nx)))
	}
    else {
        F <- new("matrix.csr", ra = rep(1, nx), ja = as.integer(factor(x)),
            ia = 1:(nx + 1), dimension = as.integer(c(nx, length(xun))))
	}
   switch(constraint,
        V = {   R <- A;
                r <- rep(0,nrow(R))
                },
        C = {   R <- -A;
                r <- rep(0,nrow(R))
                },
        I = {   R <- makeD(p)
                r <- rep(0,p-1)
                },
        D = {   R <- -makeD(p)
                r <- rep(0,p-1)
                },
        VI = {  R <- makeD(p)
		R <- rbind(R,A)
                r <- rep(0,nrow(R))
		},
        VD = {  R <- -makeD(p)
		R <- rbind(R,A)
                r <- rep(0,nrow(R))
		},
        CI = {  R <- makeD(p)
		R <- rbind(R,-A)
                r <- rep(0,nrow(R))
		},
        CD = {  R <- -makeD(p)
		R <- rbind(R,-A)
                r <- rep(0,nrow(R))
		},
	N = { R=NULL; r=NULL}
	)
   list(x = list(x=xun), F=F[,-1], lambda = lambda, A=A[,-1], 
	Dorder = 1, R=R[,-1], r=r)
}
"plot.qss1" <-
function(x, rug = TRUE, jit = TRUE, add = FALSE, ...)
{
    if(!add) plot(x,type = "n", ...)
	lines(x, ...)
    if(rug) {
	if(jit)
	    rug(jitter(x[,1]))
	else rug(x[,1])
    }
}
"plot.qts1" <-
function (x, rug = TRUE, jit = TRUE, add = FALSE, ...) 
{
    if (!add) 
        plot(x, type = "S", ...)
    lines(x, type = "S", ...)
    if (rug) {
        if (jit) 
            rug(jitter(x[, 1]))
        else rug(x[, 1])
    }
}
"plot.qss2" <-
function (x, render = "contour", ncol = 100, zcol = NULL, ...)
{
    stopifnot(requireNamespace("tripack"))
    y <- x[, 2]
    z <- x[, 3]
    x <- x[, 1]
    tri <- tripack::tri.mesh(x, y)
    if (render == "rgl") {
        if(!requireNamespace("rgl",quietly=TRUE))
		stop("The package rgl is required")
        collut <- terrain.colors(ncol)
        if (!length(zcol))
            zcol <- z
        if (max(z) > max(zcol) || min(z) < min(zcol))
            warning("fitted z values out of range of zcol vector")
        zlim <- range(zcol)
        colz <- ncol * (z - zlim[1])/(zlim[2] - zlim[1]) + 1
        colz <- collut[colz]
        s <- c(t(tripack::triangles(tri)[, 1:3]))
        rgl::rgl.triangles(x[s], y[s], z[s], col = colz[s])
    }
    else {
        stopifnot(requireNamespace("akima"))
        if(render == "contour"){
                plot(x, y, type = "n", ...)
                contour(akima::interp(x, y, z), add = TRUE, frame.plot = TRUE, ...)
                tripack::convex.hull(tri, plot.it = TRUE, add = TRUE)
                }
        else if(render == "persp")
                persp(akima::interp(x, y, z, ), theta = -40, phi = 20, xlab = "x",
                        ylab = "y", zlab = "z", ...)
        else stop(paste("Unable to render: ",render))
    }
}
plot.rqss <-
function (x, rug = TRUE, jit = TRUE, bands = NULL, coverage = 0.95, add = FALSE, 
	  shade = TRUE, select = NULL, pages = 0, titles = NULL, bcol = NULL, ...) 
{
    SetLayout <- function(m, p) {
            # Shamelessly cribbed from mgcv
            # m is the number of plots
            # p is the number of pages
        if (p > m) 
            p <- m
        if (p < 0) 
            p <- 0
        if (p != 0) {
            q <- m%/%p
            if ((m%%p) != 0) {
                q <- q + 1
                while (q * (p - 1) >= m) p <- p - 1
            }
            c <- trunc(sqrt(q))
            if (c < 1) 
                c <- 1
            r <- q%/%c
            if (r < 1) 
                r <- 1
            while (r * c < q) r <- r + 1
            while (r * c - q > c && r > 1) r <- r - 1
            while (r * c - q > r && c > 1) c <- c - 1
            oldpar <- par(mfrow = c(r, c))
        }
        else oldpar <- par()
        return(oldpar)
    }
    m <- length(x$qss)
    if (m == 0) 
        stop("No qss object to plot")
    if(length(select)) {
        if(all(select %in% 1:m))
            oldpar <- SetLayout(length(select), pages)
        else
            stop(paste("select must be in 1:",m,sep="")) 
        }
    else
        oldpar <- SetLayout(m, pages)
    if ((pages == 0 && prod(par("mfrow")) < m && dev.interactive()) || 
        pages > 1 && dev.interactive()) 
        ask <- TRUE
    else ask <- FALSE
    if (ask) {
        oask <- devAskNewPage(TRUE)
        on.exit(devAskNewPage(oask))
    }
    qssnames <- names(x$qss)
    if(length(titles)){
        if(length(titles) != length(qssnames))
            stop("Length of titles doesn't match length of qssnames")
	}
    else{
        titles <- paste("Effect of ", qssnames)
	}
    if (length(bands)) {
        rdf <- x$n - x$edf
        if (any(unlist(lapply(x$qss, function(x) ncol(x$xyz) == 3)))) 
            warning("Can't plot confidence bands in 3D (yet)")
        band <- as.list(rep(NA, m))
        V <- summary(x, cov = TRUE, ...)$Vqss
        "summary.qss1" <- function(object, V, ngrid = 400, ...) {
            x <- object$xyz[, 1]
            eps <- 0.01
            newd <- data.frame(x = seq(min(x) + eps, max(x) - 
                eps, length = ngrid))
            G <- predict(object, newd, ...)
            ones <- as.matrix.csr(matrix(1, nrow(G$D), 1))
            D <- cbind(ones, G$D)
            S <- as.matrix(D %*% V %*% t(D))
            se <- sqrt(diag(S))
            cv <- qt(1 - (1 - coverage)/2, rdf)
            if (bands %in% c("uniform","both")) {
                E <- eigen(as.matrix(V))
                B <- E$vectors %*% diag(sqrt(pmax(0,E$values))) %*% t(E$vectors)
                D <- as.matrix(D)
                BX1 <- B %*% t(D[-1, ])
                BX1 <- BX1/sqrt(apply(BX1^2, 2, sum))
                BX0 <- B %*% t(D[-nrow(D), ])
                BX0 <- BX0/sqrt(apply(BX0^2, 2, sum))
                kappa <- sum(sqrt(apply((BX1 - BX0)^2, 2, sum)))
                cvu <- critval(kappa, alpha = 1 - coverage, rdf = rdf)
                if(bands == "both") cv <- c(cvu,cv)
                else cv <- cvu
                }
            list(pred = data.frame(x = G$x, y = G$y, se = se), 
                cv = cv)
        }
    }
    if(!length(select)) 
        select <- 1:m
    for (i in select) {
	if(!is.null(x$qss[[i]]$Dorder)){ 
	    if(x$qss[[i]]$Dorder == 0){
		qts <- x$qss[[i]]$xyz
		qts[, 2] <- x$coef[1] + qts[, 2]
		plot.qts1(qts, add = add, ...)
		}
	    else { 
		qss <- x$qss[[i]]$xyz
		if (length(bands)) {
		    if (is.na(x$coef["(Intercept)"])) 
			stop("rqss confidence bands require an intercept parameter")
		    B <- summary(x$qss[[i]], V[[i]], ...)
		    cv <- B$cv
		    B <- B$pred
		    B$y <- B$y + x$coef["(Intercept)"]
		    if(!length(bcol)) bcol <- c("grey85","grey65")
		    for(k in 1:length(cv)){
			if (add || k > 1) 
			    if(shade){
				polygon(c(B$x,rev(B$x)),
				c(B$y - cv[k] * B$se,rev(B$y + cv[k] * B$se)),
				col = bcol[k], border = FALSE)
			    }
			    else
				matlines(B$x, cbind(B$y, B$y + cv[k] * cbind(-B$se, 
				B$se)), lty = c(1, 2, 2), col = c("black", "blue", "blue"))
			else {
			    matplot(B$x, B$y + cv[k] * cbind(-B$se, B$se), 
				xlab = paste(qssnames[i]), ylab = "Effect", type = "n", ...)
			    if(shade){
				polygon(c(B$x,rev(B$x)),
				    c(B$y - cv[k] * B$se,rev(B$y + cv[k] * B$se)),
				    col = bcol[k], border = FALSE)
			    } 
			    else{
				lines(B$x, B$y + cv * B$se, lty = 2, ...)
				lines(B$x, B$y - cv * B$se, lty = 2, ...)
			    }
			}
			lines(B$x, B$y, ...)
		    }
		    band[[i]] <- list(x = B$x, blo = B$y - B$se %o% cv, bhi = B$y + B$se %o% cv)
		    if (rug) {
			if (jit) 
			    rug(jitter(qss[, 1]))
			else rug(qss[, 1])
                    }
		}
		else {
		    qss[, 2] <- x$coef[1] + qss[, 2]
		    plot.qss1(qss, xlab = paste(qssnames[i]), ylab = "Effect", 
		    rug = rug, jit = jit, add = add, ...)
		}
	    }
        }
	qss <- x$qss[[i]]$xyz
        if (ncol(qss) == 3) {
             qss[, 3] <- x$coef[1] + qss[, 3]
             plot.qss2(qss, ...)
        }
	title(titles[i])
    }
    if (pages > 0) 
        par(oldpar)
    if (length(bands)) 
        class(band) <- "rqssband"
    else
        band <- NULL
    invisible(band)
}

"triogram.fidelity" <- function (x, y, ndum=0, dummies = NULL)
{
#Make fidelity block of the triogram design in sparse matrix.csr form
#The rather esoteric match call identifies and handles duplicated xy points
n <- length(x)
A <- as.data.frame(cbind(x,y))
dupA <- duplicated(A)
if(any(dupA)){
  x <- x[!dupA]
  y <- y[!dupA]
  J <- match(do.call("paste",c(A,"\r")),do.call("paste",c(A[!dupA,],"\r")))
  z <- new("matrix.csr",ra=rep(1,n), ja=J, ia=1:(n+1),dimension=as.integer(c(n,max(J))))
  }
else{
  z <- as(n,"matrix.diag.csr")
  z <- as(z,"matrix.csr")
 }
#Augment with dummy vertices, if any...
if(length(dummies)){
	if (is.list(dummies)){
        	if (all(!is.na(match(c("x", "y"), names(dummies))))){
        		ndum <- length(dummies$x)
			if(length(dummies$y) == ndum){
        			x <- c(x,dummies$x)
        			y <- c(y,dummies$y)
        			zdum <- as.matrix.csr(0,n,ndum)
        			z <- cbind(z,zdum)
				}
        		else stop("dummies x and y components differ in length")
			}
        	else stop("dummies list lacking x and y elements")
		}
    	else stop("dummies argument invalid (not a list) in triogram.fidelity")
	}
else if(ndum > 0){
        u <- runif(ndum); v <- runif(ndum)
        xd <- min(x) + u * (max(x)-min(x))
        yd <- min(y) + v * (max(y)-min(y))
        T <- tripack::tri.mesh(x,y)
        s <- tripack::in.convex.hull(T,xd,yd)
        x <- c(x,xd[s])
        y <- c(y,yd[s])
        ndum <- sum(s)
        zdum <- as.matrix.csr(0,n,ndum)
        z <- cbind(z,zdum)
	dummies <- list(x = xd[s],y = yd[s])
        }
list(x=x,y=y,F=z, dummies = dummies)
}
"triogram.penalty" <- function (x, y, eps = .Machine$double.eps)
{
    n <- length(x)
    tri <- tripack::tri.mesh(x, y)
    bnd <- tripack::on.convex.hull(tri,x,y)
    q <- length(tri$tlist)
    m <- 13 * n
    z <- .Fortran("penalty", as.integer(n), as.integer(m), as.integer(q),
        as.double(x), as.double(y), as.integer(bnd),as.integer(tri$tlist),
        as.integer(tri$tlptr), as.integer(tri$tlend), rax = double(m),
	jax = integer(m), ned = integer(1), as.double(eps), 
	ierr = integer(1))[c("rax", "jax", "iax", "ned", "ierr")]
    if (z$ierr == 1)
        stop("collinearity in ggap")
    nnz <- 4 * z$ned
    ra <- z$rax[1:nnz]
    ja <- z$jax[1:nnz]
    ia <- as.integer(1 + 4 * (0:z$ned))
    dim <- as.integer(c(z$ned, n))
    new("matrix.csr",ra=ra,ja=ja,ia=ia,dimension=dim)
}

predict.rqss <-
function (object, newdata, interval = "none",  level = 0.95, ...) 
{
    ff <- object$fake.formula
    Terms <- delete.response(terms(object$formula, "qss"))
    Names <- all.vars(parse(text = ff))
    if (any(!(Names %in% names(newdata)))) 
        stop("newdata doesn't include some model variables")
    #ff <- reformulate(ff)
    nd <- eval(model.frame(ff, data = newdata), parent.frame())
    qssterms <- attr(Terms, "specials")$qss
    if (length(qssterms)) {
        tmp <- untangle.specials(Terms, "qss")
        dropv <- tmp$terms
        m <- length(dropv)
        if (length(dropv)) 
            PLTerms <- Terms[-dropv]
        attr(PLTerms, "specials") <- tmp$vars
    }
    else {
        PLTerms <- Terms
        m <- 0
    }
    if(requireNamespace("MatrixModels") && requireNamespace("Matrix"))
        X <- as(MatrixModels::model.Matrix(PLTerms, data = nd, 
		  contrasts = contrasts, sparse = TRUE),"matrix.csr")
    else
        X <- model.matrix(PLTerms, data = nd)
    p <- ncol(X)
    y <- X %*% object$coef[1:p]
    X <- as.matrix.csr(X)
    if (m > 0) {
        for (i in 1:m) {
            qss <- object$qss[[i]]
            names <- all.vars(Terms[dropv[i]])
            names <- names[names %in% Names]
            dimnames(qss$xyz)[[2]] <- c(names, "zfit")
            newd <- nd[names]
            if (ncol(qss$xyz) == 3) {
                g <- predict.qss2(qss$xyz, newdata = newd, ...)
                y <- y + g$z
                if(interval == "confidence")
                    X <- cbind(X,g$D)
            }
            else if (ncol(qss$xyz) == 2) {
                g <- predict(qss, newdata = newd, ...)
                y <- y + g$y
                if(interval == "confidence")
                    X <- cbind(X,g$D)
            }
            else stop("invalid fitted qss object")
        }
    }
    if(interval == "confidence"){
        v <- sqrt(diag(X %*% summary(object, cov = TRUE)$V %*% t(X)))
        calpha <- qnorm(1 - (1-level)/2)
        y <- cbind(y,y - v*calpha,y + v*calpha)
        dimnames(y)[[2]] <- c("yhat","ylower","yupper")
	}
    y
}

"predict.qss1" <-
function (object, newdata, ...)
{
    x <- object$xyz[, 1]
    y <- object$xyz[, 2] 
    if(ncol(newdata)==1)
        newdata <- newdata[,1]
    else
        stop("newdata should have only one column for predict.qss1")
    if (any(diff(x) < 0))
        stop("x coordinates in qss1 object not monotone")
    if (max(newdata) > max(x) || min(newdata) < min(x))
        stop("no extrapolation allowed in predict.qss")
    bin <- cut(newdata, unique(x), label = FALSE, include.lowest = TRUE)
    p <- length(x)
    m <- length(newdata)
    V <- cbind(bin, bin + 1)
    B <- cbind(x[bin + 1] - newdata, newdata - x[bin])/(x[bin +
        1] - x[bin])
    ra <- c(t(B))
    ja <- as.integer(c(t(V)))
    ia <- as.integer(c(2 * (1:m) - 1, 2 * m + 1))
    dim <- c(m, p)
    D <- new("matrix.csr", ra = ra, ja = ja, ia = ia, dimension = dim)
    list(x = newdata, y = D %*% y, D = D[, -1])
}

predict.qss2 <- function (object, newdata, ...) 
{
    x <- object[, 1]
    y <- object[, 2]
    z <- object[, 3]
    tri.area <- function(v) {
        0.5 * ((v[2, 1] - v[1, 1]) * (v[3, 2] - v[1, 2]) - (v[3, 
            1] - v[1, 1]) * (v[2, 2] - v[1, 2]))
    }
    barycentric <- function(v) {
        b <- rep(0, 3)
        Area <- tri.area(v[1:3, ])
        b[1] <- tri.area(v[c(4, 2, 3), ])/Area
        b[2] <- tri.area(v[c(1, 4, 3), ])/Area
        b[3] <- tri.area(v[c(1, 2, 4), ])/Area
        if (any(b < 0) || any(b > 1)) 
            stop("barycentric snafu")
        b
    }
    if (is.list(newdata)) {
	fnames <- (dimnames(object)[[2]])[1:2]
        if (all(!is.na(match(fnames, names(newdata))))) {
            newx <- newdata[[fnames[1]]]
            newy <- newdata[[fnames[2]]]
	   }
        else (stop("qss object and newdata frame names conflict"))
        }
    else if (is.matrix(newdata)) 
        if (ncol(newdata) == 2) {
            newx <- newdata[, 1]
            newy <- newdata[, 2]
        }
        else (stop("newdata matrix must have 2 columns"))
    tri <- tripack::tri.mesh(x, y)
    if (!all(tripack::in.convex.hull(tri, newx, newy))) 
        stop("some newdata points outside convex hull")
    p <- length(x)
    m <- length(newx)
    V <- matrix(0, m, 3)
    B <- matrix(0, m, 3)
    for (i in 1:m) {
        V[i, ] <- unlist(tripack::tri.find(tri, newx[i], newy[i]))
        v <- rbind(cbind(x[V[i, ]], y[V[i, ]]), c(newx[i], newy[i]))
        B[i, ] <- barycentric(v)
    }
    ra <- c(t(B))
    ja <- as.integer(c(t(V)))
    ia <- as.integer(3 * (0:m) + 1)
    D <- new("matrix.csr", ra = ra, ja = ja, ia = ia, dimension = c(m, 
        p))
    list(x = newx, y = newy, z = c(D %*% z), D = D[,-1])
}

fitted.rqss  <-  function(object, ...) (object$X %*% object$coef)[1:object$n]
resid.rqss <- function(object, ...) object$resid[1:object$n]

rqss <- function (formula, tau = 0.5, data = parent.frame(), weights, 
    subset, na.action, method = "sfn", lambda = NULL, contrasts = NULL, 
    ztol = 1e-05, control = sfn.control(), ...) 
{
    call <- match.call()
    m <- match.call(expand.dots = FALSE)
    temp <- c("", "formula", "data", "weights", "na.action", "subset")
    m <- m[match(temp, names(m), nomatch = 0)]
    m[[1]] <- as.name("model.frame")
    special <- "qss"
    Terms <- if (missing(data)) 
        terms(formula, special)
    else terms(formula, special, data = data)
    qssterms <- attr(Terms, "specials")$qss
    dropx <- NULL
    if (length(tau) > 1) {
        tau <- tau[1]
        warning("multiple taus not supported, using first element")
    }
    if (length(qssterms)) {
        tmpc <- untangle.specials(Terms, "qss")
        ord <- attr(Terms, "order")[tmpc$terms]
        if (any(ord > 1)) 
            stop("qss can not be used in an interaction")
        dropx <- tmpc$terms
        if (length(dropx)) 
            Terms <- Terms[-dropx]
        attr(Terms, "specials") <- tmpc$vars
        fnames <- function(x) {
            fy <- all.names(x[[2]])
            if (fy[1] == "cbind") 
                fy <- fy[-1]
            fy
        }
        fqssnames <- unlist(lapply(parse(text = tmpc$vars), fnames))
        qssnames <- unlist(lapply(parse(text = tmpc$vars), function(x) deparse(x[[2]])))
    }
    ff <- delete.response(terms(formula(Terms)))
    if (exists("fqssnames")) {
	mff <- m
	mff$formula <- Terms
	#mff <- delete.response(terms(formula(Terms)))
        ffqss <- paste(fqssnames, collapse = "+")
	#ff <- paste(deparse(formula(mff)), "+", ffqss)
	mff$formula <- as.formula(paste(deparse(mff$formula), "+", ffqss))
	ff <- delete.response(terms(formula(mff)))
	ff <- deparse(formula(ff))
	mff <- eval(mff, parent.frame())
    }
    m$formula <- Terms
    m <- eval(m, parent.frame())
    weights <- model.extract(m, weights)
    process <- (tau < 0 || tau > 1)
    Y <- model.extract(m, "response")
    if (requireNamespace("MatrixModels") && requireNamespace("Matrix")) {
        X <- MatrixModels::model.Matrix(Terms, m, contrasts.arg = contrasts, 
            sparse = TRUE)
        vnames <- dimnames(X)[[2]]
        X <- as(X, "matrix.csr")
    }
    else {
        X <- model.matrix(Terms, m, contrasts)
        vnames <- dimnames(X)[[2]]
    }
    p <- ncol(X)
    pf <- environment(formula)
    nrL <- 0
    if (method == "lasso") {
        if (!length(lambda)) 
            stop("No lambda specified for lasso constraint")
        if (length(lambda) == 1) 
            lambda <- c(0, rep(lambda, p - 1))
        if (length(lambda) != p) 
            stop("lambda must be either of length p, or length 1")
        if (any(lambda < 0)) 
            stop("negative lambdas disallowed")
        L <- diag(lambda, nrow = length(lambda))
        L <- L[which(lambda != 0), , drop = FALSE]
        L <- as.matrix.csr(L)
        nrL <- nrow(L)
        ncL <- ncol(L)
    }
    if (length(qssterms) > 0) {
        F <- as.matrix.csr(X)
	pf <- environment(formula)
        qss <- lapply(tmpc$vars, function(u) eval(parse(text = u), mff, pf))
        mqss <- length(qss)
        ncA <- rep(0, mqss + 1)
        nrA <- rep(0, mqss + 1)
        nrR <- rep(0, mqss + 1)
        for (i in 1:mqss) {
            F <- cbind(F, qss[[i]]$F)
            ncA[i + 1] <- ncol(qss[[i]]$A)
            nrA[i + 1] <- nrow(qss[[i]]$A)
            nrR[i + 1] <- ifelse(is.null(nrow(qss[[i]]$R)), 0, 
                nrow(qss[[i]]$R))
            vnames <- c(vnames, paste(qssnames[i], 1:ncA[i + 
                1], sep = ""))
        }
        A <- as.matrix.csr(0, sum(nrA), sum(ncA))
        if (sum(nrR) > 0) {
            R <- as.matrix.csr(0, sum(nrR), sum(ncA))
            nrR <- cumsum(nrR)
        }
        ncA <- cumsum(ncA)
        nrA <- cumsum(nrA)
        lambdas <- rep(0, mqss)
        for (i in 1:mqss) {
            lambdas[i] <- qss[[i]]$lambda
            Arows <- (1 + nrA[i]):nrA[i + 1]
            Acols <- (1 + ncA[i]):ncA[i + 1]
            A[Arows, Acols] <- qss[[i]]$lambda * qss[[i]]$A
            if (nrR[i] < nrR[i + 1]) 
                R[(1 + nrR[i]):nrR[i + 1], (1 + ncA[i]):ncA[i + 
                  1]] <- qss[[i]]$R
        }
        A <- cbind(as.matrix.csr(0, nrA[mqss + 1], p), A)
        if (nrR[mqss + 1] > 0) {
            R <- cbind(as.matrix.csr(0, nrR[mqss + 1], p), R)
            r <- rep(0, nrR[mqss + 1])
        }
        else {
            R <- NULL
            r <- NULL
        }
        if (method == "lasso") 
            A <- rbind(cbind(L, as.matrix.csr(0, nrL, ncol(F) - ncL)), A)
	if(length(weights)){
	   F <- F * weights
	   Y <- Y * weights
	}
        X <- rbind(F, A)
        Y <- c(Y, rep(0, nrow(A)))
        rhs <- t(rbind((1 - tau) * F, 0.5 * A)) %*% rep(1, nrow(X))
        XpX <- t(X) %*% X
        nnzdmax <- XpX@ia[length(XpX@ia)] - 1
        if (is.null(control[["nsubmax"]])) 
            control[["nsubmax"]] <- max(nnzdmax, floor(1000 + 
                exp(-1.6) * nnzdmax^1.2))
        if (is.null(control[["nnzlmax"]])) 
            control[["nnzlmax"]] <- floor(2e+05 - 2.8 * nnzdmax + 
                7e-04 * nnzdmax^2)
        if (is.null(control[["tmpmax"]])) 
            control[["tmpmax"]] <- floor(1e+05 + exp(-12.1) * 
                nnzdmax^2.35)
        fit <- if (length(r) > 0) 
            rqss.fit(X, Y, tau = tau, rhs = rhs, method = "sfnc", 
                R = R, r = r, control = control, ...)
        else rqss.fit(X, Y, tau = tau, rhs = rhs, method = "sfn", 
            control = control, ...)
        for (i in 1:mqss) {
            ML <- p + 1 + ncA[i]
            MU <- p + ncA[i + 1]
            qss[[i]] <- list(xyz = cbind(qss[[i]]$x$x, qss[[i]]$x$y, 
                c(0, fit$coef[ML:MU])), dummies = qss[[i]]$dummies, 
			     Dorder = qss[[i]]$Dorder)
            if (ncol(qss[[i]]$xyz) == 2) 
                class(qss[[i]]) <- ifelse(qss[[i]]$Dorder == 1,"qss1", "qts1")
            else class(qss[[i]]) <- "qss2"
        }
        names(qss) <- qssnames
        fit$qss <- qss
    }
    else {
        X <- as.matrix.csr(X)
        nrA <- 0
        if (method == "lasso") {
            rhs <- t(rbind((1 - tau) * X, 0.5 * L)) %*% rep(1, 
                nrow(X) + nrow(L))
            X <- rbind(X, L)
            Y <- c(Y, rep(0, nrL))
        }
        else rhs <- NULL
        if (length(weights)) {
            if (any(weights < 0)) 
                stop("negative weights not allowed")
            X <- X * weights
            Y <- Y * weights
        }
        XpX <- t(X) %*% X
        nnzdmax <- XpX@ia[length(XpX@ia)] - 1
        if (is.null(control[["nsubmax"]])) 
            control[["nsubmax"]] <- max(nnzdmax, floor(1000 + 
                exp(-1.6) * nnzdmax^1.2))
        if (is.null(control[["nnzlmax"]])) 
            control[["nnzlmax"]] <- floor(2e+05 - 2.8 * nnzdmax + 
                7e-04 * nnzdmax^2)
        if (is.null(control[["tmpmax"]])) 
            control[["tmpmax"]] <- floor(1e+05 + exp(-12.1) * 
                nnzdmax^2.35)
        fit <- rqss.fit(X, Y, tau = tau, rhs = rhs, control = control, 
            method = method, ...)
        fit$nrA <- nrA
    }
    names(fit$coef) <- vnames
    n <- length(fit$resid) - nrL - nrA[length(nrA)]
    if(length(weights)) fit$resid[1:n] <- fit$resid[1:n]/weights
    uhat <- fit$resid[1:n]
    Rho <- function(u, tau) sum(u * (tau - (u < 0)))
    fit$fidelity <- Rho(uhat, tau)
    fit$edf <- sum(abs(uhat) < ztol)
    fit$X <- X
    fit$y <- Y
    fit$n <- n
    fit$nrL <- nrL
    fit$terms <- Terms
    fit$fake.formula <- ff
    fit$formula <- formula
    fit$method <- method
    fit$call <- call
    fit$tau <- tau
    if (length(qssterms)) {
        fit$lambdas <- lambdas
        fit$qssnames <- qssnames
        fit$nrA <- nrA
        fit$ncA <- cumsum(c(p, diff(ncA)))
    }
    else fit$ncA <- p
    attr(fit, "na.message") <- attr(m, "na.message")
    class(fit) <- "rqss"
    fit
}
"Munge" <- function(formula, ...) { 
    # Recursive substitution {per Gabor Grothendieck}
    if (length(formula) > 1) {
	if (identical(formula[[2]], as.name(names(list(...))))) 
	    formula <- eval(formula, list(...))
	if (length(formula) > 1) 
	    for (i in 1:length(formula)) 
		formula[[i]] <- Recall(formula[[i]], ...)
	}
   formula
}

"summary.rqss" <- function(object, cov = FALSE, ztol = 1e-5, ...){
    resid <- object$resid
    coef <- object$coef
    lambdas <- object$lambdas
    formula <- object$formula
    fidelity <- object$fidelity
    edf <- object$edf
    nrA <- object$nrA
    ncA <- object$ncA
    nrL <- object$nrL
    tau <- object$tau
    cntl <- object$control
    X <- object$X
    p <- ncol(object$X)
    m <- length(ncA)
    n <- length(resid) - nrL - nrA[m]
    uhat <- resid[1:n]
    h <- bandwidth.rq(tau, n, hs = TRUE)
    while((tau - h < 0) || (tau + h > 1)) h <- h/2
    h <- (qnorm(tau + h) - qnorm(tau - h)) * max(ztol,min(sqrt(var(uhat)),
        (quantile(uhat, 0.75) - quantile(uhat, 0.25))/1.34))
    f <- c(dnorm(uhat/h)/h,rep(1, length(resid) - n))
    D <- t(X) %*% (f * X)
    D <- chol(.5 * (D + t(D)), nsubmax = cntl$nsubmax,
             nnzlmax = cntl$nnzlmax, tmpmax = cntl$tmpmax)
    D <- backsolve(D,diag(p))
    D0 <- tau * (1 - tau) * t(X) %*% X
    V <- D %*% D0 %*% D
    scale <- mean(f)
    serr <- sqrt(diag(V))
    ptab <- array(coef[1:ncA[1]], c(ncA[1],4))
    pnames <- names(coef[1:ncA[1]])
    dimnames(ptab) <- list(pnames, c("Estimate", "Std. Error", "t value", "Pr(>|t|)"))
    ptab[, 2] <- serr[1:ncA[1]]
    ptab[, 3] <- ptab[, 1]/ptab[, 2]
    ptab[, 4] <- 2 * (1 - pt(abs(ptab[, 3]), n - edf))
    if(cov) {
        Vcov <- V[1:ncA[1],1:ncA[1]]
        Vqss <- as.list(1:(m-1))
        }
    if(m > 1) {
        penalty <- rep(NA, m - 1)
        qssedfs <- rep(NA, m - 1)
        ntab <- matrix(0, m - 1, 5)
        vnames <- sub(".$","",names(coef[ncA[-length(ncA)]+1]))
        dimnames(ntab) <- list(vnames, c("EDF", "Lambda", "Penalty", "F value", "Pr(>F)"))
        for(i in 2:m) {
           ntab[i - 1, 3] <- sum(abs(resid[n + nrL + ((nrA[i - 1] + 1):nrA[i])]))
           ntab[i - 1, 1] <- sum(abs(resid[n + nrL + ((nrA[i - 1] + 1):nrA[i])]) > ztol)
           v <- V[c(1, (ncA[i-1] + 1):ncA[i]), c(1, (ncA[i-1] + 1):ncA[i])]
           v <- .5 * (v + t(v))
           if(cov) Vqss[[i-1]] <- v
           b <- coef[(ncA[i-1] + 1):ncA[i]]
           ntab[i-1, 4]  <- t(b) %*% solve(v[-1,-1],b)/ntab[i-1,1]
           ntab[i-1, 5]  <- 1 - pf(ntab[i-1,4],ntab[i-1,1], n - edf)
           }
        ntab[,2] <- lambdas
        ntab[,3] <- ntab[,3]/lambdas
        if(cov)
           z <- list(coef = ptab, qsstab = ntab, fidelity = fidelity, tau = tau,
		formula = formula, edf = edf, n = n, Vcov = Vcov, Vqss = Vqss, V = V)
        else
           z <- list(coef = ptab, qsstab = ntab, fidelity = fidelity, tau = tau,
		formula = formula, edf = edf, n = n)
        }
    else
        if(cov)
           z <- list(coef = ptab, fidelity = fidelity, formula = formula, tau = tau,
		edf = edf, n = n, Vcov = Vcov, V = V)
        else
           z <- list(coef = ptab, fidelity = fidelity, formula = formula, tau = tau,
		edf = edf, n = n)

    class(z) <- "summary.rqss"
    return(z)
}
"plot.summary.rqss" <- function(x, ...)
    warning("No plot method for summary.rqss objects:   plot the rqss object instead")

"print.rqss" <- function(x, ...) {
    cat("Formula:\n")
    print(x$formula)
    sx <- summary(x)
    cat("Quantile fidelity at tau = ", x$tau, "is", sx$fidelity, "\n")
    cat("Estimated Model Dimension is", sx$edf, "\n")
}
 
"logLik.rqss" <- function(object, ...){
	n <- object$n
	tau <- object$tau
	val <- n * (log(tau * (1-tau)) - 1 - log(object$fidelity/n))
	attr(val,"n") <- n
	attr(val,"df") <- object$edf
	class(val) <- "logLik"
	val
	}
"AIC.rqss" <- function(object, ... , k = 2){
	v <- logLik(object)
	if(k < 0) 
		k <- log(attr(v,"n"))
	val <- AIC(logLik(object), k = k)
	attr(val,"edf") <- attr(v,"df")
	val
	}
"dither"  <- function(x, type = "symmetric", value = NULL) {
	if(length(x) == 0)
		return(x)
	if(!is.numeric(x))
		stop("'x' must be numeric")
	if(!length(value))
		value <- min(diff(sort(unique(x))))
	if(type == "symmetric")
		v <- x + runif(length(x), -value/2, value/2)
	else if(type == "right")
		v <- x + runif(length(x), 0, value)
	else
		stop("invalid type")
	v
}
	
print.summary.rqss <-
function (x, digits = max(3, getOption("digits") - 3), signif.stars = getOption("show.signif.stars"), 
    ...) 
{
    cat("Formula:\n")
    print(x$formula)
    if (length(x$coef) > 0) {
        cat("\nParametric coefficients:\n")
        printCoefmat(x$coef, digits = digits, signif.stars = signif.stars, 
            na.print = "NA", ...)
    }
    cat("\n")
    if (length(x$qsstab) > 0) {
        cat("Approximate significance of qss terms:\n")
        printCoefmat(x$qsstab, digits = digits, signif.stars = signif.stars, 
            has.Pvalue = TRUE, na.print = "NA", cs.ind = 1, ...)
    }
    cat("\n")
    if (length(x$fidelity) > 0) 
        cat("  Quantile Fidelity at tau = ", x$tau, "  is  ", formatC(x$fidelity, 
		digits = 6, width = 11), "\n", sep = "")
    cat("  Effective Degrees of Freedom = ", formatC(x$edf, digits = 5, width = 8, 
        flag = "-"), "  Sample Size = ", x$n, "\n", sep = "")
    invisible(x)
}
critval <- function(kappa, alpha = 0.05, rdf = 0){
# Hotelling tube critical value for uniform confidence bands
# This should agree to about 6 digits with the following call to locfit:
# crit(const = c(kappa,1), cov = 1-alpha,rdf = rdf)$crit.val
   tube <- function(x,alpha,rdf){
      if(rdf <= 0)
         kappa * exp(-x^2/2)/pi + 2 * (1 - pnorm(x)) -  alpha
      else
         kappa * (1+x^2/rdf)^(-rdf/2)/pi + 2 * (1 - pt(x,rdf)) -  alpha
      }
   uniroot(tube,c(1,5),alpha = alpha, rdf = rdf)$root
   }

