#' Test Integers are in Sequence
#'
#' Test if items in an integer vector are within a sequence.
#' @param x an integer vector, or a vector that can be coerced to integer by \code{as.integer(x)}.
#' @return If items are within a sequence (a run of 3 or more integers), TRUE is returned, otherwise FALSE is returned. NAs are also returned as FALSE.
#' @examples
#' in.seq(c(1:3,NA,5:7))
#' in.seq(as.character(1:5))
#' @export
in.seq <- function(x)
{
	x <- as.integer(x)
	if (length(x) < 3)
		return(x & FALSE)
	d1 <- c(diff(x, 1), NA)
	d2 <- c(NA, diff(x, 2), NA)
	result <- ((d1 == 1 & d2 == 2) | (d1 == -1 & d2 == -2))
	result[is.na(result)] <- FALSE
	return(result)
}
		
#' Escape Regex Charaters in a String
#'
#' Regex metacharacters to escape: \code{. \\ | ( ) [ \{ ^ $ * + ?}
#' @param x a character vector, or an object that can be coerced to character by \code{as.character(x)}.
#' @return Charaters in x are escaped, e.g., the dot "." is escaped to "\\.".
#' @examples
#' pattern <- escRegex(".")
#' gsub(pattern, '-', '12.05.2018')
#' @export
escRegex <- function(x)
{
	return(gsub("([.\\|()[\\{^$*+?])", "\\\\\\1", x))
}

#' Contract sequences with continous elements as ranges
#'
#' @param x a numeric integer vector, or string or character vector that can be coerced into a numeric vector.
#' @param rng a character string to indicate a range.
#' @param sep a character srting for separating values.
#' @param conj a charater string to separate the last to values instead of sep.
#' @param empty a charater string to returned, if processing of \code{x} fails.
#' @return A formatted string with ranges contracted and each element delimited as spedcific by \code{sep} and \code{conj}.
#' @examples
#' s <- "1,2,3,4,8,9,16,15,14,19"
#' contractSeqs(s)
#' s1 <- unlist(strsplit(s, ","))
#' contractSeqs(s1)
#' contractSeqs(as.numeric(s1), sep= "; ", conj = " and ")
#' @rdname contractSeqs
#' @export
contractSeqs <- function(x, rng = "-", sep = ",", conj = sep, empty = "")
	UseMethod("contractSeqs", x)
#' @return 
#'
#' @rdname contractSeqs
#' @method contractSeqs numeric
#' @export
contractSeqs.numeric <- function(x, rng = "-", sep = ",", conj = sep, empty = "")
{
	pattern1 <- sprintf("(%s){2,}", escRegex(sep))
	pattern2 <- sprintf("%s(((?!%s).){1,}$)", escRegex(sep), escRegex(sep))
	pattern3 <- sprintf("%s\\1", conj)
	x[in.seq(x)] <- ""
	result <- gsub(pattern1, rng, paste(x, collapse = sep), perl = TRUE)
	if (conj != sep) 
		result <- gsub(pattern2, pattern3, result, perl = TRUE)
	return(result)
}
#' @return
#'
#' @rdname contractSeqs
#' @method contractSeqs character
#' @export
contractSeqs.character <- function(x, rng = "-", sep = ",", conj = sep, empty = "")
{
	msg <- "%s could not be coerced to numeric: returning empty string"
	ln <- length(x)
	if (ln == 0) return(empty)
	if (ln == 1) msg <- sprintf(msg, "string")
	if (ln > 1) {
		x <- toString(x) #for character vectors
		msg <- sprintf(msg, "vector")
	}
	x <- try(eval(parse(text=sprintf("c(%s)", x))), silent = TRUE)
	if (is.numeric(x)) {
		contractSeqs(x, rng = rng, sep = sep, conj = conj, empty = empty)
	} else {
		warning(msg)
		return(empty)
	}
}
#' @return 
#'
#' @rdname contractSeqs
#' @method contractSeqs default
#' @export
contractSeqs.default <- function(x, ...)
{
	warning("argument is not character or numeric: returning empty string")
	return(empty)
}
