expand.grid <- function(..., KEEP.OUT.ATTRS = TRUE)
{
    ## x should either be a list or a set of vectors or factors
    nargs <- length(args <- list(...))
    if(! nargs) return(as.data.frame(list()))
    if(nargs == 1 && is.list(a1 <- args[[1]]))
	nargs <- length(args <- a1)
    if(nargs == 0) return(as.data.frame(list()))
    cargs <- args
    nmc <- paste("Var", 1:nargs, sep="")
    nm <- names(args)
    if(is.null(nm)) nm <- nmc
    else if(any(ng0 <- nchar(nm) > 0)) nmc[ng0] <- nm[ng0]
    names(cargs) <- nmc
    rep.fac <- 1
    d <- sapply(args, length)
    if(KEEP.OUT.ATTRS) {
	dn <- vector("list", nargs)
	names(dn) <- nmc
    }
    orep <- prod(d)
    for(i in 1:nargs) {
	x <- args[[i]]
	if(KEEP.OUT.ATTRS)
	    dn[[i]] <- paste(nmc[i], "=", if(is.numeric(x)) format(x) else x,
			     sep = "")
	nx <- length(x)
	orep <- orep/nx
	x <- x[rep.int(rep.int(seq_len(nx),
			       rep.int(rep.fac, nx)), orep)]
	## avoid sorting the levels of character variates
	if(!is.factor(x) && is.character(x)) x <- factor(x, levels = unique(x))
	cargs[[i]] <- x
	rep.fac <- rep.fac * nx
    }
    if(KEEP.OUT.ATTRS)
	attr(cargs, "out.attrs") <- list(dim=d, dimnames=dn)
    structure(cargs, row.names = seq_len(prod(d)),
              ## R <= 2.3.x: colnames = nmc,
              class = "data.frame")
}
