#' Remove Redundant Parentheses
#' (very very primitive version)
#'
#' @param exp expression vector or matrix
#' @param method 1, 2 or 3 \cr
#' If exp contains any logical operator, method=1 will be used.\cr
#' @param loc the location of the paren to be removed. \cr
#' Use with caution when the paren is preceded by - sign,
#' or exp is a vector or matrix.
#' @param debug = 1 to print intermediate result.
#'
#' @details
#'
#' When loc > 0 is given, those parentheses located at the loc-th position
#' of the elements of exp will be removed.\cr
#' No signs will be distributed: \cr
#' \code{remove_paren("-(a+b)",loc=2)} will result in \code{-a+b}.
#' \cr
#'
#' First,  (+....)  will be replaced by  (...) .\cr
#' When method = 1, a pair of parentheses which encloses a single term
#' w/o divisions will be removed. \cr
#' When method = 2, a pair of parentheses which is not preceded by [*/-]
#' and not trailed by [*/] will be removed. \cr
#' When method = 3, patterns like  -(-a) or -(-(a)) will be processed.
#' \cr\cr
#' Use Expand to distribute multiplicative factors.
#'
#' \cr\cr
#' \code{method=2} may return wrong result: \cr
#' \code{remove_paren("-((a+b))", method=2)} becomes \code{-a+b}.
#' \cr
#' \cr
#'
#' @return
#' A vector or matrix of the same size as the input
#'
#' @examples
#' remove_paren( c("(x)", "-(x)", "(x+y)", "-(x+y)") )
#' remove_paren( c("a*(x)*b", "a*(x+y)*b", "(-x)*a", "-(x+y)*a") )
#' remove_paren( c("a%*%(x)%*%b", "a%*%(x+y)%*%b", "(-x)%*%a", "-(x+y)%*%a") )
#' remove_paren( c("a%@%(x)%*%b", "a%@%(x+y)%@%b", "(-x)%@%a", "-(x+y)%@%a") )
#'
#' exp <- "((a)) - ((a)+f(b)) + (a-b) - (x-gg(y)) + (((z+1)))"
#' remove_paren( exp )
#'
#' exp2 <- c(exp, "(a+b)*(c)-d+1")
#' remove_paren( exp2 )
#'
#' ( temp <- And(Or("a==A","b>0"),"c") )
#' remove_paren(temp)
#'
#' remove_paren("!(!(a))")
#' remove_paren("!(!(a))",loc=4)
#' remove_paren("!( !(a) )",loc=9)
#'
#' @export
#'

remove_paren <- function( exp, method=1:9, loc=0, debug=0 ){
 # remove redundant parentheses
 # Shin-ichi Mayekawa
 # 20150823,24
 # (+....) -> (...):  20150905
 # bugfix: 20150906,07
 # regexpr()[[1]]: 20150907
 # 1/(a): 20150907
 # parens preceded by alpha: 20150913
 # %*%: 20150915
 # special operator: 20150922
 # (Use remove_paren6.R if this version does not work.)
 #
 # moved from symbolic to tools: 20171007
 # remove double matching paren: 20171008
 # new method 1 with  &|!: 20171008
 # loc: 20171010dnc
 # roxgen2 wrong result: 20171024dnc
 # bug fix for special ops;  ` added: 20200520
 # lv() removed from examples for version 4.2: 20230625
 #

 # mark
 MARK=";"

 # is exp a scalar?
 if( is.vector(exp) ) lenexp=length(exp)
 else lenexp=nrow(exp)*ncol(exp)

 # if not scalar, use recursive call
 if( lenexp > 1  |  is.matrix(exp) ){
  exp[]=unlist(lapply( exp, remove_paren, method=method, loc=loc, debug=debug ))
  return( exp )
 }


 ############# main starts here. #####################################


 #loc
 if( loc >= 1  &&  substr(exp,loc,loc) %in% c("(",")") ){
  loc2=locbalpar(exp,loc)
  if( loc2 > 0 ){
   substr(exp,loc,loc)=MARK
   substr(exp,loc2,loc2)=MARK
   exp=gsub( MARK,"", exp )
   exp=gsub( " ", "", exp )
   exp=gsub( "+-", "-", exp, fixed=1 )
   exp=gsub( "++", "+", exp, fixed=1 )
   exp=gsub("!#", "#", exp, fixed=1)
   exp=gsub("!!", "", exp, fixed=1) ########### 20171010dnc
   return( exp )
  }
 }


 # compress exp
 exp00=exp
 exp=gsub( " ", "", exp )


 # remove leading plus sign:   (+....) -> (...)  20150905
 # must escape ( and +
 exp=gsub( "\\(\\+", "\\(", exp )

 # find the location of left parens
 temp=unlist(strsplit(exp,""))
 loc1=which( unlist( gregexpr( "\\(", temp ) ) == 1 )

 np=length(loc1)

 if( debug ) Print(exp, np,loc1)

 # Is there any paren?
 if( np == 0 ) return(exp00)


 # Omit those parens preceded by alnum, i.e.,  function calls
 rm=NULL
 for( i in 1:np ){
  if( loc1[i] == 1 ) next
  if( regexpr( "[[:alnum:]_.]", temp[loc1[i]-1] )[[1]] > 0 )
   rm=c(rm,i)
 }
 if( length(rm) > 0 ) loc1=loc1[-rm]
 np=length(loc1)
 if( np == 0 ) return(exp00)


 # find the matching parens
 # loc2=loc1
 # for( i in 1:np ){
 # loc2[i]=locbalpar( exp, loc1[i] )
 # }
 loc2=unlist(lapply( loc1, function(x) locbalpar( exp, x ) ))

 # range=mapply( ":", loc1, loc2 )
 range=cbind(loc1,loc2)
 if( debug ) Print(temp,range, printrownames=1)


 # if exp contains logical operators, use method 1 only.
 if( regexpr("[&|!]", exp) > 0 ) method=1


 # fixed point iteration
 exp0=exp
 for( llll in 1:np ){


  ################################
  # remove matching double parens
  removed=numeric(0)
  for( i in 1:np ){
   from=range[i,1]; to=range[i,2]
   f=substr(exp,from,to)
   if( substr(exp,from+1,from+1) == "(" ){
    #Print(i, f, from, to)
    #Print(locbalpar(exp,from+1))
    #Print(locbalpar(exp,locbalpar(exp,from+1)))
    if( locbalpar(exp,from+1) == to-1 ){
     if( debug ) Print("double paren removed.")
     substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
     removed=c(removed,i)
    }
   }
   # Print("end of", i)
  } # end of method == 0


  ################################
  if( 1 %in% method   &  np > 0 ){

   # remove (single term)
   if( debug ) cat("llll=",llll,"M1 Removing (single term)...\n")
   removed=numeric(0)
   for( i in 1:np ){

    from=range[i,1]; to=range[i,2]
    f=substr(exp,from,to)
    if( debug ) Print(i,f, from, to)

    if( from == 1){
     # no preceding anything
     if( regexpr( "[-+|=><]", f )[[1]] == -1 ){
      # inside is a factor
      if( debug ) cat("M11\n")
      substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
      removed=c(removed,i)
     }
    } # from==1
    else{
     # check if ( is preceeded by / or !
     cond1=regexpr("[/!%]",substr(exp,from-1,from-1))[[1]]
     if( debug ) Print(cond1,substr(exp,from-1,from-1))

     if( cond1 == -1 ){
      if( regexpr( "[-+|=><]", f )[[1]] == -1 ){
       # inside is a factor
       if( debug ) cat("M12\n")
       substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
       removed=c(removed,i)
      }
     }
     else if( regexpr("/",substr(exp,from-1,from-1))[[1]] > -1 ){
      # .../()
      if( regexpr( "[-+/*]", f )[[1]] == -1 ){
       if( debug ) cat("M13\n")
       # inside is a factor and does not contain /
       substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
       removed=c(removed,i)
      }
     }
     else if( regexpr("!",substr(exp,from-1,from-1))[[1]] > -1 ){
      # ...!()
      if( regexpr( "[-+*&|]", f )[[1]] == -1 ){
       # inside is a single element
       if( debug ) cat("M14\n")
       substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
       removed=c(removed,i)
      }
     }
     else if( regexpr("%",substr(exp,from-1,from-1))[[1]] > -1 ){
      # ...%()
      if( regexpr( "[-+*/%&|]", f )[[1]] == -1 ){
       # inside is a single element
       if( debug ) cat("M15\n")
       substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
       removed=c(removed,i)
      }

     }


    } # from>1

   } # end of i loop for method 1
   if( debug ) Print(removed, exp)
   # update range
   if( length(removed) > 0 ) range=range[-removed,,drop=0]
   np=nrow(range)
   if( debug ) Print(range)

  } # end of method 1


  #################################
  if( 2 %in% method   &  np > 0  ){

   # remove (terms) w/o preceding * or - and trailing *
   if( debug ) cat("llll=",llll
       ,"M2 Removing (terms) w/o preceding * or - and trailing *...\n")
   removed=numeric(0)
   for( i in 1:np ){

    from=range[i,1]; to=range[i,2]
    f=substr(exp,from,to)
    if( debug ) Print(i,f)

    cond1=-1       #   ` added  20200520
    if( from > 1 ) cond1=regexpr("[-$&~*/%`]",substr(exp,from-1,from-1))[[1]]
    cond2=-1       #   ` added  20200520
    if( to < nchar(exp) )
      cond2=regexpr("[$&~*/%`]",substr(exp,to+1,to+1))[[1]]
    if( cond1 == -1   &  cond2 == -1 ){
     substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
     removed=c(removed,i)
    }

   } # end of i loop for method 2
   if( debug ) Print(removed,exp)
   # update range
   if( length(removed) > 0 ) range=range[-removed,,drop=0]
   np=nrow(range)
   if( debug ) Print(range)

  } # end of method 2

  ###############################
  if( 3 %in% method  &  np > 0 ){

   # remove -(-a)
   if( debug ) cat("llll=",llll,"M3 Removing -(-a) or -(-(a)) ...\n")
   removed=numeric(0)
   for( i in 1:np ){

    from=range[i,1]; to=range[i,2]
    f=substr(exp,from,to)
    if( debug ) Print(i,f)

    cond1=0
    if( from > 1  &  substr(exp,from-1,from-1) == "-" ){
     # if( regexpr( "^\\(-[[:alnum:]_.]+\\)$", f ) > 0 ) cond1=1
     if( regexpr( "^\\(-[[:alnum:]_.*/]+\\)$", f )[[1]] > 0 ) cond1=1
    }
    if( cond1 ){
     substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
     substr(exp,from-1,from-1)="+"; substr(exp,from+1,from+1)=MARK
     removed=c(removed,i)
    }

    cond1=0
    if( from > 1  &  substr(exp,from-1,from-1) == "-" ){
     if( regexpr( "^\\(-\\(.*\\)\\)$", f )[[1]] > 0 ){
      if( locbalpar(f,3) == nchar(f)-1 ) cond1=1
     }
    }
    if( cond1 ){
     substr(exp,from,from)=MARK;  substr(exp,to,to)=MARK
     substr(exp,from-1,from-1)="+";
     substr(exp,from+1,from+2)=MARK
     substr(exp,to-1,to-1)=MARK
     removed=c(removed,i)
    }

   } # end of i loop for method 3

   if( debug ) Print(removed,exp)
   # update range
   if( length(removed) > 0 ) range=range[-removed,,drop=0]
   np=nrow(range)
   if( debug ) Print(range)

  } # end of method 3


  # convergence
  if( all( exp == exp0 )  |   np == 0 ) break
  exp0=exp

 } # end of llll loop


 # remove MARK
 exp=gsub( MARK,"", exp )

 # take care of the case exp="x+(-2)" or "+(+2)"
 exp=gsub( "+-", "-", exp, fixed=1 )
 exp=gsub( "++", "+", exp, fixed=1 )

 exp=gsub("!#", "#", exp, fixed=1)
 exp=gsub("!!", "", exp, fixed=1) ########### 20171010dnc

 if( debug ) Print(exp)

 return( exp )


} # end of remove_paren


