R/rutils.R

Defines functions `%ni%` setuprmd rmdcss listfuns lininterpol listExamples kablerow halftable digitsbyrow splitDate pkgfuns pathstart pathfinish pathkind identifyfuns getnamespace getname getDBdir findfuns extractRcode extractpathway describefunctions confirmdir wtedmean which.closest detibble toXL tidynames str2 str1 revsum removeEmpty quants properties printV outfit magnitude makeUnit makelist insertmissingRC info greplow gettime getseed getmax getmin geomean getmatcolfromlist freqMean facttonum expandcolumns countgtOne countNAs countgtzero countzeros countones classDF

Documented in classDF confirmdir countgtOne countgtzero countNAs countones countzeros describefunctions detibble digitsbyrow expandcolumns extractpathway extractRcode facttonum findfuns freqMean geomean getDBdir getmatcolfromlist getmax getmin getname getnamespace getseed gettime greplow halftable identifyfuns info insertmissingRC kablerow lininterpol listExamples listfuns magnitude makelist makeUnit outfit pathfinish pathkind pathstart pkgfuns printV properties quants removeEmpty revsum rmdcss setuprmd splitDate str1 str2 tidynames toXL which.closest wtedmean

# codeutils ------------------------------------

#' @title classDF - tabulate the class of each column in a data.frame
#'
#' @description classDF - tabulate the class of each column in a data.frame.
#'
#' @param dataframe - the input data.frame for examination
#' @return generates paired column names with their classes
#' @export classDF
#' @examples
#'  data(ChickWeight)
#'  classDF(ChickWeight)
classDF <- function(dataframe) {
  nvar <- dim(dataframe)[2]
  for (i in 1:nvar) cat(colnames(dataframe)[i],class(dataframe[,i]),"\n")
} # end of class_DF

#' @title countones used in apply to count the number of ones in a vector
#'
#' @description countones used in apply to count number of ones in a vector
#' @param invect vector of values
#' @return A single value of zero or the number of ones
#' @export countones
#' @examples
#' \dontrun{
#'   set.seed(12346)
#'   x <- trunc(runif(10)*10)
#'   x
#'   countones(x)  # should be 2
#' }
countones <- function(invect) {
  pick <- which(invect == 1)
  return(length(pick))
}

#' @title countzeros used in apply to count the number of zeros in a vector
#'
#' @description countzeros used in apply to count zeros in a vector
#' @param invect vector of values
#' @return A single value of zero or the number of zeros
#' @export countzeros
#' @examples
#' \dontrun{
#'   set.seed(12346)
#'   x <- trunc(runif(10)*10)
#'   x
#'   countzeros(x)  # should be 1
#' }
countzeros <- function(invect) {
  pick <- which(invect == 0.0)
  return(length(pick))
}

#' @title countgtzero used in apply to count the number >0 in a vector
#'
#' @description countgtzero used in apply to count number >0 in a vector
#' @param invect vector of values
#' @return A single value of number of values > 0
#' @export countgtzero
#' @examples
#' \dontrun{
#'   set.seed(12346)
#'   x <- trunc(runif(10)*10)
#'   x
#'   countgtzero(x)  # should be 9
#' }
countgtzero <- function(invect) {
  pick <- which(invect > 0)
  return(length(pick))
}

#' @title countNAs used in apply to count the number of NAs in a vector
#'
#' @description countNAs used in apply to count number of NAs in a vector
#' @param invect vector of values
#' @return A single value of zero or the number of NAs
#' @export countNAs
#' @examples
#' \dontrun{
#'   set.seed(12346)
#'   x <- trunc(runif(10)*10)
#'   x[c(3,7)] <- NA
#'   countNAs(x)  # should be 2
#' }
countNAs <- function(invect) {
  pick <- which(is.na(invect))
  return(length(pick))
}

#' @title countgtOne used in apply to count the number > 1 in a vector
#'
#' @description countgtOne used in apply to count the number > 1 in a vector
#' @param invect vector of values
#' @return A single value of zero or the number of NAs
#' @export countgtOne
#' @examples
#' \dontrun{
#'   set.seed(12346)
#'   x <- trunc(runif(10)*10)
#'   x
#'   countgtone(x)  # should be 7
#' }
countgtOne <- function(invect) {
  pick1 <- which(invect > 1.0)
  return(length(pick1)/length(invect))
}

#' @title expandcolumns inserts missing matrix columns where colnames are numbers
#' 
#' @description expandcolumns takes in a matrix that has column names that are 
#'     numbers. These might be years or some other integers. If there are years
#'     missing this function will insert them into a larger matrix which is then
#'     returned.
#'
#' @param x a matrix with integers as column names. This might be a series of 
#'     counts at size for a series of years or counts at age for a number of 
#'     years. If some years of data are missing this function will insert empty
#'     columns into a larger matrix so that when plotted a true view of 
#'     available data can be presented. If there are no missing years then the
#'     original matrix is returned.
#'
#' @return a matrix with integers (years) as column names is returned with no
#'     missing years, even where some years have no data.
#' @export
#'
#' @examples
#' dat <- matrix(trunc(rnorm(50, mean=20, sd=4)),nrow=10,ncol=5,
#'               dimnames=list(1:10,c(1990,1991,1995,1997,1999)))
#' print(dat)
#' print(expandcolumns(dat))               
expandcolumns <- function(x) {
  numcl <- nrow(x)    # number of size classes or ages
  numyrs <- ncol(x)   # number of years of observations
  origyrs <- as.numeric(colnames(x))
  allyrs <- origyrs[1]:origyrs[numyrs]
  numall <- length(allyrs)
  if (numall > numyrs) {
    expandx <- matrix(0,nrow=numcl,ncol=numall,
                      dimnames=list(rownames(x),allyrs))
    pickorig <- match(origyrs,allyrs)
    expandx[,pickorig] <- x
  } else {
    expandx <- x
  }
  return(expandx)
} # end of expandcolumns

#' @title facttonum converts a vector of numeric factors into numbers
#'
#' @description facttonum converts a vector of numeric factors into numbers.
#'     If the factors are not numeric then the outcome will be a series of 
#'     NA. It is up to you to apply this function only to numeric factors. 
#'     A warning will be thrown if the resulting output vector contains NAs
#'
#' @param invect vector of numeric factors to be converted back to numbers
#'
#' @return an output vector of numbers instead of the input factors
#' @export
#'
#' @examples
#' \dontrun{
#'  DepCat <- as.factor(rep(seq(100,600,100),2)); DepCat
#'  5 * DepCat[3]
#'  as.numeric(levels(DepCat))  # #only converts levels not the replicates
#'  DepCat <- facttonum(DepCat)
#'  5 * DepCat[3]
#'  x <- factor(letters)
#'  facttonum(x)
#' }
facttonum <- function(invect){
  if (inherits(invect,"factor")) {
    outvect <- suppressWarnings(as.numeric(levels(invect))[invect])
  }
  if (inherits(invect,"numeric")) outvect <- invect
  if (any(is.na(outvect)))
    warning("NAs produced, input vector may have non-numbers present \n")
  return(outvect)
} # end of facttonum

#' @title freqMean calculates the mean and stdev of count data
#'
#' @description freqMean calculates the mean and stdev of count data
#'     it requires both the values and their associated counts and
#'     return a vector of two numbers.
#'
#' @param values the values for which there are counts
#' @param counts the counts for each of the values empty cells can be
#'     either 0 or NA
#'
#' @return a vector containing the mean and st.dev.
#' @export
#'
#' @examples
#' \dontrun{
#' vals <- c(1,2,3,4,5)    values=dat[,1];counts=dat[,2]
#' counts <- c(3,NA,7,4,2)
#' freqMean(vals,counts)  # should give 3.125 and 1.258306
#' }
freqMean <- function(values,counts) {
  N <- length(values)
  if (N != length(counts)) {
    cat("vectors have different lengths \n")
    ans <- c(NA,NA)
    names(ans) <- c("mean","stdev")
  } else {
    pick <- which(is.na(counts))
    if (length(pick) > 0) {
      counts <- counts[-pick]
      values <- values[-pick]
    }
    nobs <- sum(counts,na.rm=T)
    sumX <- sum(values * counts,na.rm=T)
    av <- sumX/nobs
    if (length(counts[counts > 0.01]) > 1) {
      sumX2 <- sum(values * 1.0 * values * counts,na.rm=T)
      stdev <- sqrt((sumX2 - (sumX * 1.0 * sumX)/nobs)/(nobs-1))
    } else { stdev <- NA
    }
    ans <- c(av,stdev)
    names(ans) <- c("mean","stdev")
  }
  return(ans)
} # end of freqMean

#' @title getmatcolfromlist extracts named columns from a list of matrices or df
#'
#' @description getmatcolfromlist One often has a list of matrices
#'
#' @param x  a list of 2d matrices
#' @param columname the name of the row or column to be extracted
#'
#' @return a list of the required columns 
#' @export
#'
#' @examples
#' y <- matrix(rnorm(100,mean=5,sd=1),nrow=10,ncol=10,
#'             dimnames=list(paste0(1:10,"R"),paste0(1:10,"C")))
#' x <- list(one=y,two=y)
#' getmatcolfromlist(x,columname="5R")
#' getmatcolfromlist(x,columname="4C")            
getmatcolfromlist <- function(x,columname) { # x=prods; columname="MSY"
  nscenes <- length(x)
  namecol <- tolower(columname)
  vals <- vector(mode="list",length=nscenes)
  names(vals) <- paste0(names(x),"_",columname)
  
  for (i in 1:nscenes) {  # i=1
    tmp <- x[[i]]
    rownames(tmp) <- tolower(rownames(tmp))
    colnames(tmp) <- tolower(colnames(tmp))
    if (namecol %in% colnames(tmp)) { vals[[i]] <- tmp[,namecol]
    } else {
      if (namecol %in% rownames(tmp)) {
        vals[[i]] <- tmp[namecol,]
      } else {
        warning(cat("unknown columname used in getmatcolfromlist \n"))
      } # end of inner if
    } # end of outer if
  }
  return(vals)
} # end of getmatcolfromlist

#' @title geomean log-normal bias corrected geometric mean of a vector
#'
#' @description Calculates log-normal bias corrected geometric mean of a 
#'     vector. NAs and zeros are removed from consideration. If a vector of
#'     length zero is entered then geomean returns 0.
#' @param invect is a vector of numbers in linear space.
#' @return The bias-corrected geometric mean of the vector
#' @export geomean
#' @examples
#'  x <- c(1,2,3,4,5,6,7,8,9)
#'  geomean(x)
#'  geomean(c(NA,0,NA,0))
#'  geomean()
geomean <- function(invect=NULL) {
  if ((length(invect) == 0) | (sum(invect,na.rm=TRUE) == 0.0)) {
    gmean <- 0
  } else {
    pick <- which((invect <= 0.0))
    if (length(pick) == 0) {
      avCE <- mean(log(invect),na.rm=TRUE)
      stdev <- sd(log(invect),na.rm=TRUE)
    } else {
      avCE <- mean(log(invect[-pick]),na.rm=TRUE)
      stdev <- sd(log(invect[-pick]),na.rm=TRUE)
    }
    gmean <- exp(avCE + (stdev^2)/2)
  }
  return(gmean)
}  # end of geomean

#' @title getmin generates the lower bound for a plot
#'
#' @description getmin generates lower bound for a plot where it is unknown
#'     whether the minimum is less than zero of not. If less than 0 then
#'     multiplying by the default mult of 1.05 works well but if the outcome
#'     if > 0 then the multiplier needs to be adjusted appropriately so 
#'     the minimum is slightly lower than the minimum of the data
#'
#' @param x the vector of data to be tested for its minimum
#' @param mult the multiplier for both ends, defaults to 1.05 (=0.95 if >0)
#'
#' @return a suitable lower bound for a plot if required
#' @export
#'
#' @examples
#' \dontrun{
#' vect <- rnorm(10,mean=0,sd=2)
#' sort(vect)
#' getmin(vect,mult=1.0)
#' }
getmin <- function(x,mult=1.05) {
  ymin <- min(x,na.rm=TRUE)
  if (ymin < 0) {
    ymin <- ymin * mult
  } else {
    ymin <- ymin * (2 - mult)
  }
  return(ymin)
} # end of getmin

#' @title getmax generates the upper bound for a plot
#'
#' @description getmax generates upper bound for a plot where it is unknown
#'     whether the maximum is greater than zero of not. If > 0 then
#'     multiplying by the default mult of 1.05 works well but if the outcome
#'     if < 0 then the multiplier needs to be adjusted appropriately so the 
#'     maximum is slightly higher than the maximum of the data
#'
#' @param x the vector of data to be tested for its maximum
#' @param mult the multiplier for both ends, defaults to 1.05 (=0.95 if < 0)
#'
#' @return a suitable upper bound for a plot if required
#' @export
#'
#' @examples
#' \dontrun{
#'  vect <- rnorm(10,mean=0,sd=2)
#'  sort(vect,decreasing=TRUE)
#'  getmax(vect,mult=1.0)
#'  vect <- rnorm(10,mean = -5,sd = 1.5)
#'  sort(vect,decreasing=TRUE)
#'  getmax(vect,mult=1.0)
#' }
getmax <- function(x,mult=1.05) {
  ymax <- max(x,na.rm=TRUE)
  if (ymax > 0) {
    ymax <- ymax * mult
  } else {
    ymax <- ymax * (2 - mult)
  }
  return(ymax)
} # end of getmax

#' @title getseed generates a random number seed
#' 
#' @description getseed generates a seed for use within set.seed. 
#'     It produces up to a 6 digit integer from the Sys.time. This
#'     Initially, at the start of a session there is no seed; a new one 
#'     is created from the current time and the process ID when one is 
#'     first required. Here, in getseed, we do not use the process ID so 
#'     the process is not identical but this at least allows the 
#'     set.seed value to be stored should the need to repeat a set of 
#'     simulations arise. The process generates up to a six digit number
#'     it then randomly reorders those digits and that becomes the seed.
#'     That way, if you were to call getseed in quick succession the
#'     seeds generated should differ even when they are generated close
#'     together in time.
#'
#' @return  an integer up to 7 digits long 
#' @export
#'
#' @examples
#' useseed <- getseed()
#' set.seed(useseed)
#' rnorm(5)
#' set.seed(12345)
#' rnorm(5)
#' set.seed(useseed)
#' rnorm(5)
getseed <- function() {
  pickseed <- as.character(as.integer(Sys.time()))
  nc <- nchar(pickseed)
  if (nc > 7) pickseed <- substr(pickseed,(nc-6),nc)
  nc <- nchar(pickseed)  
  pseed <- unlist(strsplit(pickseed,split=character(0)))
  pseed <- sample(pseed,nc)
  newseed <- paste(pseed,collapse="")
  newseed <- as.numeric(newseed)
  return(newseed)
} # end of getseed

#' @title gettime calculates time in seconds passed each day
#' 
#' @description gettime is a function designed to facilitate the measurement
#'     of time between intervals within R software that are expected to
#'     take a maximum of hours. It calculates the time as seconds elapsed 
#'     from the start of each day. As long as the timing of events does not
#'     pass from one day to the next accurate results will be generated.
#'
#' @return the time in seconds from the start of a day
#' @export
#'
#' @examples
#' \dontrun{
#'   begin <- gettime()
#'   for (i in 1:1e6) sqrt(i)
#'   finish <- gettime()
#'   print(finish - begin)
#' }
gettime <- function() {
  tim <- unlist(as.POSIXlt(Sys.time()))
  hr <- as.numeric(tim["hour"])*3600
  min <- as.numeric(tim["min"])*60
  sec <- as.numeric(tim["sec"])
  return(hr+min+sec)
} # end of gettime

#' @title greplow - uses tolower in the search for the pattern
#'
#' @description greplow a grep implementation that ignores the case of 
#'     either the search pattern or the object to be search. Both are 
#'     converted to lower case before using grep.
#' @param pattern - the text to search for in x
#' @param x - the vector or object within which to search for 'pattern' once
#'    both have been converted to lowercase.
#'
#' @return the index location within x of 'pattern', if it is present, 
#'     an empty integer if not
#' @export greplow
#'
#' @examples
#' \dontrun{
#' txt <- c("Long","Lat","LongE","LatE","Depth","Zone","Effort","Method")
#' greplow("zone",txt)
#' greplow("Zone",txt)
#' greplow("long",txt)
#' }
greplow <- function(pattern,x) {
  return(grep(tolower(pattern),tolower(x)))
}

#' @title info gets the dimension or length of a matrix, array, data.frame or list
#' 
#' @description info gets the dimension or length of a matrix, array, 
#'    data.frame or list. It is safer than dim because if the object is a
#'    list dim fails.
#'
#' @param invar an object that is either a matrix, an array, a data.frame or
#'     a list
#' @param verbose should the head of the object be printed to console. 
#'     default=FALSE
#'
#' @return the dimensions of the object
#' @export
#'
#' @examples
#' x <- array(rnorm(125,mean=5,sd=1),dim=c(5,5,5))
#' info(x,FALSE)
#' x <- list(x=5,y=6,z=7)
#' info(x)
info <- function(invar,verbose=FALSE) { # invar=x; verbose=TRUE
  cat("Class: ",class(invar),"\n")
  if (verbose) print(str(invar,max.level=1))
  cat("\n")
  categories <-  c("matrix","array","data.frame")
  if (inherits(invar,categories)) {
    cat("Dimension: ",dim(invar),"\n")
    if (verbose) print(head(invar,2))
  } else {
    cat("Length: ",length(invar),"\n")
  }
} # end of info

#' @title insertmissingRC inserts missing row and columns, which must be numbers
#' 
#' @description insertmissingRC takes in a matrix that has row and column names 
#'     that are ordered integers These might be years, size classes, or some 
#'     other integers, but they must be in ascending order. If there are rows or 
#'     columns missing this function will insert them into a larger matrix which 
#'     is then returned. The default increment for the row and column names is
#'     1, but the option is there to change this if needed. If non-numeric 
#'     row or column names occur a fatal error is called and the function stops 
#'     with a message.
#'
#' @param x a matrix with ordered integers as row and column names. If some 
#'     years (cols) or size-classes (rows) of data are missing this 
#'     function will insert empty rows and/or columns into a larger matrix so 
#'     that when plotted a true view of available data can be presented that
#'     illustrates what data is missing from the series. If there are no missing 
#'     rows or columns then the original matrix is returned.
#' @param inccol the increment used in the sequence of column names, default=1
#' @param incrow the increment used in the sequence of row names, default=1
#' @return a matrix with integers (years) as column names is returned with no
#'     missing years, even where some years have no data.
#' @export
#'
#' @examples
#' dat <- matrix(trunc(rnorm(50, mean=20, sd=4)),nrow=10,ncol=5,
#' dimnames=list(c(2,4,6,8,14,16,18,20,24,26),
#'               c("1990","1991","1995","1997","1999")))
#' print(dat)
#' print(insertmissingRC(dat,inccol=1,incrow=2))               
#' dat <- matrix(trunc(rnorm(50, mean=20, sd=4)),nrow=10,ncol=5,
#' dimnames=list(c(2,4,6,8,14,16,18,20,24,26),
#'               c("1990","1991A","1995","1997","1999")))
#' # running  insertmissingRC(dat,incrow=2) would throw an eror
insertmissingRC <- function(x,inccol=1,incrow=1) { # x=dat
  numcl <- ncol(x)   # eg number of years of observations
  origcl <- as.numeric(colnames(x))
  if (any(is.na(origcl))) 
    stop(cat("Fatal Error: non-numeric column names input to insertmissingRC \n"))
  numrw <- nrow(x)    # eg number of size classes or ages
  origrw <- as.numeric(rownames(x))
  if (any(is.na(origrw))) 
    stop(cat("Fatal Error: non-numeric row names input to insertmissingRC \n"))  
  allcl <- seq(origcl[1],origcl[numcl],inccol)
  numall <- length(allcl)
  if (numall > numcl) {
    expandx <- matrix(0,nrow=numrw,ncol=numall,
                      dimnames=list(rownames(x),allcl))
    pickorigcl <- match(origcl,allcl)
    expandx[,pickorigcl] <- x
  } else {
    expandx <- x
  }
  allrw <- seq(origrw[1],origrw[numrw],incrow)
  numallrw <- length(allrw)
  if (numallrw > numrw) {
    expandx2 <- matrix(0,nrow=numallrw,ncol=numall,
                       dimnames=list(allrw,allcl))
    pickorigrw <- match(origrw,allrw)
    expandx2[pickorigrw,] <- expandx
    expandx <- expandx2
  }
  return(expandx)
} # end of insertmissingRC

#' @title makelabel generates a label from text and values
#'
#' @description makelabel It is common to want a label with text and a series 
#'     of values. But paste and paste0 cycles the text and the values. To
#'     avoid this makelabel first combines the values as text and then
#'     adds the input text to the front of the values
#'
#' @param txt the input text for the label, can be empty
#' @param vect the series of values to be included in the label
#' @param sep the separator for the components; defaults to  '_'
#' @param digits how many significant digits for the values; default = 3
#'
#' @return a character string made up of text and values
#' @export
#'
#' @examples
#' pars <- c(18.3319532,33.7935124,3.0378107,6.0194465,0.5815360,0.4270468)
#' makelabel("Cohort1",pars[c(1,3,5)],sep="__")
#' makelabel("",pars[c(1,3,5)],sep="__",digits=4)
makelabel <- function (txt, vect, sep = "_", digits = 3) {
  label <- round(vect[1], digits)
  if (length(vect) > 1) {
    nnum <- length(vect)
    for (i in 2:nnum) label <- paste(label, round(vect[i], digits), sep = sep)
  }
  if (nchar(txt) > 0) label <- paste0(txt,sep,label)
  return(label)
} # end of makelabel

#' @title makelist a utility that outputs a list structure defined by its input
#'
#' @description makelist is a utility that performs the common task of making
#'     a list structure of the same length as the vector of names input. It
#'     also names each of the list components after the vector of names. The
#'     output list structure is then ready to be populated with results.
#'
#' @param scenes an vector of character names describing different scenarios
#'
#' @return a list of length scenes names for the vector of names in scenes
#' @export
#'
#' @examples
#' scenarios <- c("base_case","higher_M","Lower_M")
#' changeM <- makelist(scenes=scenarios)
#' changeM
makelist <- function(scenes) {
  nscen <- length(scenes)
  tmp <- vector(mode="list",length=nscen)
  names(tmp) <- scenes
  return(tmp)
} # end of makelist

#' @title makeUnit generates a unit matrix whose diagonal can be changed
#' 
#' @description makeUnit generates a unit matrix but includes the facility
#'     to alter the diagonal value away from 1.0 if desired.
#'
#' @param N the order of the matrix
#' @param diagvalue defaults to 1.0, but otherwise can be a different 
#'     constant or a vector of dimension N
#'
#' @return a square matrix defaulting to a unit matrix
#' @export
#'
#' @examples
#' \dontrun{
#'   makeUnit(4)
#'   surv <- exp(-0.2)
#'   makeUnit(4,surv)
#' }
makeUnit <- function(N,diagvalue=1.0) {
  N <-trunc(N)
  UnitM <- matrix(0,nrow=N,ncol=N,dimnames=list(1:N,1:N))
  diag(UnitM) <- diagvalue
  return(UnitM)
}  # end of makeUnit


#' @title magnitude returns the magnitude of numbers
#'
#' @description magnitude is useful when using an
#'     optimizer such as optim, which uses a parscale parameter.
#'     magnitude can determine the respective parscale value for each
#'     parameter value.
#'
#' @param x the vector of numbers (parameters) whose magnitudes are
#'     needed
#'
#' @return a vector of magnitudes
#' @export
#'
#' @examples
#' \dontrun{
#'   x <- c(0,0.03,0.3,3,30,300,3000)
#'   magnitude(x)
#' }
magnitude <- function(x) {
  return(10^(floor(log10(abs(x)))))
}


#' @title outfit tidy print of output from optim, nlminb, or nlm
#'
#' @description outfit takes in the output list from either optim,
#'     nlminb, or nlm and prints it more tidily to the console, In the
#'     case of nlm it also prints the conclusion regarding the
#'     solution. It might be more effective to implement an S3 method.
#'
#' @param inopt the list object output by nlm, nlminb, or optim
#' @param backtran a logical default = TRUE If TRUE it assumes
#'     that the parameters have been log-transformed for stability
#'     and need back-transforming
#' @param digits the number of digits to round the backtransformed 
#'     parameters. defaults to 5.
#' @param title character string used to label the output if desired,
#'     default = empty character string
#' @param parnames default="" which means the estimated parameters
#'     will merely be numbered. If a vector of names is given 
#'     then this will be used instead, at least, for nlm and optim.
#'
#' @return nothing but it does print the list to the console tidily
#' @export
#'
#' @examples
#'  x <- 1:10  # generate power function data from c(2,2) + random
#'  y <- c(2.07,8.2,19.28,40.4,37.8,64.68,100.2,129.11,151.77,218.94)
#'  alldat <- cbind(x=x,y=y)
#'  pow <- function(pars,x) return(pars[1] * x ^ pars[2])
#'  ssq <- function(pars,indat) {
#'     return(sum((indat[,"y"] - pow(pars,indat[,"x"]))^2))
#'  }  # fit a power curve using normal random errors
#'  pars <- c(2,2)
#'  best <- nlm(f=ssq,p=pars,typsize=magnitude(pars),indat=alldat)
#'  outfit(best,backtran=FALSE) #a=1.3134, b=2.2029 ssq=571.5804
outfit <- function(inopt,backtran=TRUE,digits=5,title="",
                   parnames=""){
  #  inopt=bestvB; backtran = FALSE; digits=5; title=""; parnames=""
  nlmcode <- c("gradient close to 0, probably solution",
               ">1 iterates in tolerance, probably solution",
               "Either ~local min or steptol too small",
               "iteration limit exceeded",
               "stepmax exceeded ,5 times")
  if (length(grep("value",names(inopt))) > 0) { # optim
    cat("optim solution: ", title,"\n")
    cat("minimum     : ",inopt$value,"\n")
    cat("iterations  : ",inopt$counts," iterations, gradient\n")
    cat("code        : ",inopt$convergence,"\n")
    if (backtran) {
      ans <- cbind(par=inopt$par,transpar=round(exp(inopt$par),digits))
    } else {
      ans <- t(inopt$par)
    }
    if ((length(parnames) > 1) & (length(parnames) == length(inopt$par))) {
      rownames(ans) <- parnames
    } else {
      rownames(ans) <- 1:length(inopt$par)
    }
    print(ans)
    cat("message     : ",inopt$message,"\n")
  } # end of optim
  if (length(grep("minimum",names(inopt))) > 0) {  # nlm - preferred
    cat("nlm solution: ", title,"\n")
    cat("minimum     : ",inopt$minimum,"\n")
    cat("iterations  : ",inopt$iterations,"\n")
    cat("code        : ",inopt$code,nlmcode[inopt$code],"\n")
    if (backtran) {
      ans <- cbind(par=inopt$estimate,gradient=inopt$gradient,
                   transpar=round(exp(inopt$estimate),digits))
    } else {
      ans <- cbind(par=inopt$estimate,gradient=inopt$gradient)
    }
    if ((length(parnames) > 1) & 
        (length(parnames) == length(inopt$estimate))) {
      rownames(ans) <- parnames
    } else {
      rownames(ans) <- 1:length(inopt$estimate)
    }
    print(ans)
  } # end of nlm
  if (length(grep("objective",names(inopt))) > 0) {
    cat("nlminb solution: ", title,"\n")   # nlminb seems to be deprecated
    cat("par        : ",inopt$par,"\n")
    cat("minimum    : ",inopt$objective,"\n")
    cat("iterations : ",inopt$iterations,"\n")
    cat("code       : ",inopt$evaluations," iterations, gradient\n")
    cat("message    : ",inopt$message,"\n")
  }
  if (length(grep("hessian",names(inopt))) > 0) {
    cat("hessian     : \n")
    print(inopt$hessian)
  }
} # end of outfit

#' @title printV returns a vector cbinded to 1:length(invect)
#'
#' @description printV takes an input vector and generates another vector of
#'     numbers 1:length(invect) which it cbinds to itself. This is primarily
#'     useful when trying to print out a vector which can be clumsy to read 
#'     when print across the screen. applying printV leads to a single 
#'     vector being printed down the screen
#'
#' @param invect the input vector to be more easily visualized, this can be
#'     numbers, characters, or logical. If logical the TRUE and FALSE are
#'     converted to 1's and 0's
#' @param label the column labels for vector, default is index and value
#'
#' @return a dataframe containing the vector 1:length(invect), and invect.
#' @export
#'
#' @examples
#' \dontrun{
#' vec <- rnorm(10,mean=20,sd=2)
#' printV(vec)
#' vec <- letters
#' printV(vec)
#' vec <- c(TRUE,TRUE,TRUE,FALSE,FALSE,TRUE,TRUE,FALSE,FALSE,TRUE,TRUE)
#' printV(vec,label=c("index","logicstate"))
#' }
printV <- function(invect,label=c("value","index")) {
  n <- length(invect)
  outvect <- as.data.frame(cbind(invect,1:n))
  colnames(outvect) <- label
  return(outvect)
} # end of print_V

#' @title properties - used to check a data.frame before standardization
#'
#' @description properties - used to check a data.frame before
#'     standardization. The maximum and minimum are constrained to four
#'     decimal places. It allows for columns of NAs and for Posix 
#'     columns. In case one uses tibbles this function now checks and internally
#'     changes the indat into a strict data.frame. This will not influence the
#'     external use but it does allow the properties to be obtained. 
#'     
#' @param indat the data.frame containing the data fields to be used
#'     in the subsequent standardization. It tabulates the number of
#'     NAs and the number of unique values for each variable and finds
#'     the minimum and maximum of the numeric variables
#' @param dimout determines whether or noth the dimensions of the data.frame
#'     are printed to the screen or not; defaults to FALSE
#' @return a data.frame with the rows being each variable from the input
#'     input data.frame and the columns being the number of NAs, the
#'     number of unique values, and minimum and maximum (where possible).
#' @export properties
#' @examples
#' \dontrun{
#'  data(abdat)
#'  properties(abdat$fish)
#' }
properties <- function(indat,dimout=FALSE) {  # indat=ab; dimout=FALSE
  indat <- detibble(indat)
  dominmax <- function(x) {
    if (length(which(x > 0)) == 0) return(c(NA,NA))
    mini <- min(x,na.rm=TRUE)
    maxi <- max(x,na.rm=TRUE)
    return(c(mini,maxi))
  }
  if (length(indat$geometry > 0)) {
    pickg <- which(colnames(indat) == "geometry")
    columns <- ncol(indat)
    if (pickg == columns) {
      indat <- indat[,]
    }
    indat <- indat[,c(1:(pickg-1),(pickg+1:columns))]
    cat("geometry 'column' omitted \n")
  }
  if(dimout) print(dim(indat))
  isna <- sapply(indat,function(x) sum(is.na(x)))
  uniques <- sapply(indat,function(x) length(unique(x)))
  columns <- length(indat)
  clas <- character(columns)
  for (i in 1:columns) {
    clas[i] <- class(indat[,i])[1]
  }
  numbers <- c("integer","numeric")
  pick <- which(clas %in% numbers)
  minimum <- numeric(length(uniques))
  maximum <- minimum
  for (i in 1:length(pick)) {
    minmax <- dominmax(indat[,pick[i]])
    minimum[pick[i]] <- minmax[1]
    maximum[pick[i]] <- minmax[2]
  }
  pick <- which((clas == "character") & (isna == 0))
  if (length(pick) > 0) {
    for (i in 1:length(pick)) {
      pickna <- which(indat[,pick[i]] == "NA")
      if (length(pickna) > 0) isna[pick[i]] <- length(pickna)
    }
  }  
  index <- 1:length(isna)
  props <- as.data.frame(cbind(index,isna,uniques,clas,round(minimum,4),
                               round(maximum,4),t(indat[1,])))
  colnames(props) <- c("Index","isNA","Unique","Class","Min",
                       "Max","Example")
  return(props)
} # end of properties


#' @title quants used in apply to estimate quantiles across a vector
#'
#' @description quants used in 'apply' to estimate quantiles across a vector
#' @param invect vector of values
#' @param probs the quantiles wanted in the outputs; default = 
#'     c(0.025,0.05,0.5,0.95,0.975)
#' @return a vector of the c(0.025,0.05,0.5,0.95,0.975) quantiles or
#'     whatever is input to probs
#' @export quants
#' @examples
#' \dontrun{
#'  x <- runif(1000)
#'  quants(x)
#'  quants(x,probs=c(0.075,0.5,0.925))
#' }
quants <- function(invect,probs = c(0.025,0.05,0.5,0.95,0.975)) {
  ans <- quantile(invect,probs =probs,na.rm=T)
  return(ans)
}

#' @title removeEmpty removes empty strings from a vector of strings
#'
#' @description removeEmpty removes empty strings from a vector of strings.
#'     Such spaces often created by spurious commas at the end of lines. It
#'     also removes strings made up only of spaces and removes spaces from
#'     inside of inidivdual chunks of text.
#'
#' @param invect vector of input strings, possibly containing empty strings
#'
#' @return a possibly NULL vector of strings
#' @export
#'
#' @examples
#' \dontrun{
#' x <- c("1","","2","","   ","3"," ","4","","a string","end")
#' x
#' length(x)
#' length(removeEmpty(x))
#' removeEmpty(x)
#' }
removeEmpty <- function(invect) {
  tmp <- gsub(" ","",invect)
  tmp <- tmp[nchar(tmp) > 0]
  return(tmp)
}

#' @title revsum generates a vector of the cumulative sum from n to 1 
#'
#' @description revsum generates a vector of the cumulative sum of an input
#'     vector from n to 1 rather than from 1 - n, as in cumsum.
#' 
#' @param x an input vector
#'
#' @return a vector of cumulative values from n to 2
#' @export
#'
#' @examples
#' x <- c(1,2,3,4,5)/15
#' print(round(cbind(x,cumsum(x),revsum(x)),3))
revsum <- function(x) {
  n <- length(x)
  if (n < 2) warning("Input vector in revsum less than length 2.  \n")
  ans <- numeric(n)
  ans[n] <- x[n]
  for (i in (n-1):1) ans[i] <- ans[i+1] + x[i] 
  return(ans)
} # end of revsum


#' @title str1 a simple replacement for str(x,max.level=1,give.attr=FALSE)
#' 
#' @description str1 an abbreviated replacement for str(x,max.level=1), which I 
#'     put together because to often I make a typo when typing out the full
#'     str syntax. Hence I find str1 helpful
#'
#' @param x the object whose structure is to be listed
#' @param attrs should the objects attributes be printed? default = FALSE
#'
#' @return str(x,max.level=1)
#' @export
#'
#' @examples
#' x <- matrix(rnorm(25,mean=5,sd=1),nrow=5,ncol=5)
#' str1(x)
str1 <- function(x,attrs = FALSE){
  return(str(x,max.level=1,give.attr=attrs))
}

#' @title str2 a simple replacement for str(x,max.level=2,give.attr=FALSE)
#' 
#' @description str2 an abbreviated replacement for str(x,max.level=2), which I 
#'     put together because to often I make a typo when typing out the full
#'     str syntax. For when str1 is not detailed enough.
#'
#' @param x the object whose structure is to be listed
#' @param attrs should the objects attributes be printed? default = FALSE
#'
#' @return str(x,max.level=2)
#' @export
#'
#' @examples
#' x <- matrix(rnorm(25,mean=5,sd=1),nrow=5,ncol=5)
#' str2(x)
str2 <- function(x,attrs=FALSE){
  return(str(x,max.level=2,give.attr=attrs))
}

#' @title tidynames can replace awkward data.frame names with better ones
#'
#' @description tidynames can replace awkward or overly long data.frame
#'     column names with better ones that are easier to use. It also
#'     permits one to maintain the same set of column names within an
#'     analysis even when the source data.frame includes alterations.
#'
#' @param columns the vector of names that should include the ones to be
#'     altered
#' @param replace the names to be changed, as a vector of character
#'     strings
#' @param repwith the replacement names as a vector of character strings
#'
#' @return a vector of new columns names
#' @export
#'
#' @examples
#'  print("wait")
tidynames <- function(columns,replace,repwith) {
  nreplace <- length(replace)
  if (nreplace != length(repwith))
    stop("Different number of names in replace and repwith \n")
  for (i in 1:nreplace) {
    pick <- grep(replace[i],columns)
    #cat(i,pick,"\n")
    if (pick[1] > 0) {
      columns[pick[1]] <- repwith[i]
    } else {
      warning(paste0(replace[i]," not in the dataset"))
    }
  }
  return(columns)
} # end of tidynames

#' @title toXL copies a data.frame or matrix to the clipboard
#'
#' @description toXL copies a data.frame or matrix to the clipboard
#'    so one can then switch to Excel and just type ctrl + V to paste the
#'    data in place
#'
#' @param x a vector or matrix
#' @param output - a boolean determining whether to print the object to the
#'    screen as well as the clipboard; defaults to FALSE
#' @return Places the object 'x' into the clipboard ready for pasting
#' @export toXL
#' @examples
#' datamatrix <- matrix(data=rnorm(100),nrow=10,ncol=10)
#' colnames(datamatrix) <- paste0("A",1:10)
#' rownames(datamatrix) <- paste0("B",1:10)
#' toXL(datamatrix,output=TRUE)
toXL <- function(x,output=FALSE) {
  write.table(x,"clipboard",sep="\t")
  if(output) print(x)
}

#' @title detibble if the input is a tibble it converts it back to a data.frame
#' 
#' @description detibble is used to ensure that if an input object is a tibble
#'     then it is returned as a base R data.frame. This is sometimes needed as
#'     tibbles can upset some base R functionality.
#'
#' @param indat any matrix like data.frame or tibble
#'
#' @return a data.frame
#' @export
#'
#' @examples
#' \dontrun{
#' # syntax
#' detibble(tibble)
#' }
detibble <- function(indat) {
  if ("tbl" %in% class(indat)) {
    indat <- as.data.frame(unclass(indat), stringsAsFactors = FALSE,
                           check.names=FALSE)
  } 
  return(indat)
} # end of detibble

#' @title which.closest find the number closest to a given value
#'
#' @description which.closest finds either the number in a vector which is
#'     closest to the input value or its index value
#'
#' @param x the value to lookup
#' @param invect the vector in which to lookup the value x
#' @param index should the closest value be returned or its index; 
#'     default=TRUE
#'
#' @return by default it returns the index in the vector of the value 
#'     closest to the input value
#' @export
#'
#' @examples
#' \dontrun{
#' vals <- rnorm(100,mean=5,sd=2)
#' pick <- which.closest(5.0,vals,index=TRUE)
#' pick
#' vals[pick]
#' which.closest(5.0,vals,index=FALSE)
#' }
which.closest <- function(x,invect,index=T) {
  pick <- which.min(abs(invect-x))
  if (index) {
    return(pick)
  } else {
    return(invect[pick])
  }
} # end of which_.closest

#' @title wtedmean calculates the weighted mean of a set of values and weights
#'
#' @description wtedmean solves the problem of calculating a weighted mean
#'     value from a set of values with different weights. Within the aMSE this
#'     is common when trying to summarize across populations within an SAU or
#'     summarize SAU within a zone by finding a mean value weighted by the
#'     respective catch from each related population or SAU.
#'
#' @param x the values whose weighted mean is wanted
#' @param wts the weights to use, often a set of catches
#'
#' @return a single real number
#' @export
#'
#' @examples
#' saucpue <- c(91.0,85.5,88.4,95.2)
#' saucatch <- c(42.0,102.3,75.0,112.0)
#' wtedmean(saucpue,saucatch)
#' saucatch/sum(saucatch)  # the relative weights
wtedmean <- function(x,wts) {
  pwts <- wts/sum(wts,na.rm=TRUE)
  ans <- sum((x * pwts),na.rm=TRUE)
  return(ans)
} # end of wtedmean



# fileutils -----------------------------------------------

#' @title confirmdir checks to see if a directory exists and makes it if not
#'
#' @description confirmdir enables one to be sure a selected directory exists.
#'     If it has not been created then confirmdir will create it if it does not
#'     already exist. This is useful when defining output directories on the
#'     hard drive where large objects may be stored.
#'
#' @param x the directory to be checked and created if necessary
#' @param make should the directory be created if it does not already exist?
#'     default=TRUE
#' @param verbose should responses be sent to the console? default=TRUE
#' @param ask should confirmdir ask whether to create the directory or not?
#'    default = TRUE. Set to FALSE if running a script rather than working
#'    interactively.
#'
#' @return nothing but it can create a directory
#' @export
#'
#' @examples
#' x <- tempdir()
#' confirmdir(x)
confirmdir <- function(x,make=TRUE,verbose=TRUE,ask=TRUE) {
  if (dir.exists(x)) {
    if (verbose) cat(x," already exists  \n")
  } else {
    if (verbose) cat(x," did not exist  \n")
    if (make) {
      if (ask) {
        label <- paste0("Create directory: ",x," [Y, y, N, n]: ")
        goahead <- readline(prompt=label)
      } else {
        goahead <- "Y"
      }
      if (goahead %in% c("y","Y")) {
        dir.create(x, recursive = TRUE)
        if (verbose) cat(x," has been created  \n")
      }
    }
  }
} # end of confirmdir

#' @title describefunctions lists all R functions in a set of files
#' 
#' @description describefunctions lists all the R functions in a set of R files
#'     along with their syntax, the linenumber in each file, the filename, the
#'     function name, and the functions within the set of R files that each 
#'     function calls. In addition, there is now a crossreference column,
#'     which identifies which functions call each function. If just the indir
#'     is provided then all R files in that directory will be examined. .Rmd
#'     files will not be considered but any other file type starting with .R
#'     may cause trouble until I find a fix!
#'
#' @param indir the directory in which to find the R files
#' @param files a vector of filenames, as character, within which to search for 
#'     functions, default="", which means all R files in indir will be used
#' @param outfile the full path and name of the CSV file to which the results 
#'     should be saved. default="", which means the output will only be 
#'     returned invisibly. If outfile has a fullpath csv filename then it will
#'     also be written to that file as well as retunred invisibly
#' @param sortby how should the output be sorted? deffault = "functions", which
#'     means the functions will be sorted by name. The alternative is "file",
#'     which will sort the output by input filename 
#'     
#' @seealso{
#'    \link{findfuns}, \link{identifyfuns}
#' }     
#'
#' @return It can produce a csv file but also returns the results invisibly 
#' @export
#'
#' @examples
#' filen <- tempfile("test",fileext=".R")
#' txt <- c("# this is a comment",
#'  "#' @title ...",
#'  "dummy <- function() {",
#'  "  out <- anotherdummy()",
#'  "  return(out)",
#'  "}",
#'  "# a possibly confusing use of function",
#'  "#' @title ...",
#'  "anotherdummy <- function() {",
#'  "  return(NULL)",
#'  "}",
#'  "  ")
#'  write(txt,file=filen)
#'  usedir <- paste0(tempdir(),"//")
#'  filename <- tail(unlist(strsplit(filen,"\\",fixed=TRUE)),1)
#'  x <- describefunctions(indir=usedir,files=filename,outfile="")
#'  x
describefunctions <- function(indir,files="",outfile="",sortby="functions") {
  # indir=indir; files=files;outfile=outfilen;sortby="functions"
  if (nchar(files[1]) == 0) {
    dirfiles <- dir(indir)
    pickfiles <- grep(".R",dirfiles,ignore.case=TRUE)
    files <- dirfiles[pickfiles]
    pickRmd <- grep(".Rmd",dirfiles,ignore.case=TRUE)
    if (length(pickRmd) > 0) files <- files[-pickRmd]
  }
  nfiles <- length(files)
  numfuns <- matrix(0,nrow=nfiles,ncol=1,dimnames=list(files,c("nfuns")))
  allfiles <- NULL
  for (i in 1:nfiles) { # i = 1
    outfuns <- listfuns(paste0(indir,files[i]))
    if (nrow(outfuns) > 1) {
      numfuns[i,1] <- nrow(outfuns)
    } else {
      if (nchar(outfuns[1,"functions"]) > 0) numfuns[i,1] <- 1
    }
    allfiles <- rbind(allfiles,outfuns)
  }
  allfilesort <- allfiles[order(allfiles[,sortby]),]
  allrefs <- matrix(0,nrow=0,ncol=1)
  for (i in 1:nfiles) {# i = 8
    if (numfuns[i] > 0) {
       allrefs <- rbind(allrefs,findfuns(indir,files[i],
                                         allfilesort[,"functions"]))
    } else {
      allrefs <- rbind(allrefs,", , ")
    }
  }
  allfiles[,"references"] <- allrefs
  x <- allfiles[order(allfiles[,sortby]),]
  x[,"crossreference"] <- ""
  nfun <- nrow(x)
  for (i in 1:nfun) {
    pickf <- grep(x[i,"functions"],x[,"references"])
    if (length(pickf) > 0) {
      if (length(pickf) == 1) x[i,"crossreference"] <- x[pickf,"functions"]
    } else {
      x[i,"crossreference"] <- paste0(x[pickf,"functions"],collapse=", ")
    }
  }
  xfinal <- x[,c(1,2,3,4,6,5)]
  if (nchar(outfile) > 5) write.csv(xfinal,file = outfile)
  return(invisible(xfinal))
} # end of describefunctions

#' @title extractpathway traces the sequence of functions calls within a function
#'
#' @description extractpathway is used when documenting the sequence of function
#'     calls within a set of functions within a package one is developing. It
#'     needs to know the location of the R directory (indir) for the package,
#'     the starting functions at the beginning of a particular algorithm, and
#'     a listing from the rutilsMH function describefunctions. Then it traces
#'     the sequential usage of all known package functions, ignoring base R
#'     functions. The final output is a vector of function names, starting with
#'     the top-level function.
#'
#' @param indir the R directory within an R package being documented
#' @param infun the name of the top-level function whose sequential function use
#'     is being explored
#' @param allfuns the output of applying readLines to a text file containing
#'     R code.
#'
#' @return a vector of function names in the sequence in which they are used
#'     within the first names function
#' @export
#'
#' @examples
#' print("Wait on complex use of tempDir; see describefunctions for an example")
extractpathway <- function(indir,infun,allfuns) {  #  indir=tempdir(),infun="dummy",allfuns=x
  functions <- allfuns[,"functions"]
  pickrow <- which(functions == infun)
  if (length(pickrow) == 0)
    stop("Input function to extractpathway, not in allfuns. /n")
  rfile <- allfuns[pickrow,"file"]
  infile <- paste0(indir,rfile,".R")
  solution <- infun
  content <- readLines(con=infile)
  funLines <- identifyfuns(content=content)
  begin <- grep(paste0(infun," <- function"),content)
  finish <- funLines[which(funLines == begin) + 1] - 1
  funcont <- content[(begin+1):finish]
  testhash <- substr(funcont,1,4)
  omit <- grep("#",testhash)
  if (length(omit > 0)) funcont <- funcont[-omit]
  funcont <- removeEmpty(funcont)
  nline <- length(funcont)
  for (i in 1:nline) { #   i =2
    txt <-  removeEmpty(unlist(strsplit(funcont[i],split=character(0))))
    bracket <- match("(",txt)
    if (!is.na(bracket)) {
      loc2 <- match("(",txt)[1] - 1 # get end of object name
      loc1 <- grep("<",txt) + 2        # get putative start of object name
      if (length(loc1) == 0) {
        fun <- paste0(txt[1:loc2],collapse="")
      } else {
        if (loc1 > loc2) {
          fun <- paste0(txt[1:loc2],collapse="")
        } else {
          fun <- paste0(txt[loc1:loc2],collapse="")
        }
      }
      if (fun %in% functions) solution <- c(solution,fun)
    }
  }
  return(unique(solution))
} # end of extractpathway


#' @title extractRcode pulls out the r-code blocks from Rmd files
#' 
#' @description extractRcode pulls out the r-code blocks from Rmd files and 
#'     saves them into a separate R file. 
#'
#' @param indir the directory in which the rmd file is to be found and into
#'     which the output file will be placed.
#' @param rmdfile the name of the Rmd file whose R code is to be extracted
#' @param filename the name of the R file into which the r-code is to go. 
#'
#' @return generates an R file in the working directory, otherwise returns nothing
#' @export
#'
#' @examples
#' print("wait on a real example")
extractRcode <- function(indir,rmdfile,filename="out.R") { # indir=indir; rmdfile=inrmd; filename="out.R"
  infile <- paste0(indir,"/",rmdfile)
  fileout <- paste0(indir,"/",filename)
  cat("# R-code from the file ",rmdfile,"\n\n",file=fileout,append=FALSE)
  txt <- readLines(infile)
  pick <- grep("```",txt)
  steps <- length(pick)
  for (i in seq(1,steps,2)) {
    begin <- pick[i]
    cat("#",txt[begin],"\n",file=fileout,append=TRUE)
    finish <- pick[i+1]
    for (j in (begin+1):(finish-1)) {
      cat(txt[j],"\n",file=fileout,append=TRUE)
    }
    cat("#",txt[finish],"\n\n\n\n",file=fileout,append=TRUE)
  }
} # end of extractRcode


#' @title findfuns finds references to other functions within other functions
#' 
#' @description findfuns is used when developing a complex project containing 
#'     many R files, each containing many R functions. Given a file that 
#'     contains a set of functions (infile) and a data.frame of all functions  
#'     from the project (allfuns), which is obtained using listfuns, then 
#'     findfuns searches each function for references to any of the projects
#'     functions. This allows them to be cross referenced
#'
#' @param indir the directory in which the file identified in 'infile' is
#'     located
#' @param infile the filename of the R file within which to search for the 
#'     functions listed in the allfuns data.frame derived from the listfuns
#'     function
#' @param allfuns a data.frame of functions and their properties listed in 
#'     the order of the sorted function names in the 'function' column 
#'     
#' @seealso{
#'    \link{describefunctions}, \link{identifyfuns}
#' }
#'
#' @return the same data.frame except that the references column will have been
#'     populated
#' @export
#'
#' @examples
#' print("wait on suitable data-set")
findfuns <- function(indir,infile,allfuns) { # indir=indir;infile=files[i]; allfuns=allfilesort[,"functions"]
  infile <- file.path(indir,infile)
  numfun <- length(allfuns)
  content <- readLines(con=infile)
  rfun <- tail(unlist(strsplit(infile,"/")),1)
  rfile <- substr(rfun,1,nchar(rfun)-2)
  funLines <- grep("function",content)
  titles <- grep("@title",content)
  testhash <- substr(content[funLines],1,4)
  omit <- grep("#",testhash)
  if (length(omit) > 0) {
    funLines <- funLines[-omit]
    testhash <- testhash[-omit]
  }
  omit2 <- grep("  ",testhash) # remove functions internal to other functions
  if (length(omit2) > 0) funLines <- funLines[-omit2]
  nfun <- length(funLines)
  outf <- as.data.frame(matrix("",nrow=nfun,ncol=1))
  bounds <- matrix(0,nrow=nfun,ncol=2,
                   dimnames=list(paste0(rfile,1:nfun),c("start","end")))
  bounds[,1] <- funLines + 1
  if (nfun > 1) {
    bounds[,2] <- c((titles[2:nfun] - 2),length(content))
  } else {
    bounds[,2] <- length(content)
  }
  for (i in 1:nfun) { # i=10
    funname <- removeEmpty(unlist(strsplit(content[funLines[i]],"<-"))[1])
    funcont <- content[bounds[i,1]:bounds[i,2]]
    testhash <- substr(funcont,1,5)
    omit <- grep("#",testhash)
    if (length(omit) > 0) funcont <- funcont[-omit]
    whichfun <- ", "
    for (j in 1:numfun) {  #  j = 65
      if (allfuns[j] != funname)
        if (length(grep(allfuns[j],funcont)) > 0)
          whichfun <- paste0(whichfun,allfuns[j],", ")
    }
    outf[i,] <- whichfun
  }
  return(outf)
} # end of findfuns


#' @title getDBdir identifies the DropBox path
#'
#' @description getDBdir identifies the path to DropBox within the users 
#'     sub-directory within the C:/users/ directory. If not present then it 
#'     returns NULL and gives a warning.
#'
#' @return the path to the DropBox directory
#' @export
#'
#' @examples
#' getDBdir()
getDBdir <- function() {
  dropdir <- paste0("c:/Users/",Sys.info()[["user"]],"/DropBox")
  if (dir.exists(dropdir)) {
    prefixdir <- dropdir
  } else { 
    cat("No DropBox found in the users sub-directory within C:/users/  \n")
    cat("output set to NULL   \n")
    prefixdir <- NULL
  }
  return(prefixdir)
} # end of getDBdir


#' @title getname returns the name of a variable as character
#'
#' @description getname runs 'deparse(substitute(x))' to get the
#'     name of the input variable. Saves remembering the syntax
#'
#' @param x any variable whose name is wanted as a character string
#'
#' @return a character string with the name of input variable
#' @export
#'
#' @examples
#' \dontrun{
#' a_variable <- c(1,2,3,4,5,6,7,8)
#' getname(a_variable)
#' }
getname <- function(x) {
  return((deparse(substitute(x))))
}

#' @title getnamespace returns the namespace for a given function
#'
#' @description getnamespace searches the loaded NameSpaces and returns the
#'     name of the NameSpace or package for the input function. This is used
#'     in by 'network'. If the namespace is not loaded this will not be able
#'     to be found.
#'
#' @param fun the name of the function of interest. It must be of class
#'     character, which can be obtained using 'getname'
#'
#' @return the name of the loaded NameSpace or package within which a 
#'     function can be found.
#' @export
#'
#' @examples
#' \dontrun{
#'    getnamespace(getname(lm))
#'    getnamespace(getname(anova))
#' }
getnamespace <- function(fun) {
  if (nchar(fun) == 0) return(NA)
  nss <- loadedNamespaces()
  envs <- c(lapply(nss,.getNamespace))
  return(nss[vapply(envs, function(env) exists(fun, env, inherits = FALSE),logical(1))])
} # end of get_namespace

#' @title identifyfuns uses text from readLines to identify function beginnings
#' 
#' @description identifyfuns is used when tracing the interactions between 
#'     functions within R packages. It uses the vector of character vectors
#'     that is produced by readLines and identifies the starting lines of all
#'     functions. It ignores all functions defined within comments, as well as
#'     ignoring all functions defined internally to other functions. It does the
#'     latter by testing for a couple of spaces at the start of a line 
#'     containing a function definition, which functions defined within another
#'     function should have.
#'
#' @param content the output of applying readLines to a text file containing 
#'     R code.
#'     
#' @seealso{
#'    \link{describefunctions}, \link{findfuns}
#' }     
#'
#' @return a vector of line numbers identifying the start of all functions 
#'     within the content. This may be a vector of zero length if there are no
#'     functions.
#' @export
#'
#' @examples
#' txt <- c("# this is a comment",
#' "dummy <- function() { return(NULL) }",
#' "# a possibly confusing use of function",
#' "anotherdummy <- function() { return(NULL) }")
#' identifyfuns(txt)
identifyfuns <- function(content) {
  funLines <- grep("function",content)
  testhash <- substr(content[funLines],1,4)
  omit <- grep("#",testhash)
  if (length(omit) > 0) {
    funLines <- funLines[-omit]
    testhash <- testhash[-omit]
  }
  omit2 <- grep("  ",testhash) # remove functions internal to other functions
  if (length(omit2) > 0) funLines <- funLines[-omit2]
  return(funLines)
} # end of identifyfuns

#' @title pathkind finds the type of separator used in a path
#'
#' @description pathkind finds the type of separator used in a path,
#'     this is either a '/' or a '\\'
#'
#' @param inpath - the path to be analysed
#'
#' @return the type of path divider, either a 0 = '\\' or a
#'    1 = '/'
#' @export
#'
#' @examples
#' indir <- "C:/Users/Malcolm/Dropbox/rcode2/aMSE/data-raw"
#' pathkind(indir)
pathkind <- function(inpath) {
  kindpath <- "/"
  if (length(grep("\\\\",inpath)) > 0) kindpath <- "\\"
  return(kindpath)
} # end of pathkind

#' @title pathfinish determines what character is at the end of a path
#'
#' @description pathfinish determines what character is at the end of a
#'     path uses pathkind to get the separator and then checks the end
#'     character
#'
#' @param inpath the path to be analysed
#'
#' @return the end character of the path; either '', '/', or "\\"
#' @export
#'
#' @examples
#'   indir <- "C:/Users/Malcolm/Dropbox/rcode2/aMSE/data-raw"
#'   pathfinish(indir)
pathfinish <- function(inpath) {
  lookfor <- pathkind(inpath)
  endpath <- ""
  if (lookfor == "/") {
    if(length(grep("/$",inpath)) > 0) endpath <- "/"
  } else {
    if(length(grep("\\\\$",inpath)) > 0) endpath <- "\\"
  }
  return(endpath)
} # end of pathfinish

#' @title pathstart determines what character(s) is at the start of a path
#'
#' @description pathstart determines what character(s) is at the start of a
#'     path uses pathkind to get the separator and then checks the start
#'     character
#'
#' @param inpath the path to be analysed
#'
#' @return the start character of the path; either '', '/', or "\\"
#' @export
#'
#' @examples
#'   indir <- "C:/Users/Malcolm/Dropbox/rcode2/aMSE/data-raw"
#'   pathstart(indir)
pathstart <- function(inpath) {  # path2="A_CodeUse/aMSEDoc/figures/install_tar.gz_file.png"
  lookfor <- pathkind(inpath)
  startpath <- ""
  if (lookfor == "/") {
    if(length(grep("^/",inpath)) > 0) startpath <- "/"
  } else {
    if(length(grep("^\\\\",inpath)) > 0) startpath <- "\\"
  }
  return(startpath)
} # end of pathstart


#' @title pathtopath combines two paths accounting for type of separator
#'
#' @description pathtopath combines two paths, the second of which could
#'     include a filename, while taking into account the type of separator.
#'     It also prevents a doubling up or missing out of said separator
#'     between the two paths being joined. If the separator type differs
#'     between the two input paths the functions stops with a warning.
#'
#' @param path1 the first path to be added together
#' @param path2 the second path which is to be added to the first. This may
#'     include a filename is so desired.
#'
#' @return the combined path
#' @export
#'
#' @examples
#'   in1 <- "c:/users/Malcolm/DropBox"
#'   in2 <- "aMSEUse/scenarios/EG"
#'   pathtopath(in1, in2)  # no separator character between the paths
#'   in2 <- "/aMSEUse/scenarios/EG"
#'   pathtopath(in1, in2)  # path2 with a separator
#'   in1 <- "c:/users/Malcolm/DropBox/"
#'   pathtopath(in1, in2)  # both paths with a separator
#'   in1 <- "c:\\users\\Malcolm\\DropBox"
#'   in2 <- "aMSEUse\\scenarios\\EG"
#'   pathtopath(in1, in2)  # a different separator but missing from both
#'   in2 <- "/aMSEUse/scenarios/EG"
#'   pathtopath(in1, in2)  # a different separators
#'   in1 <- "c:/users/Malcolm/dropbox/"
#'   in2 <- "aMSEUse\\scenarios\\EG"
#'   pathtopath(in1, in2)  # a different separators the other way around
#'   in2 <- "filename.csv"
#'   pathtopath(in1, in2)  # join path to filename that has no spearator
pathtopath <- function (path1, path2) { # path1=rundir; path2="resultTable.csv"
  typepath <- pathkind(path1)  #
  typepath2 <- pathkind(path2)
  if (typepath != typepath2) { # path1="c:\\users\\Malcolm\\DropBox"; path2="/aMSEUse/scenarios/EG"
    p1 <- grep("\\\\",path1)
    if (length(p1) > 0) { # path1 uses \\
      tmp <- unlist(strsplit(path1,"\\\\"))
      path1 <- paste0(tmp,"/",collapse = "")
    } else {  # path2 assumed to use \\
      tmp <- unlist(strsplit(path2,"\\\\"))
      path2 <- paste0(tmp,"/",collapse = "")
    }
  }  
  endpath1 <- pathfinish(path1)
  startpath2 <- pathstart(path2)
  if ((nchar(endpath1) == 0) & (nchar(startpath2) == 0)) {
    outpath <- paste(path1, path2, sep = typepath)
  } else {
    if ((endpath1 != startpath2)) {
      outpath <- paste(path1,path2,sep="")
    } else {
      lenc <- nchar(typepath) + 1
      outpath <- paste(path1,substr(path2,lenc,nchar(path2)),sep="")
    }
  }
  return(outpath)
} # end of pathtopath

#' @title pkgfuns names all functions within a package
#'
#' @description pgkfuns when given the name of a loaded library gives the 
#'     names of all functions within that library sorted in alphebetical 
#'     order.
#'
#' @param packname the name of the package as character
#'
#' @return a character vector containing the names of all functions in the 
#'     named package
#' @export
#'
#' @examples
#' \dontrun{
#'   pkgfuns("graphics")
#'   pkgfuns("rutilsMH")
#' }
pkgfuns <- function(packname) { # packname=pkgname
  funcs <- names(.getNamespace(packname))
  pick <- grep("__",funcs)
  funcs <- funcs[-pick]
  pick <- which(funcs == ".packageName")
  funcs <- funcs[-pick]
  return(sort(funcs))
} # end of pgkfuns

#' @title splitDate - Generates a vector of date and time components
#'
#' @description splitDate - Generates a vector of date and time components,
#'     perhaps for inclusion in filenames or other labels; helpful for
#'     keeping different run outputs seperate and identifiable.
#' @param dat - a system time from Sys.time() to be broken in components;
#'     defaults to NA, whereupon the current time is used.
#' @return a vector od characters relating to 'Year', 'Month', 'Day','Time',
#'     and a DateTime, which is a combination of all of these suitable for
#'     inclusion in a filename.
#' @export
#' @examples
#' \dontrun{
#' tmp <- splitDate()
#' print(tmp)
#' print(names(tmp))
#' print(as.numeric(tmp[1:3]))
#' print(tmp["DateTime"])
#' }
splitDate <- function(dat=NA) {
  if(is.na(dat)) dat <- as.POSIXlt(Sys.time())
  out <- unlist(dat)
  tim <- paste(trunc(as.numeric(out[3])),trunc(as.numeric(out[2])),
               "_",trunc(as.numeric(out[1]),1),sep="")
  day <- as.character(trunc(as.numeric(out[4])))
  month <- as.character(trunc(as.numeric(out[5])) + 1)
  if (nchar(month) == 1) month <- paste(0,month,sep="")
  year <- as.character(trunc(as.numeric(out[6])) - 100)
  combined <- paste(year,month,day,"_",tim,sep="")
  ans <- c(year,month,day,tim,combined)
  names(ans) <- c("Year","Month","Day","Time","DateTime")
  return(ans)
} # end of split_Date

# rmdutils------------------------------------


#' @title digitsbyrow a helper function for knitr, to specify formats by row
#'
#' @description digitsbyrow is a solution obtained from StackOverFlow, suggested
#'     by Tim Bainbridge in 11/12/19. knitr formats table columns as a whole,
#'     which can be a problem if one wants to mix integers with real numbers in
#'     the same columns. This first transposes the data.frame/matrix being
#'     printed, fixes the formats, and then transposes it back. In knitr one
#'     then needs to use the align argument to fix the alignment. In may version
#'     I have conserved both rownames and colnames for both data.frames and
#'     matrices (the original only did so for data.frames but I often print
#'     matrices). digitsbyrow converts all entries to character so knitr becomes
#'     necessary for printing.
#'
#' @param df the data.frame or matrix to be printed by knitr
#' @param digits a vector of the digits wanted for each row of the df or matrix
#'
#' @return a formatted data.frame or matrix depending on input
#' @export
#'
#' @examples
#' x <- matrix(c(rnorm(5,mean=5,sd=1),seq(1,10,1)),nrow=3,ncol=5,byrow=TRUE,
#'             dimnames=list(1:3,1:5))
#' digitsbyrow(x, c(3,0,0))
#' # needs knitr to use kable
#' # kable(digitsbyrow(x, c(3,0,0)),align='r',row.names=TRUE)
digitsbyrow <- function(df, digits) {
  tmp0 <- data.frame(t(df))
  tmp1 <- mapply(
    function(df0, digits0) {
      formatC(df0, format="f", digits=digits0)
    },
    df0=tmp0, digits0=digits
  )
  tmp1 <- data.frame(t(tmp1))
  rownames(tmp1) <- rownames(df)
  colnames(tmp1) <- colnames(df)
  if (inherits(df[1],"matrix")) tmp1 <- as.matrix(tmp1)
  return(tmp1)
} # end of digitsbyrow

#' @title halftable halves the height of a tall narrow data.frame
#'
#' @description halftable would be used when printing a table using kable
#'     from knitr where one of the columns was Year. The objective would be 
#'     to split the table in half taking the bottom half and attaching it on
#'     the right hand side of the top half. The year column would act as the
#'     index.
#'
#' @param inmat the data.frame to be subdivided
#' @param yearcol the column name of the year field
#' @param subdiv the number of times the data.frame should be subdivided;
#'     the default is 3 but the numbers can only be 2 or 3.
#'
#' @return a data.frame half the height and double the width of the original
#' @export
#'
#' @examples
#' \dontrun{
#' x <- as.data.frame(matrix(runif(80),nrow=20,ncol=4))
#' x[,1] <- 1986:2005
#' x[,4] <- paste0("text",1:20)
#' halftable(x,yearcol="V1",subdiv=2)
#' halftable(x[,c(1,2,4)],yearcol="V1")
#' x1 <- rbind(x,x[1,])
#' x1[21,"V1"] <- 2006
#' halftable(x1,yearcol="V1",subdiv=3)
#' }
halftable <- function(inmat,yearcol="Year",subdiv=3) {
  if (!(subdiv %in% c(2,3))) stop("\n subdiv must be 2 or 3 \n")
  numrow <- dim(inmat)[1]
  numcol <- dim(inmat)[2]
  extra <- rep(NA,numcol)
  if ((numrow %% subdiv) == 0) {
    newnr <- numrow/subdiv
    incomplete <- FALSE
  } else {
    newnr <- trunc(numrow/subdiv) + 1
    incomplete <- TRUE
  }
  # years <- inmat[,yearcol]
  first <- inmat[1:newnr,]
  if (subdiv == 2) {
    second <- inmat[-c(1:newnr),]
    diff <- (nrow(first) - nrow(second))
    if (diff > 0) {
      numcol <- ncol(inmat)
      third <- rbind(second,extra)
    } else {
      third <- second
    }
  } else {
    second <- inmat[c(newnr+1):c(2*newnr),]
    first <- cbind(first,second)
    third <- inmat[-c(1:(2*newnr)),]
    diff <- nrow(first) - nrow(third)
    if (diff > 0) third <- rbind(third,extra)
    if (diff > 1) third <- rbind(third,extra)
  }
  outmat <- cbind(first,third)
  rownames(outmat) <- 1:newnr
  return(outmat)
} # end of halftable





#' @title kablerow a replacement for knitr::kable which enables row formatting
#'
#' @description knitr::kable enables one to round the number of digits for each
#'     column of a table. However, sometimes one wants to format the rows and
#'     not the columns. kablerow enables that while using the kable function.
#'     It rounds the rows to the desired number of digits and then converts
#'     those rounded values to characters, which kable can then print more
#'     appropriately.
#'
#' @param x an input matrix or data.frame
#' @param rowdigits the number of digits desired for each row
#' @param namerows should row.names be printed; default=NA. change to TRUE for
#'     row.names printing
#' @param namecols should col.names be printed; default=NA (which prints V1, V2
#'     ,V3, ...)
#'
#' @return Nothing but it does use knitr::kable to print a formatted matrix
#' @export
#'
#' @examples
#' x <- matrix(rnorm(25,mean=5,sd=1),nrow=5,ncol=5)
#' colnames(x) <- 1:5
#' numdig <- c(2,3,4,3,2)
#' rownames(x) <- c("a","b","c","d","e")
#' kablerow(x,rowdigits=c(2,3,4,3,2),namerows=TRUE)
kablerow <- function(x,rowdigits,namerows=NA,namecols=NA) { # x=x; rowdigits=c(2,3,4,3,2); namerows=FALSE
  xr <- as.data.frame(x)
  num <- nrow(x)
  for (i in 1:num) {
    x[i,] <- round(x[i,],rowdigits[i])
    xr[i,] <- as.character(x[i,])
  }
  kable(xr,align="r",row.names=namerows,col.names=namecols)
} # end of kablerow


#' @title listExamples lists all the examples in a package R file
#'
#' @description listExamples lists all the examples in a package R file. It
#'     comments out the first line number and any dontrun statements along 
#'     with their following curly bracket.
#'
#' @param infile - a character variable containing the path and filename
#' @return Creates an R file in the working directory and prints its name to
#'     the console
#'
#' @export
#' @examples
#' \dontrun{
#' txt <- vector("character",4)
#' txt[1] <- "#' @examples "
#' txt[2] <- "#' /dontrun{"
#' txt[3] <- "#' print("This is an example of using listExamples")"
#' txt[4] <- "#' }"
#' infile <- textConnection(txt)
#' listExamples(infile)
#' }
listExamples <- function(infile) {  
  outfile <- paste0("examples_",tail(unlist(strsplit(infile,"/")),1))
  cat("All the example code from  \n",file=outfile,append=FALSE)
  cat(infile,"\n\n",file=outfile,append=TRUE)
  content <- readLines(con=infile)
  egLines <- grep("@examples",content)
  funlines <- grep("<- function",content)
  nline <- length(egLines)
  for (i in 1:nline) {
    # find extent of example  i = 1
    count <- 1
    cat("#Linenumber: ",egLines[i] + count,"\n",file=outfile,append=TRUE)
    repeat {  # i=1; count=1
      tmpline <- content[egLines[i] + count]
      if (substr(tmpline,1,1) == "#") {
        lenc <- nchar(tmpline)
        if ((length(grep("dontrun",tmpline)) > 0) |
            (length(grep("#' }",tmpline)) > 0)) {
          cat("# ",tmpline,file=outfile,append=TRUE)
        }
        cat(substr(tmpline,3,lenc),"\n",file=outfile,append=TRUE)
        count <- count + 1
      } else {
        cat("# ",tmpline,"\n",file=outfile,append=TRUE)
        cat("\n\n\n\n",file=outfile,append=TRUE)
        break()
      }
    }
  }
  print(outfile)
} # end of list_Examples

#' @title lininterpol - linearly interpolate values in a vector with NAs
#'
#' @description lininterpol - linearly interpolate values in a vector with 
#'     NAs. A common problem when plotting up time series is where there are
#'     missing values or NAs the plotted line will have gaps, one can always
#'     plot points on top of a line to identify where there are missing 
#'     values but an alternative would be to interpolate the missing values 
#'     linearly and plot that line as a dashed line. This function generates
#'     those linear interpolations. The input vector cannot have missing 
#'     values at the beginning or the end. If there are no missing values 
#'     the original vector is returned
#'
#' @param invect - the vector of values including missing values
#'
#' @return invect but with NAs replaced with linearly interpolated values.
#' @export
#'
#' @examples
#' \dontrun{
#'  Expt <- c(20102,18465,16826,15333,14355,NA,13843.7,NA,NA,NA,15180)
#'  lininterpol(Expt)
#' }
lininterpol <- function(invect) { 
  npt <- length(invect)
  answer <- invect
  pickNA <- which(is.na(invect))
  nna <- length(pickNA)
  if (nna == 0) return(invect)  # no NAs
  if ((pickNA[1] == 1) | (pickNA[nna] == nna))
    #   picknNA <- which(invect > 0)
    stop("input vector in lin-interpol cannot start or end with an NA")
  # identify groups of NAs
  group <- c(pickNA[1])
  count <- 1
  ans <- vector("list",nna) # possible each NA is an individual
  for (i in 2:nna) {
    if ((pickNA[i] - pickNA[(i-1)]) > 1) {
      ans[[count]] <- group
      group <- pickNA[i]
      count <- count + 1
    } else {
      group <- c(group,pickNA[i])
    }
  }
  ans[[count]] <- group
  for (i in 1:count) {  # i <- 2
    pickNA <- ans[[i]]
    begin <- (pickNA[1] - 1)
    finish <- (tail(pickNA,1) + 1)
    first <- invect[begin]
    second <- invect[finish]
    answer[begin:finish] <- seq(first,second,length=(length(pickNA) + 2))
  }
  return(answer)
}  # end of lin_interpol

#' @title listfuns produces a listing of all functions in an input R file
#'
#' @description listfuns reads in a given R file and then identifies each
#'     function header within it and pulls out the function name, its syntax,
#'     the line-number in the file, and associates that with the filename.
#'
#' @param infile the R file to be examined
#'
#' @return a data.frame of syntax, function name, line number, and file name
#' @export
#'
#' @examples
#' print("wait for an example")
listfuns <- function(infile) { # infile=paste0(indir,files[i])
  content <- readLines(con=infile)
  if (length(grep("/",infile) > 0)) {
    rfun <- tail(unlist(strsplit(infile,"/")),1)
    rfile <- substr(rfun,1,nchar(rfun)-2)
  } else {
    rfile <- infile
  }
  funLines <- grep("function",content)
  testhash <- substr(content[funLines],1,4)
  omit <- grep("#",testhash)
  if (length(omit) > 0) {
    funLines <- funLines[-omit]
    testhash <- testhash[-omit]
  }
  omit2 <- grep("  ",testhash) # remove functions internal to other functions
  if (length(omit2) > 0) funLines <- funLines[-omit2]
  nLine <- length(funLines)
  delF <- NULL
  if (nLine > 0) {
    for (i in 1:nLine) {
      tmpLine <- gsub(" ","",content[funLines[i]])
      if ((length(grep("function\\(",tmpLine)) == 0) |
          (substr(tmpLine,1,2) == "#'") |
          (length(grep("<-function",tmpLine)) == 0) |
          (length(grep("} #",tmpLine)) > 0)) delF <- c(delF,i)
    }
  }  
  ndelF <- length(delF)
  if (ndelF > 0) {
    funLines <- funLines[-delF]
  }
  if (ndelF == nLine) {
    txt <- paste0(infile,"  contained no recognizable functions")
    warning(cat(txt,"\n"))
    out <- "NA"
    funnames <- ""
    funLines <- 1
    n <- 1
  } else {
    outlines <- sort(c(funLines))
    out <- content[outlines]
    funnames <- out
    n <- length(out)
    for (i in 1:n) {  # i=1
      out[i] <- gsub(" ","",(unlist(strsplit(out[i],"\\{")))[1])
      funnames[i] <- removeEmpty(unlist(strsplit(out[i],"<-"))[1])
      out[i] <- gsub("<-function","",out[i])
    }
  }
  columns <- c("linenumber","file","functions","references","syntax")
  rows <- paste0(rfile,1:n)
  outfuns <- as.data.frame(matrix(NA,nrow=n,ncol=length(columns),
                                  dimnames=list(rows,columns)))
  outfuns[,"syntax"] <- out
  outfuns[,"functions"] <- funnames
  outfuns[,"linenumber"] <- funLines
  outfuns[,"file"] <- rfile
  return(outfuns)
} # end of listfuns


#' @title rmdcss generates some initial css style code for HTML Rmd files
#' 
#' @description rmdcss generates some initial css style code for HTML Rmd files
#'     as well as a mathjax script that will generate equation numbers for any
#'     display equations in the document. This prints the css style code and
#'     the mathjax script to the console from where it should be pasted into the
#'     Rmd file immediately following the YAML header. It now contains font 
#'     sizes for the h1 heaqder and the .inline and .display math classes
#'
#' @return nothing but it prints css style code and a mathjax script to the 
#'     console
#' @export
#'
#' @examples
#' rmdcss()
rmdcss <- function() {
  cat('<style type="text/css"> \n',
      '  body, td { \n',
      '  font-size: 16px; \n',
      '  font-family: "Times New Roman", Times, serif; \n',
      '} \n')
  cat('code.r{ \n',
      '  font-size: 15px; \n',
      '} \n')
  cat('pre {  \n',
      'font-size: 8px  \n',
      '}  \n')
  cat('h1 {  \n',
      '  font-size: 32px  \n',
      '}  \n')
  cat('.inline{font-size: 15px; } \n',
      '.display{font-size: 18px;} \n',
      '<','/style>  \n')
  cat('\n\n')
  cat('<script type="text/x-mathjax-config">  \n',
      '  MathJax.Hub.Config({  \n',
      '    TeX: {   \n',
      '      equationNumbers: {   \n',
      '        autoNumber: "all",  \n',
      '        formatNumber: function (n) {return ',3.,'+n}  \n',
      '      }  \n', 
      '    }  \n',
      '  });  \n',
      '<','/script>  \n')
} # end of rmdcss

#' @title setuprmd sets up and Rmd file ready to generate an HTML file
#' 
#' @description setuprmd sets up a custom Rmd file for generating an HTML file,
#'     which better suits my own preferences
#'
#' @param filen the full path filename for the final Rmd file. Ensure its 
#'     filetype = .Rmd. The default = "", which write the custom text to the 
#'     console.
#'
#' @return nothing but it does write a file to one's hard drive in the location
#'    listed in filen
#' @export
#'
#' @examples
#' setuprmd(filen="")
setuprmd <- function(filen="") {
  cat('--- \n',
      'title: "Title" \n',
      'author: Malcolm Haddon \n',
      'date: "`r Sys.time()`"  \n',
      'output: \n',
      '  html_document:   \n',
      '    df_print: paged    \n',
      '    fig_caption: yes \n',
      '    fig_height: 5.5 \n',
      '    fig_width: 6.5\n',
      '    number_section: yes \n',
      # '    toc: yes \n',
      # '    toc_depth: 2 \n',
      '---  \n',
      sep = "", file=filen, append=FALSE)
  cat('  \n',
      '```{r setup, include=FALSE}  \n',
      'knitr::opts_chunk$set(  \n',
      '  echo = FALSE,  \n',
      '  message = FALSE,  \n',
      '  warning = FALSE)  \n\n',
      'options(knitr.kable.NA = "", \n',
      '        knitr.table.format = "pandoc")  \n',
      '  \n\n',
      sep = "", file=filen, append=TRUE)
  cat('options("show.signif.stars"=FALSE,  \n',
      '        "stringsAsFactors"=FALSE,   \n',
      '        "max.print"=50000,          \n',
      '        "width"=240)                \n',
      '```  \n\n',
      sep = "", file=filen, append=TRUE)
  # cat('   \n',
  #     '<style type="text/css">  \n',
  #     '   body, td, h1, h2, h3, h4 {  \n',
  #     '   font-size: 16px;   \n',
  #     '   font-family: "Times New Roman" Times, serif; \n',
  #     '}   \n',
  #     '< /style>   \n\n\n',
  #     sep = "", file=filen, append=TRUE)
} # end of setuprmd

#' @title '%ni%' identifies which element in x is NOT in y
#'
#' @param x a vector of elements which can be numeric or character
#' @param y a vector of elements which can be numeric or character
#'
#' @export
#' 
#' @examples
#'   x <- 1:10
#'   y <- 6:18
#'   x %ni% y
#'   pick <- (x %ni% y)
#'   x[pick]
`%ni%` <- function(x,y) {
  !(x %in% y)
}
haddonm/codeutils documentation built on April 15, 2024, 1:02 p.m.