#' Return the locations of the matching parentheses in a string.
#'
#' @param exp Input string: a single (vector of length 1).
#' @param loc The starting or ending locations of the parentheses
#' whose matching pair will be found.
#'
#' @details
#' \code{loc} must be positive and less than \code{nchar(exp)}.\cr
#' \code{substring(exp,loc,loc)} must be opening or closing parens.\cr
#' Otherwise, this function returns 0.
#'
#'
#' @return The location vecotr of length(loc) of the matching parens  or  0.
#'
#' @examples
#' # Input string
#' #                1111111111222222222233333333
#' #       1234567890123456789012345678901234567
#' exp <- "a+([a+b]*(c*{d+e}))-(f-g) +(((<()>)) "
#'
#' locbalpar( exp, 3 )
#' locbalpar( exp, 19 )
#' locbalpar( exp, c(4,8) )
#' locbalpar( exp, c(13,17) )
#' locbalpar( exp, c(31,34) )
#' locbalpar( exp, c(34,31, 3,19) )
#'
#' locbalpar( exp, regexpr( "\\(", exp ) )
#'
#' # No matching paren
#' locbalpar( exp, 28 )
#'
#'
#' # As long as substr(exp,n,1) is a paren, below should always be true.
#' n <- 10; n == locbalpar( exp, locbalpar( exp, n ) )
#' n <- 36; n == locbalpar( exp, locbalpar( exp, n ) )
#'
#' @export
#'

locbalpar <- function( exp, loc ){
 # find the location of the balanced paren
 # Shin-ichi Mayekawa
 # 20111208,20150620
 # remove attributes from loc: 20150823
 # returns 0 instead of NULL when error: 20170922
 # roxygen2 error fixed: 20191129dnc
 # roxygen2 error (\\\\( fixed: 20200609dnc
 # error check: 20221011dnc
 # vector loc: 20221011dnc
 #

 # error check
 if( length(exp) > 1 ){
  cat("\n\nerror1(locbalpar)"
      , "exp must be a string vector of length 1.\n\n")
  return(0)
 }
 nstr=nchar(exp)
 nloc=length(loc)
 if( any(loc > nstr)  |  any(loc <= 0) ){
  cat("\n\nerror1(locbalpar)"
      , "loc must be positive and less than nchar(exp).\n\n")
  return(0)
 }

 # remove attributes from loc by regexpr
 attributes(loc)$match.length=NULL
 attributes(loc)$useBytes=NULL


 # recursive call when loc is a vector
 if( nloc >= 2 ){
  res=numeric(nloc)
  for( i in 1:nloc ){
   res[i]=Recall( exp, loc[i] )
  }
  return( res )
 }


 ###########################################################################
 # simple case starts here.
 ###########################################################################

 # paren
 par=substr(exp,loc,loc)

 # matching paren
 lpar=c("(","{","[","<")
 rpar=c(")","}","]",">")
 if( any( par %in% lpar ) ){
  par2=rpar[par==lpar]
  rev=0
  exp1=substr(exp,loc,nstr)
 }
 else if( any( par %in% rpar ) ){
  par2=lpar[par==rpar]
  rev=1
  exp1=revchar(substr(exp,1,loc))
  loc0=loc
  loc=1
 }
 else{
  cat("\n\nwarning1(locbalpar) substr(exp,loc,loc) is not a paren.\n\n")
  return(0)
 }

 #Print(exp,exp1,printrowname=1)

 # individual character as a vector element
 vexp=unlist(strsplit(exp1,""))
 nstrs=length(vexp)
 # Print(vexp, nstrs,printrowname=1)

 # mark the location of par and par2
 locl=grep(par,vexp,fixed=1)
 locr=grep(par2,vexp,fixed=1)
 # Print(locl,locr)

 # count from the left
 npar=numeric(nstrs)
 npar[locl]=1; npar[locr]=-1
 parcount=cumsum(npar)
 # Print(npar,parcount,printrowname=1)

 # parcount = 0 indicates the location of matching par
 locbp=which(parcount==0)[1]
 if( !is.na(locbp) ) locbp=locbp+loc-1
 else locbp=0
 if( locbp > 0  &  rev == 1 ){
  locbp=loc0-locbp+1
 }
 # Print( locbp )

 attributes(locbp)=NULL
 return( locbp )

} # locbalpar

