#### Index Matrices -- Coercion and Methods (--> ../man/indMatrix-class.Rd )

## The typical	 'constructor' : coerce from  'perm'
setAs("integer", "indMatrix",
      function(from)
	  new("indMatrix", Dim = c(length(from), max(from)),
              Dimnames = list(names(from), NULL),
	      perm = from))

setAs("numeric", "indMatrix",
      function(from)
	  if(all(from == (i <- as.integer(from)))) as(i, "indMatrix")
      else stop("coercion to \"indMatrix\" only works from integer numeric"))

## A constructor from a list giving the index ('perm') and the number of columns
## (need this for cases in which the value(s) represented by the last
## column(s) has no observations):
.list2indMatrix <- function(from) {
    if(length(from) == 2 &&
       all(from[[1]] == (i <- as.integer(from[[1]]))) &&
       from[[2]] == (d <- as.integer(from[[2]])) &&
       length(d) == 1 && d >= max(i)) {
	new("indMatrix", perm = i, Dim = c(length(i), d))
    } else
	stop("coercion from list(i1,...,ik, d) to \"indMatrix\" failed.
 All entries must be integer valued and the number of columns, d, not smaller
 than the maximal index i*.")
}
setAs("list", "indMatrix", .list2indMatrix)

setAs("indMatrix", "matrix",
      function(from) {
	  fp <- from@perm
	  r <- ldiag(n = from@Dim[2])[fp,]
	  if(.has.DN(from)) dimnames(r) <- from@Dimnames
	  r
      })


## coerce to 0/1 sparse matrix, i.e. sparse pattern
.ind2ngT <- function(from) {
    d <- from@Dim
    new("ngTMatrix", i = seq_len(d[1]) - 1L, j = from@perm - 1L,
        Dim = d, Dimnames = from@Dimnames)
}
setAs("indMatrix", "ngTMatrix", .ind2ngT)

setAs("indMatrix", "TsparseMatrix", .ind2ngT)
setAs("indMatrix", "nMatrix", .ind2ngT)
setAs("indMatrix", "lMatrix", function(from) as(.ind2ngT(from), "lMatrix"))
setAs("indMatrix", "dMatrix", function(from) as(.ind2ngT(from), "dMatrix"))
setAs("indMatrix", "dsparseMatrix", function(from) as(from, "dMatrix"))
setAs("indMatrix", "lsparseMatrix", function(from) as(from, "lMatrix"))
setAs("indMatrix", "nsparseMatrix", .ind2ngT)
setAs("indMatrix", "CsparseMatrix",
      function(from) as(.ind2ngT(from), "CsparseMatrix"))
setAs("indMatrix", "ngeMatrix", function(from) as(.ind2ngT(from),"ngeMatrix"))

setAs("nMatrix", "indMatrix",
      function(from) {
	  from <- as(as(from, "TsparseMatrix"), "ngTMatrix")
	  n <- (d <- from@Dim)[1]
	  if(length(i <- from@i) != n)
	      stop("the number of non-zero entries differs from nrow(.)")
	  if((need.sort <- is.unsorted(i))) {
	      ii <- sort.list(i)
	      i <- i[ii]
	  }
	  if(n >= 1 && !identical(i, 0:(n - 1)))
	      stop("must have exactly one non-zero entry per row")

	  new("indMatrix", ## validity checking checks the 'perm' slot:
	      perm = 1L + if(need.sort) from@j[ii] else from@j,
	      Dim = d, Dimnames = from@Dimnames)
      })

setAs("matrix", "indMatrix", function(from) as(as(from, "nMatrix"), "indMatrix"))

setAs("indMatrix", "matrix", function(from) as(.ind2ngT(from), "matrix"))

setAs("sparseMatrix", "indMatrix", function(from)
    as(as(from, "nsparseMatrix"), "indMatrix"))

setMethod("is.na", signature(x = "indMatrix"), is.na_nsp)
setMethod("is.infinite", signature(x = "indMatrix"), is.na_nsp)
setMethod("is.finite", signature(x = "indMatrix"), allTrueMatrix)

setMethod("t", signature(x = "indMatrix"), function(x) t(.ind2ngT(x)))

setMethod("isSymmetric", signature(object = "indMatrix"),
	  function(object, ...) {
	      d <- dim(object)
	      if(d[1L] != d[2L])
		  FALSE
	      else ## using "!=" (instead of "==") as the former is typically sparse
		  !any(object != t(object))
	  })


setMethod("%*%", signature(x = "matrix", y = "indMatrix"),
	  function(x, y) x %*% as(y, "lMatrix"))
setMethod("%*%", signature(x = "Matrix", y = "indMatrix"),
	  function(x, y) x %*% as(y, "lMatrix"))

setMethod("%*%", signature(x = "indMatrix", y = "matrix"),
	  function(x, y) { mmultCheck(x,y); y[x@perm ,] })
setMethod("%*%", signature(x = "indMatrix", y = "Matrix"),
	  function(x, y) { mmultCheck(x,y); y[x@perm ,] })


setMethod("crossprod", signature(x = "indMatrix", y = "matrix"),
	  function(x, y) as(t(x), "lMatrix") %*% y)
setMethod("crossprod", signature(x = "indMatrix", y = "Matrix"),
	  function(x, y) as(t(x), "lMatrix") %*% y)
setMethod("crossprod", signature(x = "indMatrix", y = "indMatrix"),
	  function(x, y) {
	      mmultCheck(x,y, 2L)
              ## xy <- interaction(x@perm, y@perm)
              ## this is wrong if any of the columns in X or Y are empty because interaction()
              ## drops non-occuring levels from a non-factor. Explicitly defining a factor with
              ## levels 1:ncol(<indMatrix>) avoids that.
              nx <- x@Dim[2L]
              ny <- y@Dim[2L]
	      ## xy <- interaction(factor(x@perm, levels=seq_len(nx)),
	      ##   		   factor(y@perm, levels=seq_len(ny)))
	      ## much faster (notably for large x,y):
	      xy <- x@perm + nx*as.double(y@perm-1L)
	      Matrix(tabulate(xy, nbins = nx*ny), nrow = nx, ncol = ny,
		     dimnames = list(x@Dimnames[[2L]], y@Dimnames[[2L]]))
	  })

setMethod("tcrossprod", signature(x = "matrix", y = "indMatrix"),
	  function(x, y) { mmultCheck(x,y, 3L); x[, y@perm] })
setMethod("tcrossprod", signature(x = "Matrix", y = "indMatrix"),
	  function(x, y) { mmultCheck(x,y, 3L); x[, y@perm] })
setMethod("tcrossprod", signature(x = "indMatrix", y = "indMatrix"),
	  function(x, y) { mmultCheck(x,y, 3L); x[, y@perm] })

setMethod("crossprod", signature(x = "indMatrix", y = "missing"),
	  function(x, y=NULL) Diagonal(x = tabulate(x@perm, nbins=x@Dim[2L])))

setMethod("tcrossprod", signature(x = "indMatrix", y = "missing"),
	  function(x, y=NULL) x[,x@perm])


setMethod("kronecker", signature(X = "indMatrix", Y = "indMatrix"),
	  function (X, Y, FUN = "*", make.dimnames = FALSE, ...) {
	      if (FUN != "*") stop("kronecker method must use default 'FUN'")
	      if(any(as.double(X@Dim)*Y@Dim >= .Machine$integer.max))
		  stop("resulting matrix dimension would be too large")
	      ## Explicitly defining a factor with levels 1:ncol(.) avoids that
	      ## interaction() drops non-occuring levels when any of the
	      ## columns in X or Y are empty:
	      ## perm <-  as.integer(interaction(factor(rep(X@perm, each =Y@Dim[1]),
	      ##                                        levels=seq_len(X@Dim[2])),
	      ##                                 factor(rep.int(Y@perm, times=X@Dim[1]),
	      ##                                        levels=seq_len(Y@Dim[2])),
	      ##                                 lex.order=TRUE))
	      ## much faster (notably for large X, Y):
	      fX <- rep    (X@perm-1L, each  = Y@Dim[1])
	      fY <- rep.int(Y@perm-1L, times = X@Dim[1])
	      new("indMatrix", perm = 1L + fY + Y@Dim[2] * fX,
		  Dim = X@Dim*Y@Dim)
	  })


setMethod("[", signature(x = "indMatrix", i = "index", j = "missing",
			 drop = "logical"),
	  function (x, i, j, ..., drop)
      {
	  n <- length(newperm <- x@perm[i])
	  if(drop && n == 1) { ## -> logical unit vector
	      newperm == seq_len(x@Dim[2])
	  } else { ## stay matrix
	      if(!is.null((DN <- x@Dimnames)[[1]])) DN[[1]] <- DN[[1]][i]
	      new("indMatrix", perm = newperm,
		  Dim = c(n, x@Dim[2]), Dimnames = DN)
	  }
      })



.indMat.nosense <- function (x, i, j, ..., value)
    stop('replacing "indMatrix" entries is not allowed, as rarely sensible')
setReplaceMethod("[", signature(x = "indMatrix", i = "index"), .indMat.nosense)
setReplaceMethod("[", signature(x = "indMatrix", i = "missing", j = "index"),
		 .indMat.nosense) ##   explicit	 ^^^^^^^^^^^^ for disambiguation
setReplaceMethod("[", signature(x = "indMatrix", i = "missing", j = "missing"),
		 .indMat.nosense)


### rbind2() method: --> bind2.R
