#' Recode the Values of obj from "from" to "to".
#'
#' @param obj a vector, matrix or list of those.
#' @param from a vector of old values
#' @param to   a vector or new values
#' @param ft   the conversion table data frame
#' @param method = 1 to use \code{merge}, \cr
#' = 2 to use R style teble look-up using names attribute of a vector.
#' @param useNA = 1 to mark out-of-range elements
#' @param debug = 1 to print intermediate result.
#'
#' @details
#'  When \code{ft} is not given,
#'  \code{cbind(from,to)}  makes the conversion table \code{ft}. \cr
#'  \code{ft} has priority over from and to.
#'
#'  The core of this function is:\cr\cr
#'  If \code{method==1} \cr
#'  \code{New <- merge( cbind(from,to), data.frame(from=c(obj))
#'  , by="from", sort=FALSE, all.y=TRUE )} \cr
#'  If \code{method==2} \cr
#'  \code{names(to) <- from; New <- to[as.character(c(obj))]} \cr
#'
#'  Method 2 is faster than method 1. \cr
#'
#'  If the conversion table is not large enough,
#'  if \code{method=2} and \code{useNA=1},
#'  out-of-range elements will be set to NAs,
#'  otherwise, the original values will not  be changed.
#'
#'  When the types of \code{obj} and \code{from} are different,
#'  they will be converted to character.
#'
#'  \code{lazy.tools::new_name} will be deprecated.
#'
#' @return
#'  A matrix or vector depending on the \code{obj}.
#'  When \code{obj} is a list, a list will be returned.
#'
#' @examples
#' n <- 10
#' n1 <- n-2
#' obj <- cbind( letters[1:n], rev( letters[1:n] ) )
#' obj[1,1] <- NA; obj[3,2] <- NA
#' rownames(obj) <- paste("RR",1:n,sep="")
#' colnames(obj) <- paste("CC",1:ncol(obj),sep="")
#' from <- letters[1:n1]
#' to <- LETTERS[1:n1]
#' # to <- 1:n1
#' res <- recode( obj, from, to )
#' Print(obj,res,from,to)
#' res2 <- recode( obj, from, to, useNA=1 )
#' Print(obj,res2,from,to)
#'
#'
#' n=10
#' n1=n-2
#' obj=cbind( 1:n, n:1 )
#' rownames(obj)=paste("RR",1:n,sep="")
#' colnames(obj)=paste("CC",1:ncol(obj),sep="")
#' from=1:n1
#' to=from^2
#' res=recode( obj, from, to, method=2 )
#' Print(obj,res, from, to)
#'
#'
#' n <- 10
#' n1 <- n-2
#' obj <- cbind( 1:n, n:1 )        # This is numeric.
#' rownames(obj) <- paste("RR",1:n,sep="")
#' colnames(obj) <- paste("CC",1:ncol(obj),sep="")
#' from <- as.character(1:n1)      # This is character.
#' to <- LETTERS[1:n1]
#' res <- recode( obj, from, to )
#' Print(obj,res, from, to)
#'
#' @export
#'

recode <- function( obj, from=NULL, to=NULL, ft=NULL, method=2
                    , useNA=0, debug=0 ){
 # recode the values of obj from from to to
 # Shin-ichi Mayekawa
 # 20221022
 # warning: 20221026
 # merge2 -> merge: 20221026
 # debug: 20221103
 # useNA: 20221222
 #

 # check
 if(  !is.null(ft) && (is.null(from) || is.null(to)) ){
  cat("\nerror1(recode): from and to  or  ft must be given.\n")
  stop()
 }
 lf=length(from)
 lt=length(to)
 if( lf != lt ){
   cat("\nerror1(recode): The lengths of from and to must be the same.\n")
   stop()
 }


 ###########################################################
 # if list, call recode recursively
 #
 if( is.list(obj) ){
  lname=names(obj)
  res=lapply( obj, recode, from, to, ft )
  names(res)=lname
  return( res )
 }
 ###########################################################

  # shape of obj
 isvec=is.vector(obj)
 ismat=is.matrix(obj)
 if( isvec ){
  nr=length(obj); nc=1
  dimname=list( names(obj), NULL )
 }
 if( ismat ){
  nr=nrow(obj); nc=ncol(obj)
  dimname=dimnames(obj)
 }


 if( method == 1 ){

 # conversion table
 if( is.null(ft) ){
   ft=data.frame( old=from, new=to )
 }
 if( debug ){ cat("ft\n"); str(ft) }

 # reshape obj
 vecobj=c(obj)
 lobj=length(vecobj)
 vecobj=data.frame( old=vecobj, ..id..=1:lobj )
 if( debug ){ cat("vec(obj)\n"); str(vecobj) }

 # uo=unique(c(vecobj))
 # if( !all(uo %in% from) ){
 #  cat("\nwarning1(recode): Somve values of obj are missing from 'from'\n")
 # }

 # merge
 New=merge( ft, vecobj, by="old", sort=FALSE, all.y=TRUE )
 if( debug ){ cat("merged\n"); str(New) }
 New=New[order(New$..id..), "new"]

 }
 else{

  if( !is.null(ft) ){
   from=ft[,1]; to=ft[,2]
  }
  names(to)=from
  New=to[as.character(c(obj))]

  if(0){
    # Above New already has NAs for the out-of-range elements.
    # So we do not need below
    if( useNA == 1 ){
      uf=unique(from)
      uo=unique(c(obj))
      locd=which( !(uo %in% uf) )
      loco=which( c(obj) %in% uo[locd] )
      Print(uo,uf,locd, loco)
      New[loco]=NA
    }
  }
 }

 # back to the original shape
 New=matrix( New, nr, nc )
 dimnames(New)=dimname
 if( isvec ){
   New=c(New)
   names(New)=dimname[[1]]
 }

 if( useNA == 0 ){
   # copy the original values to NA.
   New[is.na(New)]=obj[is.na(New)]
 }


 return(New)

} # recode


