#' Calculation of sample quantiles by interpolating
#' sample cumulative distribution function.
#'
#' @param x numeric vector
#' @param freq frequency associated with x \cr
#'  (x,freq) is the frequency table
#' @param probs  numeric vector of probabilities with values in [0,1]
#' @param method = "constant" | "linear" | "spline",  interpolation method
#' @param cdf cdf at x. This has priority over freq.
#'
#' @details
#'  If freq=1  and method="constant" (default),
#'  the result is equivalent to native quantile function with type=1.
#'
#'  Given the frequency table of data, (x, freq), cdf will be calculated as:
#'  \preformatted{
#'  cdf = sumsum(freq)/sum(freq)
#'  }
#'
#'
#' @return
#'  a vector of quantiles
#'
#' @examples
#'
#' # generate data
#' set.seed(1701)
#' n <- 100
#' x <- round(10*rnorm(n),0)
#'
#' # by native quantile func
#' probs <- c(0.25, 0.5, 0.75)
#' res1 <- quantile(x,probs, type=1)
#'
#' # by Quantile
#' res2 <- Quantile( x,1, probs=probs )
#'
#' # frequency table
#' freq <- table(x)
#' xc <- as.numeric(names(freq))
#'
#' # from frequency table
#' res3 <- Quantile( xc,freq, probs=probs )
#'
#' # comparison
#' Print(probs,res1,res2, res3)
#'
#'
#'
#' #
#' # Effect of the sample size
#' #
#' # When small, Quantile with linear method is equal to quantile with type=4.
#' #
#' #
#'
#' getQ <- function( x, prob ){
#'  res1 <- NULL;
#'  for( type in 1:9 ) res1 <- cbind(res1,quantile(x,probs, type=type))
#'  colnames(res1) <- paste("t",1:9,sep="")
#'  return( res1 )
#' }
#'
#' # generate data
#' set.seed(1701)
#' n <- 5
#' x <- round(10*rnorm(n),0)
#' probs <- c(0.10, 0.25, 0.5, 0.75, 0.90)
#'
#' # by native quantile func
#' res1 <- getQ(x,probs)
#' # by Quantile
#' res2 <- Quantile( x,1, probs=probs, method="linear" )
#' # comparison
#' Print(res1, fmt="8.4")
#' Print(probs,res2, fmt="8.4")
#'
#'
#' # generate data
#' set.seed(1701)
#' n <- 50
#' x <- round(10*rnorm(n),0)
#' probs <- c(0.10, 0.25, 0.5, 0.75, 0.90)
#'
#' # by native quantile func
#' res1 <- getQ(x,probs)
#' # by Quantile
#' res2 <- Quantile( x,1, probs=probs, method="linear" )
#' # comparison
#' Print(res1, fmt="8.4")
#' Print(probs,res2, fmt="8.4")
#'
#' # comparison
#' Print(res1)
#' Print(probs,res2, res3)
#'
#'
#' # generate data
#' set.seed(1701)
#' n <- 500
#' x <- round(10*rnorm(n),0)
#' probs <- c(0.10, 0.25, 0.5, 0.75, 0.90)
#'
#' # by native quantile func
#' res1 <- getQ(x,probs)
#' # by Quantile
#' res2 <- Quantile( x,1, probs=probs, method="linear" )
#' # comparison
#' Print(res1, fmt="8.4")
#' Print(probs,res2, fmt="8.4")
#'
#' # comparison
#' Print(res1)
#' Print(probs,res2, res3)
#'
#'
#' @export
#'

Quantile <- function( x, freq=NULL, probs=seq(0,1,0.25), method="constant"
                      , cdf=NULL ){
 # find the 100 p % quantiles from (x,freq) or (x,cdf)
 # by interpolating (cdf, x) at cdf=p.
 # Shin-ichi Mayekawa
 # 20211227
 # in lazy.tools: 20220620
 # comment and examples: 20230720
 #


 # Args:
 #  method = "constant" gives the lowest value as the quantile.
 #

 if( is.null(freq) & is.null(cdf) ){
  stop( "error1:(Quantile) (x,freq) or (x,cdf) must be given.")
 }

 # freq from cdf
 if( !is.null(cdf) ){
  freq=cumsuminv(cdf)
  vals=x
 }
 else{
  # take care of freq
  lx=length(x); lf=length(freq)
  if( lf == 1 ) freq=rep(freq,lx)
  else if( lf > lx ) freq=freq[1:lx]
  else if( lf < lx)  freq=c(freq,rep(freq[lf],lx-lf))
  freq[is.na(freq)]=0   # Better than specifying na.pass to xtabs.

  # use xtabs to collect duplicated x values
  temp=xtabs( freq~x )
  vals=as.numeric(names(temp))
  cdf=cumsum(temp)/sum(temp)
 }

 # here, (vals, freq, cdf) is ready.

 # find the value of x at cdf=probs by interpolating (cdf,vals).
 # method="constant" with f=1 corresponds to native quantile with type=1.
 if( method %in% c("linear", "constant") ){
  # linear
  qt=approx( cdf, vals, xout=probs, method=method
             , yleft=min(x), yright=max(x), f=1, ties="ordered")$y
 }
 else{
  # spline
  if( method == "spline" ) method="natural"
  qt=spline( cdf, vals, xout=probs, method=method
             , ties="mean")$y
 }

 return( qt )

} # end of Quantile


