#' Sort Matrix/Data Frame According to the Pattern of Missing Elements
#'
#' @param X a data frame or matrix to be sorted.
#' @param misslast = 1 to treat NA as the largest,
#' = 1 to treat NA as the smallest.
#' @param nobs = 1 to sort by the # of observed elements first \cr
#' = -1 to sort by the descending # of observed elements first \cr
#' = 0 not to pay any attention to the # of missing elements.
#'
#' @examples
#' set.seed(17029)
#' nn=50
#' dataframe=0
#'
#' if( dataframe ){
#'  x=sample(0:9,nn,replace=1)
#'  x[runif(nn) < 0.5]=NA
#'  x2=sample(letters[1:10],nn,replace=1)
#'  x2[runif(nn) < 0.5]=NA
#'  x3=sample(LETTERS[1:10],nn,replace=1)
#'  x3[runif(nn) < 0.5]=NA
#'  X=data.frame(x,x2,x3, stringsAsFactors=0)
#' } else{
#'  x=sample(0:9,nn,replace=1)
#'  x[runif(nn) < 0.3]=NA
#'  x2=sample(1:10,nn,replace=1)
#'  x2[runif(nn) < 0.3]=NA
#'  x3=sample(1:10,nn,replace=1)
#'  x3[runif(nn) < 0.3]=NA
#'  X=cbind(x,x2,x3)
#' }
#' res=sort_missing_data( X, misslast=1, nobs=-1 )
#' Xs=res$sorted_data
#' freqs=res$freq
#' Nobss=res$Nobs
#' od=res$od
#'
#' # Nobsss should be equal to Nobs, Xss, to Xs
#' Nobsss=ncol(X)-apply(Xs,1,function(x) sum(is.na(x)))
#' Xss=X[od,]
#' pat=rep(1:length(freqs),freqs)
#' Print(Xs,Xss,Nobss, pat,od)
#'
#' misspat=Xs[cumsum(res$freq),]
#' nobs=ncol(misspat)-apply(misspat,1,function(x) sum(is.na(x)))
#' Print(misspat,nobs)
#'
#' mands( Xs, by=pat )
#'
#'
#' @return
#' A list of (sorted_data, freq, Nobs, class, od) \cr
#' where sorted_data is of the same size as X, \cr
#' freq is the vector consisting of the # of observatons for each pattern, \cr
#' Nobs is the vector of length nrow(X)
#' containing the # of observed elements for each row of X.\cr
#' class is the interger in [1,length(freq)] indicating the classification
#' of each obs according to the missing pattern. \cr
#' od is the order to be used to sort X as X_sorted=X[od,].
#'
#'
#' @export
#'
#'

sort_missing_data <- function ( X, misslast=1, nobs=0 ){
 # sort data frame with missing data
 # Shin-ichi Mayekawa
 # 20190120,21
 # return od: 20190125dnc,26
 # sort_missing_data0 simplifed: 20200925
 # return class: 20200927
 # roxygen2: 20230117dnc
 #

 sort_missing_data0 <- function ( X, misslast=1 ){
  # sort data frame with missing data
  # The original version is from mysort function of mvnmle package
  # Shin-ichi Mayekawa
  # 20190120
  # return od: 20190125dnc
  # simplified: 20200925
  #

  #
  # This function returns:
  #   sorted_data, freq, and od
  #

  if( misslast != 1 ) misslast=0

  if( all(!is.na(X)) ){
   return(list(sorted_data=X, freq=nrow(X), od=1:nrow(X)))
  }
  if( all(is.na(X)) ){
   return(list(sorted_data=X, freq=nrow(X), od=1:nrow(X)))
  }

  # The idea of representing the missing patterns by numbers is found in
  #  "mysort" function of "mvnmle" package.
  #
  bin <- ifelse( is.na(X), as.integer(misslast), as.integer(1-misslast) )
  dec <- bin %*% as.integer(2^((ncol(X)-1):0))
  od=order(dec)
  sorted <- X[od,]
  dec <- dec[od]

  return( list(sorted_data=sorted, freq=as.vector(table(dec)), od=od) )

 } # end of sort_missing_data0



 if( is.vector(X) ){
  nv=1
  X=matrix(X,,nv)
 }
 else nv=ncol(X)

 if( nobs != 0 ){

  Nobs=nv-apply(X,1,function(X) sum(is.na(X)))
  # for each of by group
  # by function does not give us grouping info(which obs belongs to which group)
  uNobs=unique(Nobs)
  ngroup=length(uNobs)
  if( nobs > 0 ){
   uNobs=sort( uNobs )
  }
  else{
   uNobs=sort( uNobs, decreasing=TRUE )
  }
  res=NULL
  for( k in 1:ngroup ){
   loc=which( Nobs == uNobs[k] )
   resk=sort_missing_data0( X[loc,,drop=0], misslast=misslast )
   resk$od=loc[resk$od]
   res=append( res, list(resk) )
  } # k loop for group

  # res above is a list of lists: (sorted_data, freq, od)
  # sorted data and freq: horizontally stack the sorted data for each Nobs.
  rr=lapply( res, function(X) as.data.frame(X[[1]]) )
  sorted_data=Reduce( rbind, rr )
  Nobs=nv-apply(sorted_data,1,function(X) sum(is.na(X)))
  if( is.matrix(X) ) sorted_data=as.matrix(sorted_data)
  rr=lapply( res, function(X) as.vector(X[[2]]) )
  freq=Reduce( c, rr )
  rr=lapply( res, function(X) as.vector(X[[3]]) )
  od=Reduce( c, rr )
  class=rep(1:length(freq),freq)
  return( list( sorted_data=sorted_data, freq=freq, Nobs=Nobs
                , class=class, od=od ) )

 }
 else{

  res=sort_missing_data0( X, misslast=misslast )
  Nobs=nv-apply(res$sorted_data,1,function(X) sum(is.na(X)))
  class=rep(1:length(res$freq),res$freq)
  return( append( res[-3]
                  , list(Nobs=Nobs, class=class, od=res[[3]])) )

 }

} # end of sort_missing_data


