getRvers <- function(){
	vers <- R.version$version.string
	version.string <- gsub("\\(.*?\\)","",vers,perl=FALSE)
	return(getvers(version.string))
}

getvers <- function(version.string){
	vers <- gsub("[[:space:]]","",gsub("[[:alpha:]]","",version.string))
	vers <- gsub("[[:punct:]]", "", gsub("."," ",vers, fixed=TRUE))
	vers <- strsplit(vers," ",fixed=TRUE)[[1]]
	version <- (10^((length(vers)-1):0))^3 * as.numeric(vers)
	version <- sum(version)
	return(version)
}

getjagsnames <- function(targets){
	
	retval <- unlist(lapply(1:length(targets), function(i){
	
		x <- targets[[i]]
		n <- names(targets)[i]
		new <- as.list(x)

		if(length(x)==1){
			names(new) <- n
			return(new)
		}
		if(!is.array(x)){
			names(new) <- paste(n, "[",1:length(x),"]",sep="")
			return(new)
		}else{
			dims <- apply(expand.grid(lapply(dim(x), function(x) return(1:x))),1,paste,collapse=",")
			names(new) <- paste(n,"[",dims,"]",sep="")
			return(new)
		}
		stop(paste("Unsupported argument type:",class(x)))
	}))
	
	return(retval)
}

# Currently used only by glm.template - rjags and JAGS now respect initial ordering of variables:
alphabeticalvars <- function(x, always.last=c('resid.sum.sq','deviance')){
	
	stopifnot(class(x)=='character')
	stopifnot(class(always.last)=='character')
	stopifnot(all(!is.na(always.last)))

	splits <- strsplit(gsub(']','',x,fixed=TRUE),'[',fixed=TRUE)	
	mnames <- sapply(splits, function(x) return(x[[1]]))

	indices <- lapply(splits, function(x){
		if(length(x)==1)
			return(numeric(0))
		else
			return(as.numeric(strsplit(x[2],',')[[1]]))
		})

	lengths <- sapply(indices,length)
	maxind <- max(lengths)+1

	sortmat <- matrix(0, nrow=length(x), ncol=maxind)
	for(i in 1:length(x)){
		if(lengths[i]>0)
			sortmat[i,2:(lengths[i]+1)] <- indices[[i]]
	}
	sortmat[,1] <- rank(mnames, ties.method='average')
	
	# If any match the always lasts chars:
	if(length(always.last)>0){
		for(i in 1:length(always.last)){
			sortmat[which(x==always.last[i]),1] <- nrow(sortmat)+i
		}
	}
	
	arglist <- lapply(1:ncol(sortmat), function(x) return(as.numeric(sortmat[,x])))
	return(x[do.call('order', args=arglist)])
	
}

getarraynames <- function(targets){
	
	vars <- gsub('\\[[[:print:]]*\\]','',names(targets))
	varnames <- unique(vars)
	indexes <- gsub("[[:alpha:][:punct:]]", "", gsub(","," ",names(targets)))
	indexes[indexes==""] <- 1
	dimensions <- lapply(indexes, function(y){		
		if(!grepl(' ',y)) return(as.numeric(y)) else return(as.numeric(strsplit(y, split=" ",fixed=TRUE)[[1]]))		})
	
	if(any(sapply(varnames, function(x) return(grepl(',',x)))))
		stop("Invalid symbol in variable name")
	
	retlist <- lapply(varnames, function(x){
		ds <- sapply(dimensions[vars==x],length)
		stopifnot(all(ds==ds[1]))
		thisdim <- sapply(dimensions[vars==x], function(y) return(y))
		if(is.null(dim(thisdim))) dim(thisdim) <- c(1,length(thisdim))
    dims <- apply(thisdim,1,max)
		newarr <- targets[vars==x]
		dim(newarr) <- dims
		return(newarr)
	})
	names(retlist) <- varnames
	
	return(retlist)
	
}

makerunjagsobject <- function(combinedoutput, summarise, summaryargs, burnin, sample, thin, model, data, monitor, noread.monitor, modules, factories, response, residual, fitted, method, method.options, timetaken, silent=FALSE){
	
	starttime <- Sys.time()
	
	summaryargs <- getargs('add.summary', summaryargs)
	evalsumargs <- lapply(summaryargs, eval, envir = parent.frame(), enclos=baseenv())   # some get evaluated to NULL so must set up a new vector to avoid indexing problems - this scoping means check the parent frame first, but find runjags.getOption in runjags environment
	
	combinedoutput <- combinedoutput[names(combinedoutput)%in%c('mcmc','deviance.table','deviance.sum','pd','end.state','samplers')]
	stopifnot(identical(names(combinedoutput), c('mcmc','deviance.table','deviance.sum','pd','end.state','samplers')))
	
	combinedoutput <- c(combinedoutput, list(burnin=burnin, sample=niter(combinedoutput$mcmc), thin=thin, model=model, data=data, monitor=monitor, noread.monitor=noread.monitor, modules=modules, factories=factories, response=response, residual=residual, fitted=fitted, method=method, method.options=method.options, timetaken=timetaken, runjags.version=c(runjagsprivate$runjagsversion, R.Version()$version.string, .Platform$OS.type, .Platform$GUI, .Platform$pkgType, format(Sys.time()))))
	class(combinedoutput) <- 'runjags'
	combinedoutput$summary.pars <- getdefaultsummarypars()
	
	if(is.na(summarise)) summarise <- TRUE
	# If too many vars (>50?), don't summarise or plot unless forced to:
	if(summarise && !runjags.getOption('force.summary') && nvar(combinedoutput$mcmc)>50){
		swcat("Note: Summary statistics were not produced as there are >50 monitored variables\n[To override this behaviour see ?add.summary and ?runjags.options]", call.=FALSE)
		summarise <- FALSE
	}
		
	# Call function to calculate summary statistics and plots etc:
	if(summarise){
		success <- try({
		summaries <- do.call('add.summary', c(list(runjags.object=combinedoutput), summaryargs))
		}, silent=TRUE)
		if(!silent && inherits(success, 'try-error')){
			warning(paste('The following error occured while calculating summary statistics:\n  ', gsub('\n$','',as.character(success)), sep=''),call.=FALSE)
			message <- "An error occured while calculating summary statistics - see ?add.summary"
			summaries <- list(summary=message, HPD=message, hpd=message, mcse=message, psrf=message, autocorr=message, crosscorr=message, stochastic=message, trace=message, density=message, hist=message, ecdfplot=message, key=message, acplot=message, ccplot=message, summaries=NULL, summary.available=FALSE, summary.pars=evalsumargs)
		}
	}else{
		message <- "Summary statistics are not stored internally when summarise=FALSE - see ?add.summary"
		summaries <- list(summary=message, HPD=message, hpd=message, mcse=message, psrf=message, autocorr=message, crosscorr=message, stochastic=message, trace=message, density=message, hist=message, ecdfplot=message, key=message, acplot=message, ccplot=message, summaries=NULL, summary.available=FALSE, summary.pars=evalsumargs)
	}
	if(any(c('dic','ped') %in% combinedoutput$monitor)){
		summaries <- c(summaries, list(dic=runjags.dic(deviance.table=combinedoutput$deviance.table, deviance.sum=combinedoutput$deviance.sum, mcmclist=combinedoutput$mcmc)))
	}else{
		summaries <- c(summaries, list(dic="DIC statistics have not been stored internally - see ?extract.runjags"))
	}

	combinedoutput <- combinedoutput[! names(combinedoutput)%in%names(summaries)]
	combinedoutput <- c(combinedoutput, summaries)
	class(combinedoutput) <- 'runjags'
	
	class(combinedoutput$model) <- 'runjagsmodel'
	class(combinedoutput$data) <- 'runjagsdata'
	class(combinedoutput$end.state) <- 'runjagsinits'

	class(combinedoutput) <- 'runjags'
	
	# Check to see if we have identical RNG states after the run:
	n.chains <- nchain(combinedoutput$mcmc)
	if(!silent && n.chains > 1 && any(combinedoutput$end.state[2:n.chains]==combinedoutput$end.state[1])){
		warning("Identical RNG states have been produced for multiple chains - this probably means the chains are identical.  Try again using different parameter values and/or RNG samplers as inits.", call.=FALSE)
	}
	
	# If the model was compiled previously with lecuyer loaded but not listed as a module the lecuyer module will still not be listed, so add it:
	if(any(grepl('lecuyer::RngStream', combinedoutput$end.state, fixed=TRUE)))
	  combinedoutput$modules <- c(combinedoutput$modules, list(c("lecuyer","TRUE")))
	
	combinedoutput$modules <- checkmodfact(combinedoutput$modules,'module')
	combinedoutput$factories <- checkmodfact(combinedoutput$factories,'factory')
	
	# Add the summarise time to the time taken:
	combinedoutput$timetaken <- combinedoutput$timetaken + difftime(Sys.time(), starttime)
	
	return(combinedoutput)
}

swcat <- function(...){
	
	if(!runjags.getOption('silent.runjags')){
		pargs <- list(...)
		pasted <- do.call(paste, pargs)
		pasted <- gsub('\r', '\n', pasted)
	
		# White space is destroyed by strwrap so preserve \n by splitting on them (and append a ' ' [which is removed by strwrap anyway] to preserve any trailing \n)
		pasted <- unlist(strsplit(paste(pasted,' ',sep=''), '\n'))
		pasted <- strwrap(pasted)
		cat(paste(pasted, collapse='\n'))
	}
}

versionmatch <- function(required, actual){
	actual <- as.character(actual)
	
	matched <- FALSE
	for(r in as.character(required)){
		# Default:
		type <- "gteq"
		# If only an equals match precise version:
		if(grepl("=", r, fixed=TRUE)) type <- "eq"
		# Greater than takes precedence:
		if(grepl(">", r, fixed=TRUE)) type <- "gt"
		# Greater than or equal also possible:
		if(grepl(">=", r, fixed=TRUE)) type <- "gteq"	
		r <- gsub(">|=", "", r)
		if((compareVersion(actual, r)==0) & (type=="eq" | type=="gteq")){
			matched <- TRUE
		}
		if((compareVersion(actual, r)==1) & (type=="gt" | type=="gteq")){
			matched <- TRUE
		}
	}
	
	return(matched)	
}


winbugs.extract.big <- function(find, string, remove.list=TRUE){

	stopifnot(length(find)==1)
	stopifnot(length(string)==1)
	# There will not be any # except for the bugsdata jagsdata etc comments with the rest of the line cleared

	if(identical(string, '\n') || identical(string, '\r') || identical(string, '') || !grepl(find, string, ignore.case=TRUE))
	  return(list('', listfound=FALSE))
  
	# Make sure \r are now \n and \t are :
	string <- gsub('\r', '\n', string)
	string <- gsub('\t', ' ', string)

	# Remove white space at the beginning of lines but make sure a \n starts the string otherwise we are in trouble:
	string <- gsub('\n[[:space:]]*', '\n', paste('\n', string, sep=''))


	#########   Step 1:  separate blocks on the find string:

	# Now for the find to be valid it must be following a \n and before a {
	if(!grepl(paste('\n', find, '[[:space:]]*\\{',sep=''), string, ignore.case=TRUE))
	  return(list('', listfound=FALSE))

	# Now we have to turn these into lowercase because strsplit doesnt have an ignore.case argument:
	string <- gsub(paste('\n', find, '[[:space:]]*\\{',sep=''), paste('\n', find, ' \\{',sep=''), string, ignore.case=TRUE)

	# Now we can split on the find as we know there is at least 1, so 2 fragments after splitting:
	sepstrings <- strsplit(string, paste('\n', find, ' \\{',sep=''))[[1]]
	stopifnot(length(sepstrings)>=2)
	sepstrings <- sapply(sepstrings, function(x) return(paste(find, ' {\n', x, sep='')))[2:length(sepstrings)]
	names(sepstrings) <- NULL

	# Now the start of the strings is correct, but there may be crap after the closing } (but we might have a loop inside the string to keep):
	cutstrings <- lapply(sepstrings, function(s){
		# Use gregexpr to find the locations of all { and }:
		openbraces <- as.numeric(gregexpr('\\{', s)[[1]])
		closebraces <- as.numeric(gregexpr('\\}', s)[[1]])
		# Need to not count { } on commented lines, but also leave comment lines as they are - this finds where commented { } are:
		commented <- gregexpr("#(.*?)\\{", s, perl=TRUE)[[1]]
		openbraces <- openbraces[!openbraces %in% (as.numeric(commented)+(attr(commented, 'match.length')-1))]
		commented <- gregexpr("#(.*?)\\}", s, perl=TRUE)[[1]]
		closebraces <- closebraces[!closebraces %in% (as.numeric(commented)+(attr(commented, 'match.length')-1))]
		
		# Sanity check - an open brace should be right at the start:
		stopifnot(min(openbraces) < min(closebraces))
		# If we have different numbers of close brackets than open brackets, bail out:
		if(length(openbraces) > length(closebraces))
			stop(paste('There was an unmatched number of { and } found following a ', find, ' block - ensure that the text "', find, '{" is not used inside another block', sep=''), call.=FALSE)
		# This will tell us which closebrace corresponds to the last open brace:
		bracesdone <- which(sapply(1:length(closebraces), function(b) return(sum(openbraces < closebraces[b])==b)))[1]
		# And the matching string to return (from after the opening to before the closing bracket):
		return(substr(s, openbraces[1]+1, closebraces[bracesdone]-1))
	})
	cutstrings <- sapply(cutstrings, paste, '\n', sep='')
	# Remove leading whitespace again:
	cutstrings <- sub('^[[:space:]]*','',cutstrings)
	# And trailing whitespace again:
	cutstrings <- sub('[[:space:]]*$','',cutstrings)
	
	listsfound <- grepl('[[:space:]]*list[[:space:]]*\\(', cutstrings, ignore.case=TRUE)

	return(list(cutstrings, listfound=listsfound))

	#### We no longer separate/remove list here but the code was:
	
	########  Step 2:  separate and remove list( )

	# # If we don't have any or aren't removing them just bail:
	# listsfound <- grepl('[[:space:]]*list[[:space:]]*\\(', cutstrings, ignore.case=TRUE)
	# if(!any(listsfound) || !remove.list)
	#   return(list(cutstrings, listfound=listsfound))
	# 
	# # Otherwise we need to remove the list (
	# cutstrings <- gsub(paste('[[:space:]]*list[[:space:]]*\\(',sep=''), ' ', cutstrings, ignore.case=TRUE)
	# # And the last closebracket found (assume this is correct):
	# tozap <- cutstrings[listsfound]
	# tozap <- sapply(tozap, function(s){
	#   closebraces <- as.numeric(gregexpr('\\)', s)[[1]])
	#   toremove <- closebraces[length(closebraces)]
	#   substr(s, toremove, toremove) <- ' '
	#   return(s)
	# })
	# cutstrings[listsfound] <- tozap
	# 
	# # Remove leading whitespace again:
	# cutstrings <- sub('^[[:space:]]*','',cutstrings)
	# # And trailing whitespace again:
	# cutstrings <- sub('[[:space:]]*$','',cutstrings)
	# 
	# # And return cutstrings:
	# return(list(cutstrings, listfound=listsfound))

}


sortjagsvsbugs <- function(maindata, data.type=TRUE){
	
	# This is all worded for data, but it applies equally to inits as well
	feedtxt <- if(data.type) 'A data' else 'An inits'
	
	# Note that the list( ) will already have been removed - datalistfound tells us if it was there:	
	datalistfound <- maindata[[2]]
	maindata <- maindata[[1]]
	
	transwarngiven <- FALSE
	
	modelret <- ''
	for(i in 1:length(maindata)){
		
		if(maindata[i]=='')
			next
		
		# Manual override mechanism for data types
    	tofinds <- tolower(maindata[i])
		bugsdata <- grepl("#bugsdata#", tofinds)   	# =fixed
		jagsdata <- grepl("#rdata#", tofinds)  	# =fixed
		modeldata <- grepl("#modeldata#", tofinds)
		
		# Removes any  character between '#' and a new line
		maindata[i] <- gsub("#(.*?)\n", "\n", maindata[i], perl=TRUE)
		
		# Fixed means it is to be read as data (jags or bugs column/row major order), model means part of the model:
		# models don't contain .Dim, c(, ' or " (I think)
		likelyfixed <- any(c(grepl(".Dim", maindata[i]), datalistfound[i], grepl("c\\(", maindata[i]), grepl("'", maindata[i]), grepl('"', maindata[i])))
		# fixed data doesn't contain [ or for(
		likelymodel <- data.type && any(c(grepl("\\[", maindata[i]), grepl("for *\\(", maindata[i])))
		# Since I'm removing comments I think it's safe to assume anything with = is fixeddata?, anything with .Dim is fixeddata, anything with list( is fixeddata, anything with c( is fixeddata, anything with [ is modeldata, anything with for( is modeldata.  Check all these and if we get a disparity then a warning?
    # Manual overrides are done above		
		
		if(!data.type && modeldata){
			warning('Ignoring an invalid #modeldata# specification in the initial values', call.=FALSE)
			modeldata <- FALSE
		}
		
		# If we don't have an override:
		if(!any(bugsdata, jagsdata, modeldata)){
			if((likelyfixed && likelymodel) || (!likelyfixed && !likelymodel)){
				if(runjags.getOption('debug'))
					swcat(feedtxt, ' type was not recognised - assuming fixed type ', if(datalistfound[i]) '(BUGS format)' else '(R format)', '\n', sep='')

				#print("try parsing here?  Have an option to default data type if unkown?")
				#for(i in 1:length(maindata)){
				#	err <- try(eval(parse(text=maindata[i])))
				#}

				# For now assume that default is jags type data:
				modeldata <- FALSE
				bugsdata <- datalistfound[i]
				jagsdata <- !datalistfound[i]
			}else{
				modeldata <- likelymodel
				bugsdata <- !likelymodel && datalistfound[i]
				jagsdata <- !likelymodel && !datalistfound[i]
			}
		}
		
		if(modeldata){
			if(modelret!='')
				stop('Multiple model style data blocks were found in the model - this is not allowed', call.=FALSE)
			# Paste this data block onto the model and remove it from the data:
			modelret <- paste('data{\n', maindata[i], '\n}\n\n', sep='')
			maindata[i] <- ''
		}else{
			
			if(datalistfound[i]){
				# We need to transpose the data format, but to do that we first need to get it into a list:
				# If the data is like (var=val, ...) then we can just assign the value from eval(parse())
				# If the data is like (var <- val, ...) then the name of var is lost but var=val will be visible in the environment
				# So we need to use the environment to provide names for the variables with missing names				
				
				if(!grepl('list(', maindata[i], fixed=TRUE)){   # Need to make sure there is a list otherwise the templist won't be a list
					maindata[i] <- paste('list(\n', maindata[i], '\n)\n', sep='')
				}
				maindata[i] <- gsub(';',',',maindata[i],fixed=TRUE)   # semicolons will break the list	
				s <- try({
					tempenv <- new.env()
					templist <- eval(parse(text=maindata[i]), envir=tempenv)
					if(is.null(names(templist))){
						# If there are no names in g, all of the variables were assigned using <- so must be in tempenv:
						stopifnot(length(templist)==length(as.list(tempenv)))
						templist <- as.list(tempenv, all.names=TRUE)
					}
					# If some names are missing, only these will be in tempenv - so we could find the matches but it is better to just stop
					if(any(names(templist)=='')){
						stop('Some variable names were missing after attempting to parse the BUGS format list', call.=FALSE)
					}
				}, silent=TRUE)
				if(class(s)=='try-error')
					stop(paste('The following error was obtained while attempting to parse the list of ', if(data.type) 'data' else 'inits',  ':\n', as.character(s), sep=''), call.=FALSE)				
			}else{
				# We will use parse directly to get each of these and then turn them back into chars:
				s <- try({
					tempenv <- new.env()
					eval(parse(text=maindata[i]), envir=tempenv)
					templist <- as.list(tempenv, all.names=TRUE)
				}, silent=TRUE)
				if(class(s)=='try-error')
					stop(paste('The following error was obtained while attempting to parse the ', if(data.type) 'data' else 'inits', ':\n', as.character(s), sep=''), call.=FALSE)
				
			}
			
			# bugsdata is not necessarily the same as datalistfound[i] - it can be overridden
			if(bugsdata){
				#if(runjags.getOption('debug'))
				if(!transwarngiven)
					swcat('Note:  Transposing BUGS ', if(data.type) 'data' else 'inits', ' into R format\n')
				transwarngiven <- TRUE
				
				s <- try({				
	            	temp <- lapply(templist, function(x){
					if(is.null(dim(x)))
						return(x)

					dim(x) <- rev(dim(x))
					x <- aperm(x)
					return(x)
					})
				})
				if(class(s)=='try-error')
					stop(paste('The following unexpected error occured while converting the BUGS format ', if(data.type) 'data' else 'inits', ' into R format:\n', as.character(s), sep=''), call.=FALSE)

				templist <- temp
			}
			# If it is JAGS format data we can leave it alone		
			
			s <- try(maindata[i] <- dump.format(templist, checkvalid=FALSE), silent=TRUE)
			if(class(s)=='try-error')
				stop(paste('The following unexpected error occured while converting the ', if(data.type) 'data' else 'inits', ' back into dump format:\n', as.character(s), sep=''), call.=FALSE)
		}
		
	}
	
	
	# Get rid of any data that is empty or just whitespace:
	maindata <- maindata[maindata!='' & !grepl('^[[:space:]]*$', maindata)]
	if(length(maindata)==0)
		maindata <- ''
	
	if(length(maindata) > 1 && data.type){
		if(runjags.getOption('blockcombine.warning')) warning("More than 1 data block was found in the file.  Blocks were combined.", call.=FALSE)
		maindata <- paste(maindata, collapse="")
	}
	
	return(list(fixed=maindata, model=modelret))
}

#### Keeping in case of problems
oldsortjagsvsbugs <- function(maindata, bugs.format=bugs.format, data.type=TRUE){
	
	# This is all worded for data, but it applies equally to inits as well
	feedtxt <- if(data.type) 'A data' else 'An inits'
	
	# Note that the list( ) will already have been removed - datalistfound tells us if it was there:	
	datalistfound <- maindata[[2]]
	maindata <- maindata[[1]]
	
	modelret <- ''
	for(i in 1:length(maindata)){
		
		if(maindata[i]=='')
			next
		
		# Manual override mechanism for data types
    	tofinds <- tolower(maindata[i])
		bugsdata <- grepl("#bugsdata#", tofinds)   	# =fixed
		jagsdata <- grepl("#Rdata#", tofinds)  	# =fixed
		modeldata <- grepl("#modeldata#", tofinds)
		
    
		tdata <- maindata[i]
		
		browser()
		
		###########   DO SOME CONVERSIONS
		
		# Removes any  character between '#' and a new line
		# For regexp matching - '.' means any character, * means match the preceeding character any number of times

		# This would be greedy:
		#tdata <- gsub("#.*\n", "\n", maindata[i])
		# This is non-greedy matching:
		tdata <- gsub("#(.*?)\n", "\n", tdata, perl=TRUE)

		# This code will remove any "\n" from between "(" and ")" so that we can spot inappropriate commas later:
		# [^\\)] means any character except ) - it's double escaped for some reason
		greps <- gregexpr("\\([^\\)]*\n[^\\)]*\\)", tdata)
		s <- greps[[1]]
		e <- (s-1)+ attr(greps[[1]], "match.length")
		if(s[1]!=-1){ # If it doesn't find anything, s=-1
	
			finalstring <- strsplit(tdata, "", fixed=TRUE)[[1]]
			for(j in 1:length(s)){
	
				tstring <- finalstring[s[j]:e[j]]
				newstring <- gsub("\n", tstring)
				finalstring[s[j]:e[j]] <- newstring
	
			}
			tdata <- paste(finalstring, collapse="")
		}

		# Replaces any '),' with ')\n' (allowing for spaces in between) so that multiple vars on 1 line are allowed:
		tdata <- gsub(") *?, ", ")\n", tdata)

		
#
#		# Was doing helpful <- to = conversions but ditch them (you can't have .Dim <- structure or variable = value but that's up to the user)
#		temp <- strsplit(tdata[i], "")[[1]]
#		numbers <- which(temp=="=")
#		if(length(numbers)>0){
#			for(k in 1:length(numbers)){
#				tstring <- character(length=10)
#				for(j in 1:10){
#					tstring[j] <- paste(temp[pmax((numbers[k]-3-j):(numbers[k]-j), 1)], collapse="")
#				}	
#				if(all(tstring!=".Dim")) temp[numbers[k]] <- "<-"
#			}
#		}
#		tdata[i] <- paste(temp, collapse="")
#
		
		# Remove extra spaces from previously indented things:
		tdata <- gsub(",[[:space:]]*", ", ", tdata)
		
		# This code will change any "," to "\n" from between "<-" and "<-" unless a "(" or ")" are present:
		# [^\\)^\\(] means any character except ) or (
		# *? and perl makes it non-greedy matching
		# Need a while loop as the same character can't be used as the end point of one search and the start of another, so variable=value, variabile=value, variable=value would only remove the first (although variable=value, variable=value\nvariable=value, variable=value would be fine)
		s <- 0
		while(s!=-1){
			greps <- gregexpr("<-([^\\)^\\(]*?),([^\\)^\\(]*?)<-", tdata, perl=TRUE)
			s <- greps[[1]]
			e <- (s-1)+ attr(greps[[1]], "match.length")
			if(s[1]!=-1){ # If it doesn't find anything, s=-1
				finalstring <- strsplit(tdata, "", fixed=TRUE)[[1]]
				for(j in 1:length(s)){
	
					tstring <- finalstring[s[j]:e[j]]
					newstring <- gsub(",", "\n", tstring)
					finalstring[s[j]:e[j]] <- newstring
	
				}
				tdata <- paste(finalstring, collapse="")
			}
		}
		### Then make sure any new leading white space is removed (gsub is vectorised):
		tdata <- gsub("\n[[:space:]]*", "\n", tdata)
		####

		# As a final step, replace the removed , .Dim from data:
#		tdata <- gsub('\n.Dim', ',\n.Dim', tdata, fixed=TRUE)
		# And remove leading and trailing white space:
		tdata <- gsub('^ *\n', '', tdata)
		tdata <- gsub('\n *\n$', '\n', tdata)
		
		maindata[i] <- tdata
		###########   END OF CONVERSIONS
		
		# Fixed means it is to be read as data (jags or bugs column/row major order), model means part of the model:
		# models don't contain .Dim, c(, ' or " (I think)
		likelyfixed <- any(c(grepl(".Dim", maindata[i]), datalistfound[i], grepl("c\\(", maindata[i]), grepl("'", maindata[i]), grepl('"', maindata[i])))
		# fixed data doesn't contain [ or for(
		likelymodel <- data.type && any(c(grepl("\\[", maindata[i]), grepl("for *\\(", maindata[i])))
		# Since I'm removing comments I think it's safe to assume anything with = is fixeddata?, anything with .Dim is fixeddata, anything with list( is fixeddata, anything with c( is fixeddata, anything with [ is modeldata, anything with for( is modeldata.  Check all these and if we get a disparity then a warning?
    # Manual overrides are done above		

		if(modeldata && !data.type){
			warning('Ignoring an invalid #modeldata# specification in the initial values - assuming #Rdata#', call.=FALSE)
			modeldata <- FALSE
			bugsdata <- FALSE
			jagsdata <- TRUE
		}
		
		# If we don't have an override:
		if(!any(bugsdata, jagsdata, modeldata)){
			if((likelyfixed && likelymodel) || (!likelyfixed && !likelymodel)){
				if(runjags.getOption('debug'))
					swcat(feedtxt, ' type was not recognised - assuming fixed type\n', sep='')

				#print("try parsing here?  Have an option to default data type if unkown?")
				#for(i in 1:length(maindata)){
				#	err <- try(eval(parse(text=maindata[i])))
				#}

				# For now assume that default is jags type data:
				modeldata <- FALSE
				bugsdata <- bugs.format
				jagsdata <- !bugs.format
			}else{
				modeldata <- likelymodel
				bugsdata <- !likelymodel && bugs.format
				jagsdata <- !likelymodel && !bugs.format
			}
		}

		if(datalistfound[i] && !bugsdata)
			warning(paste(feedtxt, ' block that looks like a WinBUGS format was found - did you mean to specify bugs.format=TRUE?', sep=''), call.=FALSE)

		if(modeldata){
			if(modelret!='')
				stop('Multiple model style data blocks were found in the model - see the help file for ?read.jagsfile for options for specifying data', call.=FALSE)
			# Paste this data block onto the model and remove it from the data:
			modelret <- paste('data{\n', maindata[i], '\n}\n\n', sep='')
			maindata[i] <- ''
		}else{
			if(bugsdata){
				if(runjags.getOption('debug'))
					swcat('Transposing BUGS data\n')
				
				# We need to transpose the data format:
				s <- try({
				temp <- list.format(maindata[i], checkvalid=FALSE)
	            temp <- lapply(temp, function(x){
					if(is.null(dim(x)))
						return(x)

					dim(x) <- rev(dim(x))
					x <- aperm(x)
					return(x)
					})
				maindata[i] <- dump.format(temp, checkvalid=FALSE)
				})
				if(class(s)=='try-error')
					stop('An unexpected error occured while converting the BUGS format data into JAGS format', call.=FALSE)					
			}
			# If it is JAGS format data we can leave it alone				
		}
		
	}
	
	
	# Get rid of any data that is empty or just whitespace:
	maindata <- maindata[maindata!='' & !grepl('^[[:space:]]*$', maindata)]
	if(length(maindata)==0)
		maindata <- ''
	
	if(length(maindata) > 1 && data.type){
		if(runjags.getOption('blockcombine.warning')) warning("More than 1 data block was found in the file.  Blocks were combined.", call.=FALSE)
		maindata <- paste(maindata, collapse="")
	}
	
	return(list(fixed=maindata, model=modelret))
}


winbugs.extract.small <- function(find, string){
  
  if(identical(string, '\n') || identical(string, '\r') || identical(string, ''))
    return('')
  
  # Eliminate \t and add the # - allow 0+ spaces:
  string <- gsub('\t',' ',string)
  grfind <- paste('# *',find,' *#',sep='')
	# Separate into lines:
  split <- strsplit(string,split='[\n\r]')[[1]]
  # Eliminate lines without the target:
  split <- split[grepl(grfind,split,ignore.case=TRUE)]
  
  # If none, return:
  if(length(split)==0)
    return('')
  
  # Now remove anything before the target identifier:
  split <- gsub(paste('^.*',grfind,sep=''),'',split,ignore.case=TRUE)
  
  # Now just separate on the allowed characters and delist:
  found <- unlist(strsplit(split, '[\\,,;,&]'))
  
  # Remove trailing and leading spaces:
  found <- gsub('^ *', '', found)
  found <- gsub(' *$', '', found)
  
  # Stop if an illegal space is found unless this is modules or factories:
  if(any(grepl(' ', found)) && !find%in%c('modules','factories')){
    problem <- which(grepl(' ', found))
    stop(paste('Invalid space in the following specification(s) for #', find, '#: ', paste(found[problem], collapse=', ')), call.=FALSE)
  }
  
  # And return:
  return(found)
  
}


# Keep this in case it's needed - old and slow (but more general?) version
old.winbugs.extract.small <- function(find, string){

split <- strsplit(string, "")[[1]]

newstring <- ""

newlinelast = found <- FALSE
find.no = hash <- 0

for(i in 1:length(split)){
		
	if(found){
		
		if(split[i]=="#"){
			hash <- hash + 1
			next
		}

		if(hash!=2) next
		
		if(any(split[i]==c(",", ";", ":", "&"))){
			
			find.no <- find.no + 1
			next
		}
		
		if(is.na(newstring[find.no])) newstring[find.no] <- ""
		
		if(any(split[i]==c("", " ", "\t", "@", "%"))) next
		if(any(split[i]==c("\n", "\r"))){
			found <- FALSE
		}else{
			newstring[find.no] <- paste(newstring[find.no], split[i], sep="")
		}
				
	}

	if(!all(split[i]==c("", " ", "\t"))){
		temp <- split[i:length(split)]
		temp <- temp[temp!=" " & temp!= "" & temp!="\t"]
		if(paste(temp[1:(length(strsplit(find, "")[[1]]))], collapse="") == find){ # newlinelast not necessary for extract.small
			found <- TRUE
			hash <- 1
			split[min(which(split=="#")[which(split=="#")>=i])] <- ""
			find.no <- find.no + 1
		}
	
	
		if(any(split[i]==c("\n", "\r"))){
			newlinelast <- TRUE
		}else{
			newlinelast <- FALSE
			}
	}

}
for(i in 1:length(newstring)){
	temp <- strsplit(newstring[i], "")[[1]]
	temp[temp=="="] <- "<-"
	newstring[i] <- paste(temp, collapse="")
}
return(newstring[newstring!="" & newstring!=" "])
}


normalise.mcmcfun <- function(mcmc.list, normalise = TRUE, warn = TRUE, remove.nonstochastic = TRUE){

	if(class(mcmc.list)=="mcmc") mcmc <- mcmc.list(mcmc.list) else mcmc <- mcmc.list

	if(class(mcmc)!="mcmc.list") stop("Object to be normalised must be an mcmc list or mcmc object")
		
	vnames <- lapply(mcmc, dimnames)
	
	# Special case of 0 iterations:
	if(niter(mcmc)==0){
		truestochastic <- rep(TRUE, nvar(mcmc))
		names(truestochastic) <- vnames[[1]][[2]]
		nonstochastic=semistochastic <- !truestochastic
		
	}else{
	
		# ALWAYS cehck stochastic, but sometimes don't remove them
		variances <- sapply(mcmc, function(x){			
			return(apply(x,2,function(y){
			
				usey <- y[y!=Inf & y!=-Inf & !is.na(y)]
				if(length(usey)<=1) v <- 0 else v <- var(usey)
				return(v)
			
			}))}, simplify='array')
	
		dim(variances) <- c(nvar(mcmc),nchain(mcmc))
		dimnames(variances) <- list(vnames[[1]][[2]], NULL)
		anyvariancezero <- variances==0
	
		means <- sapply(mcmc, function(x){			
			return(apply(x,2,function(y){
			
				usey <- y[y!=Inf & y!=-Inf & !is.na(y)]
				if(length(usey)==0) v <- NA else v <- mean(usey)
				return(v)
			
			}))}, simplify='array')
	
		dim(means) <- c(nvar(mcmc),nchain(mcmc))
		dimnames(means) <- list(vnames[[1]][[2]], NULL)
	
		# If there is only one chain then meansdiffer is just taken from variance within the chain i.e. ignored:
		meansdiffer <- apply(means,1,var)>0
		if(nchain(mcmc)==1)
			meansdiffer[] <- !apply(anyvariancezero,1,any)
	
		# There are 3 options:
		# variances > 0 between chains but == 0 within chains ->  semi-stochastic
		# variance == 0 in some but not all chains -> semi-stochastic
		# variances == 0 in all chains but means differ between -> semi-stochastic
		# variances == 0 in all chains and means identical -> non-stochastic
		# all variances > 0 -> stochastic
		# 1 iteration variance will be 0 so counted as semi-stochastic
	
		# 1 iteration and chain -> everything non-stochastic
		if(nchain(mcmc)==1 && niter(mcmc)==1){
			anyvariancezero[] <- TRUE
			meansdiffer[] <- FALSE
		}
	
		nonstochastic <- apply(anyvariancezero,1,all) & !meansdiffer
		removed <- names(nonstochastic)[nonstochastic]
		if(length(removed)>0 && niter(mcmc)>1){
			warnmessage <- paste("Note: The monitored variable", if(sum(nonstochastic)>1) "s", " '", if(sum(nonstochastic)>1) paste(removed[1:(length(removed)-1)], collapse="', '"), if(sum(nonstochastic)>1) "' and '", removed[length(removed)], "' appear", if(sum(nonstochastic)==1) "s", " to be non-stochastic; ", if(sum(nonstochastic)>1) "they" else "it", " will not be included in the convergence diagnostic", sep="")	
			if(warn==TRUE)
				swcat(warnmessage,"\n")
			if(warn=="warning")
				warning(warnmessage, call.=FALSE)
		}
	
		truestochastic <- apply(!anyvariancezero,1,all)  # NOT necessary for the means to be different - they might be the same by chance
		
		#incomplete?:
		#semistochastic <- (apply(anyvariancezero,1,any) != apply(anyvariancezero,1,all)) | (apply(anyvariancezero,1,all) & meansdiffer)
		semistochastic <- !(nonstochastic | truestochastic)
		
		removed <- sort(names(semistochastic)[semistochastic])
		if(length(removed)>0 && niter(mcmc)>1){
			warnmessage <- paste("Note: The monitored variable", if(sum(semistochastic)>1) "s", " '", if(sum(semistochastic)>1) paste(removed[1:(length(removed)-1)], collapse="', '"), if(sum(semistochastic)>1) "' and '", removed[length(removed)], "' appear", if(sum(semistochastic)==1) "s", " to be stochastic in one chain but non-stochastic in another chain; ", if(sum(semistochastic)>1) "they" else "it", " will not be included in the convergence diagnostic", sep="")	
			if(warn==TRUE)
				swcat(warnmessage,"\n")
			if(warn=="warning")
				warning(warnmessage, call.=FALSE)
		}
	
		if(sum(semistochastic)+sum(truestochastic)+sum(nonstochastic) != length(vnames[[1]][[2]])){
			swcat('True stochastic:\n')
			print(truestochastic)
			swcat('Semi stochastic:\n')
			print(semistochastic)
			swcat('Non stochastic:\n')
			print(nonstochastic)
			swcat('Variable names:\n')
			print(vnames[[1]][[2]])
			if(runjags.getOption('debug'))
				browser()
			
			stop('An unexpected error occured determining which variables were and were not stochasic - please file an error report to the package maintainer', call.=FALSE)			
		}
	}
	
#		if(all(nontruestochastic))
#			stop("All monitored variables appear to be non-stochastic; try adding monitored variables either using #monitor# in the model code or by specifying a monitor argument to run.jags", call.=FALSE)

	# remove.nonstochastic is STRICT nonstochastic only (i.e. semistochastic and stochastic both left alone)
	if(remove.nonstochastic && any(nonstochastic))
		mcmc <- mcmc[,-which(nonstochastic),drop=FALSE]
		
	
	if(normalise && !all(nonstochastic)){
		if(niter(mcmc)>1000) use <- sample(1:niter(mcmc), size=1000, replace=FALSE) else use <- 1:niter(mcmc)

		shap.res <- apply(combine.mcmc(mcmc, collapse.chains=TRUE, add.mutate=FALSE), 2, function(x){
			rv <- 1
			if(!any(x==Inf | x==-Inf | is.na(x)) && var(x)!=0){
				# We might get an error if we sample 1000 funny values - in which case leave them alone (likely to be small variance so transform probably unnecessary anyway)
				suppressWarnings(success <- try({
					if(all(x > 0)){
						if(all(x < 1)){
							if(stats::shapiro.test(x[use])$p.value < stats::shapiro.test(log(x[use]/(1-x[use])))$p.value) rv <- 3
						}else{
							if(stats::shapiro.test(x[use])$p.value < stats::shapiro.test(log(x[use]))$p.value) rv <- 2
						}
					}
				}, silent=TRUE))
			}
			return(rv)
		})

		change <- which(shap.res!=1)
		for(parameter in change){
			for(chain in 1:nchain(mcmc)){
				newvalues <- unlist(mcmc[[chain]][,parameter])
				if(shap.res[parameter]==3) newvalues <- log(newvalues/(1-newvalues)) else newvalues <- log(newvalues)
				mcmc[[chain]][,parameter] <- newvalues
			}
		}
	}
	for(i in 1:length(mcmc))
		dimnames(mcmc[[i]])[[1]] <- vnames[[i]][[1]]

	if(class(mcmc.list)=="mcmc"){
		return(list(mcmc=mcmc[[1]], truestochastic=truestochastic, semistochastic=semistochastic, nonstochastic=nonstochastic))
	}else{
		return(list(mcmc=mcmc, truestochastic=truestochastic, semistochastic=semistochastic, nonstochastic=nonstochastic))
	}
}

safe.autocorr.diag <- function(x, ...){
	if(niter(x)==1){
		y <- matrix(NA, nrow=1, ncol=nvar(x), dimnames=list('Lag 1', dimnames(x[[1]])[[2]]))
		return(y)
	}
		
	y <- autocorr.diag(x[,1],...)
	if(nvar(x)>1) for(i in 2:nvar(x)) y <- cbind(y, autocorr.diag(x[,i],...))
	dimnames(y)[[2]] <- dimnames(x[[1]])[[2]]
	return(y)
}

safe.gelman.diag <- function(x, warn=TRUE,...){
	
	success <- try(gelman <- gelman.diag(x, ...), silent=TRUE)
	if(inherits(success, 'try-error')){
		
		nvars <- nvar(x)
		psrfs <- matrix(ncol=2, nrow=nvars, dimnames=list(varnames(x), c("Point est.", "97.5% quantile")))
		
		success <- try({
			for(i in 1:nvars){
			psrfs[i,] <- gelman.diag(x[,i,drop=FALSE], ...)$psrf
		}
		}, silent=TRUE)
		if(inherits(success, 'try-error')){
			#name <- new_unique("gelman.failed", ".Rsave")
			#save(failedmcmc, file=name)
			stop("An error occured while calculating the Gelman-Rubin statistic")
		}
		
		if(warn) swcat("Note:  Unable to calculate the multivariate psrf\n")
		
		y <- list(psrf=psrfs, mpsrf="Unable to calculate multivariate psrf")
		
		class(y) <- "gelman.diag"
		return(y)
	}else{
		return(gelman)
	}
	
}

getargs <- function(functions, passed, returnall=TRUE, otherfnames=character(0)){
	
	N <- length(functions)
	args <- vector('list', length=N)
	names <- vector('list', length=N)
		
	# Argument names for the functions relative to this function:
	for(i in 1:N){
		args[[i]] <- as.list(formals(get(functions[i], sys.frame(sys.parent(n=1)))))
		args[[i]] <- args[[i]][names(args[[i]])!="..."]
		names[[i]] <- names(args[[i]])
	}
	
 	argnames <- unique(unlist(names))
 	argmatch <- pmatch(names(passed), argnames)

 	if(any(is.na(argmatch))){
     
     nomatches <- names(passed)[which(is.na(argmatch))]
 		
		functions <- c(otherfnames, functions)
 		functstring <- paste(if(length(functions)>1) paste(functions[1:(length(functions)-1)], collapse="', '"), if(length(functions)>1)"' or '", functions[length(functions)], "' function", if(length(functions)>1) "s", sep="")
 		
 		argstring <- paste(if(length(nomatches)>1) "s", " '", if(length(nomatches)>1) paste(nomatches[1:(length(nomatches)-1)], collapse="', '"), if(length(nomatches)>1)"' or '", nomatches[length(nomatches)], "'", sep="")

		stop(paste("unused argument(s)", argstring, " (no unambiguous match in the '",functstring,")", sep=""), call.=FALSE)
 	}
 	
	names(passed) <- argnames[argmatch]
	passed <- passed[!is.na(argmatch)]
 	
	if(returnall){
		# Now get defaults from specified functions, giving priority to earlier functions if arguments appear in more than 1:
		alreadymatched <- names(passed)
		for(i in 1:N){
			newget <- names[[i]][!(names[[i]] %in% alreadymatched)]
			newargs <- lapply(newget, function(x) try(as.expression(get(x, pos=args[[i]])), silent=TRUE))
			names(newargs) <- newget
			newargs <- newargs[!sapply(newargs,class)=="try-error"]
			passed <- c(passed, newargs)
			alreadymatched <- c(alreadymatched, newget)
		}
	}
	return(passed)
}

tailf <- function(file, start=1, refresh=0.1, min.static=1, max.static=Inf, stop.function=function() return(FALSE), stop.text=character(0), print=TRUE, return=!print){

	done <- FALSE

	readupto <- 1

	# Allow the simulation to start:
	if(!file.exists(file)) Sys.sleep(1)

	tryCatch({
	if(!file.exists(file)) stop("The named file does not exist")

	linesto <- start+1
	going <- TRUE
	static <- 0
	lastline <- ""

	text <- ""

	# Wait for file to start being written to:
	repeat{
		suppressWarnings(output <- readLines(file))
		# Catch occasional error with na being introduced:
		if(is.na(lastline)) lastline <- ""
		if(length(output)==0){
			static <- static+1
			if(static > max.static) break
		}else{
			break
		}
	}

	repeat{
	Sys.sleep(refresh)

	suppressWarnings(output <- readLines(file))
	# Catch occasional error with na being introduced:
	if(is.na(lastline)) lastline <- ""

	if(length(output)==(linesto-1)){
		if(output[length(output)]==lastline){
			static <- static+1
			if(static > max.static) break
		}else{
			static <- 0
			newsameline <- output[readupto]		
			new <- strsplit(newsameline, '')[[1]]
			last <- length(strsplit(lastline, '')[[1]])
			new <- paste(new[(last+1):length(new)], collapse='')
			if(print) cat(new)
			text <- paste(text, new, sep='')
			lastline <- output[length(output)]
		}
	}else{
		if(output[linesto-1]!=lastline){
			new <- output[linesto-1]
			new <- strsplit(new, '')[[1]]
			last <- length(strsplit(lastline, '')[[1]])
			new <- paste(new[(last+1):length(new)], collapse='')
			if(print) cat(new)
			text <- paste(text, new, sep='')
		}
		static <- 0
		new <- paste(output[linesto:length(output)], collapse='\n')
		if(print) cat('\n', new, sep='')
		text <- paste(text, '\n', new, sep='')
		linesto <- length(output)+1
		lastline <- output[length(output)]
	}

	flush.console()
	if(static > min.static & stop.function()) break
	
	allnewoutput <- paste(output[readupto:length(output)],collapse="\n")
	readupto <- length(output)
	foundtextmatch <- any(sapply(as.list(stop.text), grepl, x=allnewoutput))
	if(foundtextmatch) break

	}

	done <- TRUE

	}, finally={
	
		retval <- list(text=text, lines=length(output), interrupt=!done)
		if(return) return(retval)
	
		})
}

prettifytable <- function(x, digits=5, colsequal=FALSE, nastring="", psrfcoldollar=FALSE){

	formatted <- formatC(x, format="fg", digits=digits, width=-1)
	absx <- abs(x)
	absx[is.na(absx)] <- 1
	formatted[absx<10^-digits] <- formatC(x[absx<10^-digits], digits=digits, width=-1)
	
	formatted <- gsub("NA",nastring,formatted)
	formatted <- gsub("NaN",nastring,formatted)
	if(any(psrfcoldollar)){
		stopifnot(length(psrfcoldollar)==nrow(formatted))		
#		formatted[psrfcoldollar,'psrf'] <- '$'
		formatted[psrfcoldollar,'psrf'] <- paste(formatted[psrfcoldollar,'psrf'], ' $', sep='')
	}
	
	# Put column names on as well:
	formatted <- rbind(dimnames(formatted)[[2]],formatted)
	dimnames(formatted) <- list(dimnames(formatted)[[1]],rep("",ncol(formatted)))
	
	if(colsequal){
		retval <- format(formatted, justify="right")
	}else{
		retval <- apply(formatted, 2, format, justify="right")
	}	
	
	# noquote suppresses the "" from the character string:
	return(noquote(retval))
	
}

checkmodfact <- function(tocheck, type){
	if(identical(tocheck,'')) tocheck <- list()
	stopifnot(type%in%c('module','factory'))
  	
  	# In case any are blank:
  	if(length(tocheck)>0)
		tocheck <- tocheck[sapply(tocheck, function(x) return(!all(x=='')))]
  	  
	nl <- switch(type, module=2, factory=3)

  	if(is.character(tocheck)){
		if(length(tocheck)>0 && any(grepl(',', tocheck, fixed=TRUE)))
			stop('Use of commas in module or factory specifications is not allowed - separate name type and status with a space', call.=FALSE)
		tocheck <- gsub('[\\(\\)]',' ',tocheck)
	  	tocheck <- strsplit(gsub('[[:space:]]+', ' ', tocheck),' ')
  	}
  	
	if(!is.list(tocheck)){
		stop(paste('Invalid ', type, ' specification - it must be either a character vector or a list', sep=''), call.=FALSE)
	}
	
	# If a blank list return '':
	if(identical(list(), tocheck)) return('')
	
  	validated <- lapply(tocheck, function(x){
		origx <- x

	  	# If not specified, assume it wants to be on:
	  	if(length(x) < nl)
			x <- c(x, "TRUE")
		
		# Check length is correct:
		if(length(x)!=nl)
			stop(paste('Incorrect number of elements for ', type, ' specification "', paste(origx,collapse=' '), '": ', length(origx), ' found but ', nl, ' expected', sep=''), call.=FALSE)
		
	  	# Replace on with TRUE and off with FALSE:
	  	x[nl] <- gsub('on','TRUE',x[nl])
	  	x[nl] <- gsub('ON','TRUE',x[nl])
	  	x[nl] <- gsub('off','FALSE',x[nl])
	  	x[nl] <- gsub('OFF','FALSE',x[nl])
	  	x[nl] <- as.character(as.logical(x[nl]))
		
		# Check factory type is valid:
	  	if(type=='factory' && !x[2]%in%c('sampler','monitor','rng'))
			stop(paste('The type of ', type, ' specification "', paste(origx,collapse=' '), '" must be one of sampler, monitor or rng', sep=''), call.=FALSE)
	
	  	# Check they are all logicable:
	  	if(is.na(x[nl]))
	  		stop(paste('The status "', origx[nl], '" of ', type, ' specification "', paste(origx, collapse=' '), '" is not interpretable as logical', sep=''), call.=FALSE)
	
		return(x)
	})

  validated <- unique(validated)
  if(type=='module')
    tocheck <- sapply(validated,function(x) return(x[1]))
  if(type=='factory')
    tocheck <- sapply(validated,function(x) return(paste(x[1],x[2],sep=' ')))
	if(length(unique(tocheck))!=length(tocheck))
		stop(paste('Replicated ', type, ' name(s) with conflicting status in "', paste(sapply(validated,paste,collapse=' '), collapse=', '), '"', sep=''), call.=FALSE)	
	if(identical(list(), validated)) validated <- ''
	
	return(validated)
}

checkvalidrunjagsobject <- function(runjags.object){

	if(!is.runjags(runjags.object))
		stop("The output of a runjags function must be supplied", call.=FALSE)
	
	# Add some info that may not be available from old (saved) runjags objects:

	# Only add things if not debugging:
	if(!runjags.getOption('debug')){
		
	  # Added for version 2.0:
		if(is.null(runjags.object$summary.pars))
			runjags.object$summary.pars <- getdefaultsummarypars()
		if(is.null(runjags.object$summary.available))
			runjags.object$summary.available <- FALSE
		if(is.null(runjags.object$deviance.table))
			runjags.object$deviance.table <- NA
		if(is.null(runjags.object$deviance.sum))
			runjags.object$deviance.sum <- NA
		if(is.null(runjags.object$pd))
			runjags.object$pd <- NA
		if(is.null(runjags.object$samplers))
			runjags.object$samplers <- NA
		if(is.null(runjags.object$semistochastic))
			runjags.object$semistochastic <- rep(nvar(runjags.object$mcmc), FALSE)
		if(is.null(runjags.object$truestochastic))
			runjags.object$truestochastic <- runjags.object$semistochastic
	  	runjags.object$nonstochastic=!(runjags.object$truestochastic | runjags.object$semistochastic)
	  	if(is.list(runjags.object$dic) && is.null(runjags.object$dic$meanpopt))
			runjags.object$dic$meanpopt <- NA
		if(is.null(runjags.object$response))
			runjags.object$response <- NA
		if(is.null(runjags.object$residual))
			runjags.object$residual <- NA
		if(is.null(runjags.object$fitted))
			runjags.object$fitted <- NA
	
		# Not the best way of doing it - add.summary doesn't update the version:
	#	if(numeric_version(runjags.object$runjags.version[1]) < 2){
	#		runjags.object$summaries <- 'Summary statistics not available - see ?add.summary'
	#	}
		
		defmo <- getdefaultmethodoptions()
		if(is.null(runjags.object$method.options)){
			runjags.object$method.options <- defmo
		}

		if(is.null(runjags.object$method.options$n.sims))
			runjags.object$method.options$n.sims <- defmo$n.sims
		if(is.null(runjags.object$method.options$cl))
			runjags.object$method.options$cl <- defmo$cl
		if(is.null(runjags.object$method.options$remote.jags))
			runjags.object$method.options$remote.jags <- defmo$remote.jags
		if(is.null(runjags.object$method.options$by))
			runjags.object$method.options$by <- defmo$by
	  	if(is.null(runjags.object$method.options$progress.bar))
	    	runjags.object$method.options$progress.bar <- defmo$progress.bar

		if(!all(c('n.sims', 'cl', 'remote.jags', 'by', 'progress.bar', 'jags', 'silent.jags', 'jags.refresh', 'batch.jags')%in%names(runjags.object$method.options))){
			runjags.object$method.options <- defmo
		}

	  
	}else{
		if(!all(c('plots','vars','mutate','psrf.target','normalise.mcmc','modeest.opts','confidence','autocorr.lags','custom','silent.jags','plots','plot.type','col','summary.iters','trace.iters','separate.chains','trace.options','density.options','histogram.options','ecdfplot.options','acplot.options')%in%names(runjags.object$summary.pars) || length(runjags.object$summary.pars)!=21)){
			cat('Invalid summary.pars\n')
			browser()
		}
	}
	
	# Less strict - don't check for the presence of summary statistics as they may not have been added yet:
	if(!all(c('mcmc', 'end.state', 'burnin', 'sample', 'thin', 'model', 'data', 'monitor', 'noread.monitor', 'modules', 'factories', 'response', 'residual', 'fitted', 'method', 'method.options', 'timetaken', 'summary.pars', 'deviance.table', 'deviance.sum') %in% names(runjags.object)))
		stop("Invalid runjags.object provided; the output of a runjags function (with class 'runjags') must be supplied", call.=FALSE)
	
  # rjags is NOT required as it is sometimes removed to decompile it
	if(!all(c('n.sims', 'cl', 'remote.jags', 'by', 'progress.bar', 'jags', 'silent.jags', 'jags.refresh', 'batch.jags')%in%names(runjags.object$method.options)))
		stop("Invalid runjags.object provided (invalid method options); the output of a runjags function (with class 'runjags') must be supplied", call.=FALSE)
	
	
	if(length(runjags.object$response)!=1 || (!is.na(runjags.object$response) && !is.character(runjags.object$response)))
		stop('The response variable (if supplied) must be a single character variable')
	if(length(runjags.object$residual)!=1 || (!is.na(runjags.object$residual) && !is.character(runjags.object$residual)))
		stop('The residual variable (if supplied) must be a single character variable')
	if(length(runjags.object$fitted)!=1 || (!is.na(runjags.object$fitted) && !is.character(runjags.object$fitted)))
		stop('The fitted variable (if supplied) must be a single character variable')
	
	runjags.object$factories <- checkmodfact(runjags.object$factories, 'factory')
	runjags.object$modules <- checkmodfact(runjags.object$modules, 'module')
		
	invisible(runjags.object)
}

checkvalidmonitorname <- function(monitor){
	
	if(is.logical(monitor) && length(monitor)>0 && !identical(NA, monitor)){
		if(any(is.na(monitor))){
			warning('Setting NA values in specified logical argument for variable selection to FALSE', call.=FALSE)
			monitor[is.na(monitor)] <- FALSE
		}
		return(monitor)
	}
	
	monitor <- as.character(monitor)
	
	# Remove spaces from monitors
	monitor <- gsub('[[:space:]]','',monitor)
	
	# Don't allow comments in monitor names
	problem <- grepl('#', monitor)
	if(any(problem))
		stop(paste('Invalid monitor name(s) "', paste(monitor[problem],collapse='", "'), '" - comment (#) characters are not allowed', sep=''), call.=FALSE)
		
	# Look for ,, and [, and ,] and give an error
	problem <- grepl('[,', monitor, fixed=TRUE) | grepl(',]', monitor, fixed=TRUE) | grepl(',,', monitor, fixed=TRUE)
	if(any(problem))
		stop(paste('Invalid monitor name(s) "', paste(monitor[problem],collapse='", "'), '" - empty indexes are not allowed', sep=''), call.=FALSE)

	# Quotation marks are allowed (as are $ and ^), as we use them for matching specific variables:
#	problem <- grepl('"', monitor, fixed=TRUE) | grepl("'", monitor, fixed=TRUE)
#	if(any(problem))
#		stop(paste('Invalid monitor name(s): ', paste(monitor[problem],collapse=', '), ' - quotation marks are not allowed', sep=''), call.=FALSE)
	
	# Look for unmatched brackets:
	problem <- (grepl('[', monitor, fixed=TRUE) & !grepl("]", monitor, fixed=TRUE)) | (!grepl('[', monitor, fixed=TRUE) & grepl("]", monitor, fixed=TRUE))
	if(any(problem))
		stop(paste('Invalid monitor name(s): ', paste(monitor[problem],collapse=', '), ' - unmatched square bracket', sep=''), call.=FALSE)

	# Look for , without [:
	problem <- grepl(',', monitor, fixed=TRUE) & !grepl("[", monitor, fixed=TRUE)
	if(any(problem))
		stop(paste('Invalid monitor name(s): ', paste(monitor[problem],collapse=', '), ' - commas are only allowed within square brackets', sep=''), call.=FALSE)
	
	# The word 'to' is going to give a problem:
	if(!all(is.na(monitor)) && any(monitor=='to'))
		warning('Use of the monitor name "to" may cause problems with some JAGS methods', call.=FALSE)
	
	monitor <- expandindexnames(monitor)
	return(monitor)
}

# Required for modified read.coda function to work properly:
expandindexnames <- function(names){
	f <- function(x){
	  nums <- suppressWarnings(as.numeric(x))
	  keep <- x[is.na(nums)]
	  nums <- nums[!is.na(nums)]
	  npairs <- length(nums)/2
	  if(npairs>0){
	    indexes <- expand.grid(lapply(1:npairs,function(p){
	      s <- ((p-1)*2)+1
	      return(nums[s]:nums[s+1])
	    }))
	    keep <- t(matrix(keep, nrow=length(keep), ncol=nrow(indexes)))
	    for(i in 1:npairs) keep[,(i*2)] <- indexes[,i]
	    keep <- apply(keep,1,paste,collapse='')
	  }
	  return(keep)
	}
	
	# Mark indexes using : using hashes in a predetermined way:
	newnames <- gsub('([[:digit:]]+):([[:digit:]]+)', '##\\1#\\2#', names)			
	# Now split on comment char:
	split <- strsplit(newnames,'#')
	# And recombine with the indexes explicitly rolled out:
	toret <- as.character(unlist(lapply(split,f)))
	# Remove any spaces:
	toret <- gsub(' ','',toret)
	# Check the bracket isn't empty:
	if(any(grepl('[]',toret,fixed=TRUE))){
		stop(paste('Empty indexes provided for variable(s) ', paste(toret[grepl('[]',toret,fixed=TRUE)], collapse=', '), ' - ensure that the full range of indices are specified using a colon (e.g. var[1:2,1])', sep=''), call.=FALSE)
	}
	
	if(length(toret)==0 || identical(toret, '') || identical(toret, NA))
	  return(toret)
	
	if(grepl(',,',toret,fixed=TRUE) || grepl('[,',toret,fixed=TRUE) || grepl(',]',toret,fixed=TRUE))
		stop(paste('Ambiguous index entry provided for variable(s) ', paste(toret[grepl(',,',toret,fixed=TRUE) || grepl('[,',toret,fixed=TRUE) || grepl(',]',toret,fixed=TRUE)], collapse=', '), ' - ensure that all indices are specified using a colon (e.g. var[1:2,1])', sep=''), call.=FALSE)
	
	return(toret)	
}

matchvars <- function(vars, names, exactneeded=NA, testfound=TRUE){
	
	if(length(names)==0)
		stop('No valid variable names in the object supplied', call.=FALSE)
	
	if(is.logical(vars) && length(vars)>0){
		if(length(vars)!=length(names)){
			stop(paste("The length of the logical vector specified to 'vars' (", length(vars), ") does not match the number of monitored variables (", length(names), ")", sep=""), call.=FALSE)
		}
		if(any(is.na(vars)))
			stop("Missing values are not allowed in the logical vector specified to 'vars'", call.=FALSE)
		
		return(which(vars))
	}
	
	vars <- as.character(na.omit(vars))
	# vars is what to find, names is the MCMC object varnames to find it in
	
	if(length(vars)>0){
		
		vars <- expandindexnames(vars)
	#	matched <- vapply(vars, function(m) return(grepl(paste("^",m,sep=""),names)), logical(length(names)))
		
		matched <- vapply(vars, function(m) return(grepl(m,paste("^",names,"$",sep=""),fixed=TRUE)), logical(length(names)))	
		exact <- vapply(vars, function(m) return((gsub("'","",gsub('"','',m,fixed=TRUE),fixed=TRUE)) == names), logical(length(names)))
		namesnoindex <- sapply(strsplit(names,'[',fixed=TRUE), function(x) return(x[[1]]))
		nameswithoutindex <- vapply(vars, function(m) return((gsub("'","",gsub('"','',m,fixed=TRUE),fixed=TRUE)) == namesnoindex), logical(length(names)))
		varsnoindex <- sapply(strsplit(vars,'[',fixed=TRUE), function(x) return(x[[1]]))
		varswithoutindex <- vapply(varsnoindex, function(m) return((gsub("'","",gsub('"','',m,fixed=TRUE),fixed=TRUE)) == names), logical(length(names)))
		
		if(is.null(dim(matched))){
			dn <- names(matched)
			dim(matched) <- c(length(names), length(vars))
			dimnames(matched) <- list(NULL, dn)
		}
		if(is.null(dim(exact))){
			dn <- names(exact)
			dim(exact) <- c(length(names), length(vars))
			dimnames(exact) <- list(NULL, dn)
		}
		if(is.null(dim(nameswithoutindex))){
			dn <- names(nameswithoutindex)
			dim(nameswithoutindex) <- c(length(names), length(vars))
			dimnames(nameswithoutindex) <- list(NULL, dn)
		}
		if(is.null(dim(varswithoutindex))){
			dn <- names(varswithoutindex)
			dim(varswithoutindex) <- c(length(names), length(vars))
			dimnames(varswithoutindex) <- list(NULL, dn)
		}
		
		if(identical(exactneeded, NA))
			exactneeded <- t(matrix((grepl("'",vars,fixed=TRUE) | grepl('"',vars,fixed=TRUE)), ncol=length(names), nrow=length(vars)))
		
		if(!all(dim(matched)==dim(exact)) || !all(dim(matched)==dim(nameswithoutindex)) || !all(dim(matched)==dim(varswithoutindex))){
			print(matched)
			print(exact)
			stop('An unexpected error occured while matching variable names - please file a bug report to the package maintainer including the matrices printed above', call.=FALSE)
		}
		
		# This still needs to be vectorised like this in case exactneeded depends on var:
		combined <- (matched & !exactneeded) | (varswithoutindex & !exactneeded) | exact | nameswithoutindex
		selected <- lapply(1:ncol(combined), function(x){
			return(which(combined[,x]))
		})
		names(selected) <- dimnames(combined)[[2]]
		# selected <- apply( (matched & !exactneeded) | (exact & exactneeded) , 2, function(x) return(if(any(x)) which(x) else 0))

		if(any(sapply(selected,length)==0) && testfound)
			stop(paste("No matches found for the following variable name(s): ", paste(vars[sapply(selected,length)==0],collapse=','), sep=''), call.=FALSE)

		selected <- unique(unlist(selected))
		
	}else{
			
		selected <- 1:length(names)
	}
	
	return(selected)
}

checkvalidforjags <- function(object){
	
	if(length(object)==1 && is.na(object)) return(list(valid=TRUE, probstring=""))
	
	if(class(object)=="runjagsdata" || class(object)=="runjagsinits") class(object) <- "character"
	if(class(object)!="list" && class(object)!="character") return(list(valid=FALSE, probstring="object must be either a named list or a character vector in the R dump format"))
	
	if(!is.list(object)) object <- list.format(object, checkvalid=FALSE)
	
	if(any(names(object) == "")){
		return(list(valid=FALSE, probstring="missing variable name(s)"))
	}

	if(!length(unique(names(object))) == length(object)){
		return(list(valid=FALSE, probstring="duplicated variable name(s)"))
	}
	
	problems <- sapply(object, function(x){
		
		# Catch potential problems with the data being passed through:
		if(length(x)==0){
			return("")
		}
		if(is.null(x)){
			return("NULL")
		}
		if(inherits(x, "data.frame")){
			return("inherits from class 'data.frame' - try converting it to a valid type using as.matrix")
		}
		if(length(x)==0){
			return("length zero")
		}
		if(class(x)=="logical" && !all(is.na(x))){
			return("TRUE/FALSE")
		}
		if(class(x)=="character" && !all(is.na(x))){
			return("character")
		}
		if(class(x)=="factor" && !all(is.na(x))){
			return("factor")
		}
		if(any(x==Inf, na.rm=TRUE)){
			return("Inf")
		}
		if(any(x==-Inf, na.rm=TRUE)){
			return("Inf")
		}
		return("")
	})	
	
	problems[names(problems)==".RNG.name"] <- ""
	
	if(all(problems=="")){
		return(list(valid=TRUE, probstring=""))
	}else{
		problems <- problems[problems!=""]
		probstring <- paste("invalid variable value(s) - ", paste(names(problems), " (", problems, ")", sep=""), collapse=", ", sep="")
		return(list(valid=FALSE, probstring=probstring))	
	}
	
}

getrunjagsmethod <- function(method){

	methodmatch <- pmatch(tolower(method), c('rjags', 'simple', 'interruptible', 'parallel', 'snow', 'rjparallel', 'background', 'bgparallel', 'xgrid'))
	if(is.na(methodmatch)){
		stop(paste("Unsupported or ambiguous method '", method, "'; choose one of 'rjags', 'simple', 'interruptible', 'parallel', 'snow', 'rjparallel', 'background' or 'bgparallel'", sep=""), call.=FALSE)
	}else{
		method <- c('rjags', 'simple', 'interruptible', 'parallel', 'snow', 'rjparallel', 'background', 'bgparallel', 'xgrid')[methodmatch]
	}
	if(.Platform$OS.type=='unix' && (.Platform$GUI!="AQUA" & Sys.info()['user']=='nobody' && !(method %in% c('rjags','simple')))){
		warning("You may be trying to use a runjags method on Xgrid which won't work - choose either rjags or simple methods for using run.jags functions on Xgrid (or see the xgrid.jags functions for an alternative)")
	}
	
	return(method)
}


addmutated <- function(oldmcmc, mutate){
	
	if(!is.null(mutate)){
		
		args <- list()
		if(class(mutate)=='list'){
			if(length(mutate)==1){
				mutate <- mutate[[1]]
			}else{
				args <- mutate[-1]
				mutate <- mutate[[1]]
			}
		}
		
		if(is.character(mutate)){
		  mutate <- try(get(mutate),silent=TRUE)
			if(class(mutate)=='try-error') stop('Unable to find a function matching the character argument given to "mutate"',call.=FALSE)
		}
		
    if(!is.function(mutate))
		stop('The "mutate" argument must be a function (or a character string naming a function, or a list with first element containing the function)')
	if(length(formals(mutate))==0)
		stop('The "mutate" argument must be a function taking at least 1 argument (an mcmc matrix)')
	 
	  if(length(formals(mutate))<(1+length(args)))
      stop('The "mutate" argument must be a function with the first argument an mcmc matrix')
    
		newmcmcs <- as.mcmc.list(lapply(1:nchain(oldmcmc), function(chain){
			x <- oldmcmc[[chain]]
			args <- c(list(x), args)
			new <- do.call('mutate', args)
		
			if(is.list(new)){
				lengths <- sapply(new,length)
				if(!all(lengths == nrow(x)))
					stop('Object produced by the specified "mutate" function was invalid - a named list or matrix with an equal number of rows to that provided must be returned', call.=FALSE)
				names <- names(new)
				if(is.null(names) || any(names==""))
					stop('Object produced by the specified "mutate" function was invalid - a named list or matrix with unique variable names must be returned', call.=FALSE)
			
				new <- as.matrix(as.data.frame(new))
				dimnames(new) <- list(NULL, names)
			}
			if(is.null(dim(new))){
				dim(new) <- c(length(new), 1)
				dimnames(new) <- list(NULL, 'mutated_variable')
			}
			ds <- dim(new)
			if(length(ds)!=2 || ds[1]!=nrow(x))
				stop('Object produced by the specified "mutate" function was invalid - a named list or matrix with an equal number of rows to that provided must be returned', call.=FALSE)
			
			if(is.null(dimnames(new)[[2]]) || any(dimnames(new)[[2]]=="") || length(unique(dimnames(new)[[2]]))!=length(dimnames(new)[[2]]))
				stop('Object produced by the specified "mutate" function was invalid - a named list or matrix with unique variable names must be returned', call.=FALSE)

			if(any(dimnames(new)[[2]] %in% varnames(x))){
				notduplicated <- which(! dimnames(new)[[2]] %in% varnames(x))
				new <- new[,notduplicated,drop=FALSE]				
				if(chain==1) warning('Duplicated variables returned by the "mutate" function were removed',call.=FALSE)
			}
		
			# Copy iteration names:
			dimnames(new) <- list(dimnames(x)[[1]], dimnames(new)[[2]])

			new <- as.mcmc(cbind(x,new))			
			return(new)
		}))
		mcmc <- newmcmcs
	}else{
		mcmc <- oldmcmc
	}
	return(mcmc)
}


getsummaryargs <- function(oldsummaryargs, parentf, ignore=character(0), ...){

  # Need to first get rid of anything in summary.pars (oldsummaryargs) not in add.summary, then check ..., then overwrite with anything in ...
  
	newsummaryargs <- list(...)
	# BANOVA is currently using these so make them a note, then a warning in the next release
	newsummaryargs <- newsummaryargs[! names(newsummaryargs)%in%ignore]
	if('check.stochastic'%in%names(newsummaryargs))
		swcat('** Note:  The argument "check.stochastic" is deprecated and will be ignored **\n')
		#warning('The argument "check.stochastic" is deprecated and will be ignored',call.=FALSE)	
	newsummaryargs$check.stochastic <- NULL
	if('check.conv'%in%names(newsummaryargs))
		swcat('** Note:  The argument "check.conv" is deprecated and will be ignored **\n')
		#warning('The argument "check.stochastic" is deprecated and will be ignored',call.=FALSE)	
	newsummaryargs$check.conv <- NULL

	if('monitor.deviance'%in%names(newsummaryargs))
		warning('The argument "monitor.deviance" is deprecated and will be ignored - add "deviance" to the monitors argument instead',call.=FALSE)	
	newsummaryargs$monitor.deviance <- NULL
	if('monitor.pd'%in%names(newsummaryargs))
		warning('The argument "monitor.pd" is deprecated and will be ignored - add "full.pd" to the monitors argument instead',call.=FALSE)	
	newsummaryargs$monitor.pd <- NULL
	if('monitor.pd.i'%in%names(newsummaryargs))
		warning('The argument "monitor.pd.i" is deprecated and will be ignored - add "pd" to the monitors argument instead',call.=FALSE)	
	newsummaryargs$monitor.pd.i <- NULL
	if('monitor.popt'%in%names(newsummaryargs))
		warning('The argument "monitor.popt" is deprecated and will be ignored - add "popt" to the monitors argument instead',call.=FALSE)	
	newsummaryargs$monitor.popt <- NULL

	# Check the summary arguments:
	newsummaryargs <- getargs('add.summary', newsummaryargs, otherfnames=parentf, returnall=FALSE)
  
	# Start with add.summary defaults:
	summaryargs <- formals(add.summary)
  
	# Overwrite with any values in the old summary arguments
	summaryargs <- summaryargs[!names(summaryargs)%in%c('runjags.object',names(oldsummaryargs))]
	summaryargs <- c(summaryargs, oldsummaryargs)
	
	# Overwrite with any values passed through:
	summaryargs <- summaryargs[!names(summaryargs)%in%c(names(newsummaryargs))]
	summaryargs <- c(summaryargs, newsummaryargs)
		
	return(summaryargs)
}


weightedaverage <- function(dev1, dev2, iter1, iter2){
	
	stopifnot(all(dim(dev1)==dim(dev2)))
	
	# For combining DICs - if either are completely NA, return just NA rather than a matrix of:
	if(all(is.na(dev1)) || all(is.na(dev2)))
		return(NA)
	
	totaliter <- iter1+iter2
	dev1 <- dev1 * (iter1/totaliter)
	dev2 <- dev2 * (iter2/totaliter)
	
	return(dev1 + dev2)
	
}

checkadaptrequired <- function(rjags){
	# Adapt will return TRUE if it is complete, or an error if not (it tries to update 0):
	suppressWarnings(s <- try(adaptcomplete <- rjags::adapt(rjags, n.iter=0), silent=TRUE))
	if(class(s)=='try-error')
		adaptcomplete <- FALSE
	
	return(!adaptcomplete)
}

getstoptexts <- function(adaptfail=runjags.getOption('adapt.incomplete')%in%c('error')){
	
	st <- c('Deleting model','Adaptation incomplete','syntax error')
	
	# JAGS version 3 will stop at Adaptation incomplete with batch.jags TRUE or FALSE
	if(adaptfail || testjags(silent=TRUE)$JAGS.major==3)
		return(st)
	else
	# JAGS version 4 will not stop at Adaptation incomplete ??with batch.jags TRUE or FALSE??
		return(st[!st %in% c('Adaptation incomplete')])
	
}

loadandcheckrjags <- function(stop=TRUE, silent=FALSE){
	
	fail <- FALSE

	if(!any(.packages(TRUE)=="rjags")){
		if(!silent)
			swcat("\nThe rjags package is not installed - either install the package from CRAN or from https://sourceforge.net/projects/mcmc-jags/files/rjags/\n")
		fail <- TRUE
	}
	
	if(!fail && !requireNamespace("rjags")){
		if(!silent)
			swcat("\nThe rjags package is installed, but could not be loaded - run the testjags() function for more detailed information\n", sep="")
		fail <- TRUE
	}
	if(!fail && packageVersion('rjags') < 3.9){
		if(!silent)
			swcat("\nPlease update the rjags package to version 3-9 or later\n", call.=FALSE)
		fail <- TRUE
	}

	if(fail && stop)
		stop("Loading the rjags package failed (diagnostics are given above this error message)", call.=FALSE)
	
	return(!fail)
}