#' Return the locations of the matching parentheses
#'
#' @param expr Input string vector.
#' @param loc The starting or ending locations of the parentheses
#' in \code{expr}. (See the details and examples below.)
#' @param line line number
#' @param escape = 0 not to escape parens with double backslashes.
#' @param debug = 1 to print intermediate results
#'
#' @details
#' This is an expanded version of \code{lazy.tools::locbalpar}
#' which can handle a string vector input. \cr
#'
#' When \code{expr} is a scalar string, better use \code{lazy.tools::locbalpar}.
#' \code{loc} must be positive and less than \code{sum(nchar(exp))}.
#'
#' When \code{expr} is a string vector,
#' assuming that there are no length 0 elements of expr,
#' the location of the parens can be specified as follows:\cr
#' Method 1 (with \code{line} and \code{loc})\cr
#' Suppose that the location of the paren of interest is in location
#' (chracter) LOC at line LINE. Then the following code will find
#' the location of the matching paren. \cr
#' locbalpar2( expr, LOC, LINE ) \cr
#' Note that \code{loc} and \code{line} can be vectors of the same length.\cr
#'
#' Method 2 (with \code{loc})\cr
#' In this case, \code{loc} must be specified so that \cr
#' \code{unlist(strsplit(expr,""))[loc]} \cr
#' contains opening or closing parens.
#'
#' This function returns \code{line=0, locbp=0}.
#'
#' The following characters are treated as parentheses: \cr
#' \code{"(", "{", "[", "<", ")", "}", "]", ">"}
#'
#'
#' @examples
#'
#' # When counting characters, count the length 0 character, "", as 1.
#' # For example, consider the example below where
#' # expr1 has length 0 character, "", at lines 2 and 4 and
#' # expr2 has length 1 blank character, " ",  at lines 2 and 4.
#' # The locations of parens for expr1 must be counted from expr2 as: \cr
#' #  \code{unlist(strsplit(expr2,""))}\cr
#' # The two results below should be the same.
#' expr1 <- c( "1(", "", "4)(", "", "89)" )
#' expr2 <- c( "1(", " ", "4)(", " ", "89)" )
#' printm( expr1, expr2 )
#' exprc1 <- unlist(strsplit(expr1,""))
#' exprc2 <- unlist(strsplit(expr2,""))
#' printm( exprc1, exprc2 )
#' locbalpar2( expr2, c(2,3,2), c(1,3,3) )
#' locbalpar2( expr1, c(2,6,5) )
#' locbalpar2( expr2, c(2,6,5) )
#'
#' # escape or not
#' locbalpar2( "a(b\\(d)",2 )
#' locbalpar2( "a(b\\(d)",2, escape=0 )
#'
#'
#' # Find the matching closing brace.
#' expr <- c(
#'  "locbalpar2 <- function( expr, loc ){",
#'  " # find the location of the balanced paren",
#'  " # use standard version when expr is a string",
#'  "",
#'  " if( !is.vector(expr) ){",
#'  "  return( locbalpar( expr, loc ) )",
#'  " }",
#'  "",
#'  "} # end of locbalpar2"
#' )
#' locbalpar2( expr, regexpr("{", expr, fixed=1 )[1] )
#' 
#' # find all "(" and their matching ")".
#' locs <- gregexpr("(", expr,  fixed=TRUE )
#' LL <- NULL
#' for( i in 1:length(locs) ){
#'  if( all( locs[[i]] < 0 ) ) next
#'  LL <- rbind(LL,cbind(i,locs[[i]]))
#' }
#' locbalpar2(expr,LL[,2],LL[,1])
#'
#'
#'
#' @return
#' A list of \cr
#' \code{line}   the line number. \cr
#' \code{locbp}  the locations of matching parens in the line.
#'
#'
#' @export
#'

locbalpar2 <- function( expr, loc, line=1, escape=1, debug=0 ){
  # find the location of the balanced paren
  # Shin-ichi Mayekawa
  # vector exp: 20230806
  # new version: 20250304,05
  # escape: 20250822
  # old version recovered: 20250823
  # line option: 20250823
  #

 #
 # See locbalpar2_4.R for new version in which the 0-length character
 # is calculated as 0. #
 #

 markparens <- function( expr ){
  # mark escaped parens with the string of the same # of characters
  expr=gsub( "\\(", ".!p", expr, fixed=TRUE )
  expr=gsub( "\\)", "..p", expr, fixed=TRUE )
  expr=gsub( "\\{", ".!c", expr, fixed=TRUE ) # curly brackets
  expr=gsub( "\\}", "..c", expr, fixed=TRUE )
  expr=gsub( "\\[", ".!s", expr, fixed=TRUE ) # square brackets
  expr=gsub( "\\]", "..s", expr, fixed=TRUE )
  expr=gsub( "\\<", ".!i", expr, fixed=TRUE ) # inequality
  expr=gsub( "\\>", "..i", expr, fixed=TRUE )
  return( expr )
 } # end of markparens

 unmarkparens <- function( expr ){
  # mark escaped parens: Not Used
  expr=gsub( ".!p", "\\(", expr, fixed=TRUE )
  expr=gsub( "..p", "\\)", expr, fixed=TRUE )
  expr=gsub( ".!c", "\\{", expr, fixed=TRUE ) # curly brackets
  expr=gsub( "..c", "\\}", expr, fixed=TRUE )
  expr=gsub( ".!s", "\\[", expr, fixed=TRUE ) # square brackets
  expr=gsub( "..s", "\\]", expr, fixed=TRUE )
  expr=gsub( ".!i", "\\<", expr, fixed=TRUE ) # inequality
  expr=gsub( "..i", "\\>", expr, fixed=TRUE )
  return( expr )
 } # end of unmarkparens


 if( escape ) expr=markparens( expr )

 # use standard version when expr is a string
  if( !is.vector(expr) ){
    expr=locbalpar( expr, loc )
    if( escape ) expr=unmarkparens( expr )
    return( expr )
  }


  # const
  # # of rows of expr
  nexpr=length(expr)
  # # of characters per row:  nchar("")=0
  nc=nchar(expr)

  # replace "" by "&"
  loczero=which(nc==0)
  if( length(loczero) > 0 ){
   expr[loczero]="&"
  }
  exprs=unlist(strsplit(expr,""))
  nc1=length(exprs)

  if( any( is.na(loc) )  ||  any( loc > nc1) ){
   cat("\nerror1:(balpar2) loc out of range.\n")
   printm(loc,nc1)
   stop()
  }

  if( debug ){
   printm(expr, expr1,loc)
   printm(unlist(strsplit(expr,"")),unlist(strsplit(expr1,"")),index)
  }



  # # of characters after replacement
  nc[loczero]=1
  cnc=cumsum(nc)
  cnc0=c(0,cnc)
  brks=c(cnc0, 999999)

  # add cumulative # of characters in the lines to loc
   if( debug ) printm(cn0,loc,line)
   loc=loc+cnc0[line]
   if( debug ) printm("after line", loc,line)


  # concatenate all elements
  expr2=paste( expr, collapse="" )

  # use standard version
  locbp=locbalpar( expr2, loc )

  # recover the line info using breaks
  line=as.numeric( cut( locbp, brks, labels=1:(nexpr+1) ) )

  # relative locations in each line
  locbp=locbp-cnc0[line]
  line[is.na(locbp)]=0
  locbp[is.na(locbp)]=0

  # if( escape ) expr=unmarkparens( expr )

  res=named_list(line,locbp)
  return( res )

} # end of locbalpar2



