#' Principal Component Analysis of Y with possible missing elements
#'
#' @param Y matrix of n x p
#' @param ndim # of dimensions: Must be less than or equal to p.
#' @param estmu = 0 not to estimate mu  (No standardization of Y)
#' @param maxiter Maximum # of iterations for missing elements
#' @param eps criterion for convergence
#' @param init = 0 to use random number as the first estimate of missing\cr
#' = 1 to use the gramd mean \cr
#' = 2 to use the column mean
#' @param print = 1 to print the result
#' @details This function minimizes the following RSS \cr
#'      RSS = tr( ( Y - 1 mu' - F A')'( Y - 1 mu' - F A') ) \cr
#' where mu is the p x 1 vector, \cr
#' F is the n x ndim orthonormal score matrix, \cr
#' and A is the p x ndim matrix of loadings.
#' @return A list of:\cr
#' Yhat=1mu'-FA', F, A, mu, iter, eps, rmse=sqrt(RSS)
#' @examples
#' n <- 20; np <- 5;  pmiss <- 0.1
#' Y <- matrix( rnorm(n*np),n )
#' Y[which(matrix(runif(n*np),n,np) < pmiss)]=NA
#' princ(Y,2, print=10, estmu=1, init=1)
#' @export
#'

princ <- function( Y, ndim=1, estmu=1, maxiter=1000, eps=1e-8, init=1
                   , print=1 ){
 # pca with missing data
 # Shin-ichi Mayekawa
 # 120227,29,30
 # renamed from pcam to princ: 120302
 # return rmse: 120926
 # Print format: 130126
 # init: 130127
 # error check: 20150605
 # ndim==np: 20161003
 #
 # This program minimizes
 #  RSS = tr( ( Y - 1 mu' - F A')'( Y - 1 mu' - F A') )
 # with respect to mu, F and A and missing elements of Y.
 # F will be column centerd and F'F=n I.
 # A will satisry A'A =  diag .
 #
 #
 # Args:
 #   Y       n x np matrix
 #   ndim    # of dimensions. i.e., # of columns of F and A
 #   estmu   = 1 to estimate mu (np x 1)
 #   init    initial value of the missing elements of Y
 #           = 0 to use random numbers
 #           = 1 to use the grand mean
 #           = 2 to use the column means
 #
 #

 # error check
 if( !is.matrix(Y) ){
  cat("\n\nerror1:(princ) Input must be a matrix.\n\n")
  return()
 }

 # constants
 n=nrow(Y); np=ncol(Y)
 sqrtn=sqrt(n)
 one=matrix(1,n,1)

 if( np < ndim ){
  cat("\n\nerror1:(princ) ndim must be less than or equal to ncol(Y).\n\n")
  return()
 }

 # location of missing values
 locmiss=is.na(Y)
 pmiss=sum(locmiss)/(n*np)

 if( init == 1 ){
  # missing values replaced by grand mean
  Y[locmiss]=mean(Y[!locmiss])
 }
 else if( init == 2 ){
  for( j in 1:np ){
   Y[is.na(Y[,j]),j]=mean(Y[,j],na.rm=1)
  }
 }
 else{
  Y[locmiss]=rnorm(sum(locmiss))
 }


 rmsep=999999
 conv=0
 mu=matrix(0,np)

 for( llll in 1:maxiter ){

  if( estmu == 1 ){
   # estimate mu
   mu=matrix(colMeans(Y),np,1)
  }

  # column centered Y
  Z=Y-one%*%t(mu)

  # svd
  UDV=svd(Z,ndim,ndim)

  # rescale to get F and A
  F=matrix(UDV$u,n)*sqrtn
  A=matrix(UDV$v,np)/sqrtn
  if( ndim == 1 ) D=UDV$d[1]
  else D=diag(UDV$d[1:ndim])
  A=A%*%D

  # model value and rss
  Yhat=F%*%t(A)+one%*%t(mu)
  rmse=sqrt( sum((Y-Yhat)^2)/(n*np) )
  rmseimp=rmsep-rmse

  if( print >= 3 )
   Print(llll, rmsep, rmse, rmseimp, fmt="i3 f10.7")
  if( rmseimp <= eps ){
   conv=1
   break
  }

  # next iteration
  rmsep=rmse

  # replace the missing Y by model value
  Y[locmiss]=Yhat[locmiss]

 } # of llll loop

 if( conv != 1 & print >= 0 ){
  cat("\n\nwarning:(princ) Iteration did not converge.\n\n")
 }


 if( print > 0 ){
  cat("\n\nPrincipal Component Analysis\n\n")
  cat(" # of rows =", n, ", # of columns =", np
      , ",  # of dimensions =", ndim,"\n")
  cat(" estimation of mu =", estmu, "\n")
  cat("  percentage of missing elements in Y =", 100*pmiss, "%\n")
  cat(" method for the initial values =", init,"\n")
  cat(" max # of iterations =", maxiter, "  with eps =",eps,"\n")
  cat(" iterations required to converge =", llll, " with rmse =", rmse, "\n")
  Print( mu, F, A, fmt="8.4" )
 }
 if( print >= 2 ){
  Print(t(F)%*%F, t(A)%*%A, fmt="8.4")
 }

 return( list(Yhat=Yhat, F=F,A=A,mu=mu, iter=llll, eps=eps, rmse=rmse) )

} # end of princ



