#' Vertically Stack Data Frames /  Matrices with Different Sets of Variables
#'
#' @param ... data frames or matrices
#' @param sort = 1 to alphabetically sort the columns of the result \cr
#' Otherwise, the order of the original data frames will be kept.
#'
#' @details
#' Conceptually, \code{stack_df( df1, df2 )} is equivalent to
#' \preformatted{
#'  t( merge( cbind(t(df1),cn=colnames(df1)), cbind(t(df2),cn=colnames(df2))
#'          , by="cn", all=1 ) )
#' }.
#'
#' Using the data frames in the example section, the core part is:\cr
#' \preformatted{
#' res <- merge( data.frame(df1m,id=1:nrow(df1m))
#'             , data.frame(df2m,id=(1:nrow(df2m))+nrow(df1m)), all=1 )
#'  }
#' which vertically stacks data frames because the values of
#' the common variable id are unique.
#' \cr
#' Note that, since check.names=0 and optional=1 are used
#' when creating data frames,
#' the resulting data frame may not have valid column names.
#' \cr
#' \cr
#' When all the row names are unique, it will be used as the row names
#' of the resulting data frame. Otherwise, native \code{make.unique} will be
#' used to mark the duplicate row names which appends ".n" to the original names.
#'
#'
#' @examples
#' A <- demomat(5,4,3,cnroot="")
#' A1 <- A[,,1]; A2 <- A[,,2]; A3 <- A[1:2,,3]
#' colnames(A1) <- sub("4","v4",colnames(A1))
#' colnames(A2) <- sub("4","v4",colnames(A2))
#' colnames(A3) <- sub("4","v4",colnames(A3))
#' rownames(A1) <- paste("a1_",1:nrow(A1),sep="")
#' rownames(A2) <- paste("a2_",1:nrow(A2),sep="")
#' rownames(A3) <- paste("a3_",1:nrow(A3),sep="")
#' rownames(A3)[2] <- "a1_2"
#' A1m <- A1[,c(1,4)]; A2m <- A2[,c(1,3), drop=0]; A3m <- A3[,c(4,2,3)]
#' rownames(A2m) <- NULL
#'
#' print(A1m)
#' print(A2m)
#' print(A3m)
#'
#' res <- stack_df( A1m, A2m, A3m )
#' Print(res)
#'
#' # no factors allowed.
#' df1m <- as.data.frame(A1m, stringsAsFactors=0)
#' df2m <- as.data.frame(A2m, stringsAsFactors=0)
#' df2m[,2] <- paste("c",df2m[,2],sep="")
#' df3m <- as.data.frame(A3m, stringsAsFactors=0)
#'
#' res <- stack_df( df1m, df2m, df3m )
#' Print(df1m, df2m, df3m)
#' Print(res)
#' res2 <- stack_df( df2m, df3m, df1m )
#' Print(res2)
#' res3 <- stack_df( df1m, df2m, df3m, sort=1 )
#' Print(res3)
#'
#' # use of native Reduce function
#' res0 <- Reduce( stack_df, list(df1m, df2m, df3m) )
#'
#' res4 <- stack_df( A=demomat(3,2), b=letters[1:3] )
#' Print(res4)
#'
#'
#' @return
#' A data frame or a matrix
#'
#' @export
#'

stack_df <- function( ..., sort=0 ){
 # vertically stack several data frames
 # Shin-ichi Mayekawa
 # 20170921,23,26redcross
 # new_name -> order2: 20170927
 # check.names=optional=1: 20170928
 # all() added to ||: 20190605
 # roxygen2 example: 20200920
 # allmat: 20200921
 # make.names: 20200921
 # delete res=res[order(res$key___key___),]: 20200921
 # example of Reduce: 20201009
 #

 # analyze ...
 temp=analyze_three_dots( ... )
 name=temp$name
 val=temp$val
 n=length(name)

 allmat=unlist( lapply(val,is.matrix) )

 # error
 if( n == 0 ){
  cat("\nerror1:(stack_df) The argument is empty. \n")
  stop()
 }

 # stack vertically
 if( !is.data.frame(val[[1]]) ){
  val[[1]]=as.data.frame(val[[1]], stringsAsFactors=0, optional=1)
  if( is.null(colnames(val[[1]]))  || all(colnames(val[[1]])=="val[[1]]") )
   colnames(val[[1]])=name[[1]]
 }
 res=data.frame( val[[1]], key___key___=1:nrow(val[[1]])
                 , stringsAsFactors=0, check.names=0 )
 cname=colnames(val[[1]])
 if( is.null(rownames(val[[1]])) ) rn=1:nrow(val[[1]])
 else rn=rownames(val[[1]])
 if( n >= 2 ){
  for( i in 2:n ){
   if( !is.data.frame(val[[i]]) ){
    val[[i]]=as.data.frame(val[[i]], stringsAsFactors=0, optional=1)
    if( is.null(colnames(val[[i]])) || all(colnames(val[[i]])=="val[[i]]") )
     colnames(val[[i]])=name[[i]]
   }
   val[[i]]=data.frame( val[[i]], key___key___=(1:nrow(val[[i]]))+nrow(res)
                        , stringsAsFactors=0, check.names=0 )
   res=merge( res, val[[i]], all=1 )
   cname=unique( c(cname,colnames(val[[i]])) )
   if( is.null(rownames(val[[i]])) ) rn=c(rn,paste(i,1:nrow(val[[i]]),sep="_"))
   else rn=c(rn,rownames(val[[i]]))
  }
 }

 # sort by key___key___ and remove key___key___.
 res=res[order(res$key___key___),]
 res=res[,colnames(res)!="key___key___",drop=0]

 if( length(unique(rn)) == nrow(res) ) rownames(res)=rn
 else rownames(res)=make.unique(rn)

 # variable names
 if( sort >= 1 ){
  # sort colnames(res)
  od=order(colnames(res))
 }
 else{
  # sort the colnames(res) according to the original order
  od=order2( colnames(res), cname[cname!="key___key___"] )
 }
 res=res[,od, drop=0]

 if( all(allmat) ) res=as.matrix(res)

 return( res )

} # end of stack_df


