#' Functions for positioning tick labels on axes
#'
#' \tabular{ll}{
#' Package: \tab labeling\cr
#' Type: \tab Package\cr
#' Version: \tab 0.2\cr
#' Date: \tab 2011-04-01\cr
#' License: \tab Unlimited\cr
#' LazyLoad: \tab yes\cr
#' }
#' 
#' Implements a number of axis labeling schemes, including those 
#' compared in An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes
#' by Talbot, Lin, and Hanrahan, InfoVis 2010.
#'
#' @name labeling-package
#' @aliases labeling
#' @docType package
#' @title Axis labeling
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @references
#' Heckbert, P. S. (1990) Nice numbers for graph labels, Graphics Gems I, Academic Press Professional, Inc.
#' Wilkinson, L. (2005) The Grammar of Graphics, Springer-Verlag New York, Inc.
#' Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010.
#' @keywords dplot
#' @seealso \code{\link{extended}}, \code{\link{wilkinson}}, \code{\link{heckbert}}, \code{\link{rpretty}}, \code{\link{gnuplot}}, \code{\link{matplotlib}}, \code{\link{nelder}}, \code{\link{sparks}}, \code{\link{thayer}}, \code{\link{pretty}}
#' @examples
#' heckbert(8.1, 14.1, 4)	# 5 10 15
#' wilkinson(8.1, 14.1, 4)	# 8 9 10 11 12 13 14 15
#' extended(8.1, 14.1, 4)	# 8 10 12 14

#' # When plotting, extend the plot range to include the labeling
#' # Should probably have a helper function to make this easier
#' data(iris)
#' x <- iris$Sepal.Width
#' y <- iris$Sepal.Length
#' xl <- extended(min(x), max(x), 6)
#' yl <- extended(min(y), max(y), 6)
#' plot(x, y, 
#'     xlim=c(min(x,xl),max(x,xl)), 
#'     ylim=c(min(y,yl),max(y,yl)), 
#'     axes=FALSE, main="Extended labeling")
#' axis(1, at=xl)
#' axis(2, at=yl)
c()



#' Heckbert's labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @return vector of axis label locations
#' @references
#' Heckbert, P. S. (1990) Nice numbers for graph labels, Graphics Gems I, Academic Press Professional, Inc.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
heckbert <- function(dmin, dmax, m)
{
    range <- .heckbert.nicenum((dmax-dmin), FALSE)
    lstep <- .heckbert.nicenum(range/(m-1), TRUE)
    lmin <- floor(dmin/lstep)*lstep
    lmax <- ceiling(dmax/lstep)*lstep
    seq(lmin, lmax, by=lstep)
}
    
.heckbert.nicenum <- function(x, round)
{
	e <- floor(log10(x))
	f <- x / (10^e)
	if(round)
	{
		if(f < 1.5) nf <- 1
		else if(f < 3) nf <- 2
		else if(f < 7) nf <- 5
		else nf <- 10
	}
	else
	{
		if(f <= 1) nf <- 1
		else if(f <= 2) nf <- 2
		else if(f <= 5) nf <- 5
		else nf <- 10
	}
	nf * (10^e)
}



#' Wilkinson's labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @param Q set of nice numbers
#' @param mincoverage minimum ratio between the the data range and the labeling range, controlling the whitespace around the labeling (default = 0.8)
#' @param mrange range of \code{m}, the number of tick marks, that should be considered in the optimization search
#' @return vector of axis label locations
#' @note Ported from Wilkinson's Java implementation with some changes.
#'	Changes: 	1) m (the target number of ticks) is hard coded in Wilkinson's implementation as 5. 
#'						Here we allow it to vary as a parameter. Since m is fixed, 
#'						Wilkinson only searches over a fixed range 4-13 of possible resulting ticks.
#'						We broadened the search range to max(floor(m/2),2) to ceiling(6*m), 
#'						which is a larger range than Wilkinson considers for 5 and allows us to vary m,
#'						including using non-integer values of m.
#'				2) Wilkinson's implementation assumes that the scores are non-negative. But, his revised
#'						granularity function can be extremely negative. We tweaked the code to allow negative scores.
#'						We found that this produced better labelings.
#'				3) We added 10 to Q. This seemed to be necessary to get steps of size 1.
#'	It is possible for this algorithm to find no solution.
#'					In Wilkinson's implementation, instead of failing, he returns the non-nice labels spaced evenly from min to max.
#'					We want to detect this case, so we return NULL. If this happens, the search range, mrange, needs to be increased.
#' @references
#' Wilkinson, L. (2005) The Grammar of Graphics, Springer-Verlag New York, Inc.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
wilkinson <-function(dmin, dmax, m, Q = c(1,5,2,2.5,3,4,1.5,7,6,8,9), mincoverage = 0.8, mrange=max(floor(m/2),2):ceiling(6*m))
{
	best <- NULL
	for(k in mrange)
	{
		result <- .wilkinson.nice.scale(dmin, dmax, k, Q, mincoverage, mrange, m)
		if(!is.null(result) && (is.null(best) || result$score > best$score))
		{
			best <- result
		}
	}
	seq(best$lmin, best$lmax, by=best$lstep)
}

.wilkinson.nice.scale <- function(min, max, k, Q = c(1,5,2,2.5,3,4,1.5,7,6,8,9), mincoverage = 0.8, mrange=c(), m=k)
{
	Q <- c(10, Q)

	range <- max-min
	intervals <- k-1
	granularity <- 1 - abs(k-m)/m

	delta <- range / intervals
	base <- floor(log10(delta))
	dbase <- 10^base

	best <- NULL
	for(i in 1:length(Q))
	{
		tdelta <- Q[i] * dbase
		tmin <- floor(min/tdelta) * tdelta
		tmax <- tmin + intervals * tdelta

		if(tmin <= min && tmax >= max)
		{
			roundness <- 1 - ((i-1) - ifelse(tmin <= 0 && tmax >= 0, 1, 0)) / length(Q)
			coverage <- (max-min)/(tmax-tmin)
			if(coverage > mincoverage)
			{
				tnice <- granularity + roundness + coverage

				## Wilkinson's implementation contains code to favor certain ranges of labels
				## e.g. those balanced around or anchored at 0, etc.
				## We did not evaluate this type of optimization in the paper, so did not include it.
				## Obviously this optimization component could also be added to our function.
				#if(tmin == -tmax || tmin == 0 || tmax == 1 || tmax == 100)
				#	tnice <- tnice + 1
				#if(tmin == 0 && tmax == 1 || tmin == 0 && tmax == 100)
				#	tnice <- tnice + 1

				if(is.null(best) || tnice > best$score)
				{
					best <- list(lmin=tmin,
							 lmax=tmax,
							 lstep=tdelta,
					      	 score=tnice
						)
				}
			}
		}
	}
	best
}




## The Extended-Wilkinson algorithm described in the paper.

## Our scoring functions, including the approximations for limiting the search
.simplicity <- function(q, Q, j, lmin, lmax, lstep)
{
	eps <- .Machine$double.eps * 100

	n <- length(Q)
	i <- match(q, Q)[1]
	v <- ifelse( (lmin %% lstep < eps || lstep - (lmin %% lstep) < eps) && lmin <= 0 && lmax >=0, 1, 0)

	1 - (i-1)/(n-1) - j + v
}

.simplicity.max <- function(q, Q, j)
{
	n <- length(Q)
	i <- match(q, Q)[1]
	v <- 1

	1 - (i-1)/(n-1) - j + v
}

.coverage <- function(dmin, dmax, lmin, lmax)
{
	range <- dmax-dmin
	1 - 0.5 * ((dmax-lmax)^2+(dmin-lmin)^2) / ((0.1*range)^2)
}

.coverage.max <- function(dmin, dmax, span)
{
	range <- dmax-dmin
	if(span > range)
	{
		half <- (span-range)/2
		1 - 0.5 * (half^2 + half^2) / ((0.1 * range)^2)
	}
	else
	{
		1
	}
}

.density <- function(k, m, dmin, dmax, lmin, lmax)
{
	r <- (k-1) / (lmax-lmin)
	rt <- (m-1) / (max(lmax,dmax)-min(dmin,lmin))
	2 - max( r/rt, rt/r )
}

.density.max <- function(k, m)
{
	if(k >= m)
		2 - (k-1)/(m-1)
	else
		1
}

.legibility <- function(lmin, lmax, lstep)
{
	1			## did all the legibility tests in C#, not in R.
}


#' An Extension of Wilkinson's Algorithm for Position Tick Labels on Axes
#'
#' \code{extended} is an enhanced version of Wilkinson's optimization-based axis labeling approach. It is described in detail in our paper. See the references.
#' 
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @param Q set of nice numbers
#' @param only.loose if true, the extreme labels will be outside the data range
#' @param w weights applied to the four optimization components (simplicity, coverage, density, and legibility)
#' @return vector of axis label locations
#' @references
#' Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
extended <- function(dmin, dmax, m, Q=c(1,5,2,2.5,4,3), only.loose=FALSE, w=c(0.25,0.2,0.5,0.05))
{
	eps <- .Machine$double.eps * 100
	
	if(dmin > dmax) {
		temp <- dmin
		dmin <- dmax
		dmax <- temp
	}

	if(dmax - dmin < eps) {
		#if the range is near the floating point limit,
		#let seq generate some equally spaced steps.
		return(seq(from=dmin, to=dmax, length.out=m))
	}
	
	if((dmax - dmin) > sqrt(.Machine$double.xmax)) {
    #if the range is too large
    #let seq generate some equally spaced steps.
    return(seq(from=dmin, to=dmax, length.out=m))
  }

	n <- length(Q)

	best <- list()
	best$score <- -2
	
	j <- 1
	while(j < Inf)
	{
		for(q in Q)
		{
			sm <- .simplicity.max(q, Q, j)

			if((w[1]*sm+w[2]+w[3]+w[4]) < best$score)
			{
				j <- Inf
				break
			}
		
			k <- 2
			while(k < Inf)													# loop over tick counts
			{		
				dm <- .density.max(k, m)

				if((w[1]*sm+w[2]+w[3]*dm+w[4]) < best$score)
					break
			
				delta <- (dmax-dmin)/(k+1)/j/q
				z <- ceiling(log(delta, base=10))

				while(z < Inf)
				{			
					step <- j*q*10^z

					cm <- .coverage.max(dmin, dmax, step*(k-1))

					if((w[1]*sm+w[2]*cm+w[3]*dm+w[4]) < best$score)
						break
					
					min_start <- floor(dmax/(step))*j - (k - 1)*j
					max_start <- ceiling(dmin/(step))*j

					if(min_start > max_start)
					{
						z <- z+1
						next
					}

					for(start in min_start:max_start)
					{
						lmin <- start * (step/j)
						lmax <- lmin + step*(k-1)
						lstep <- step

						s <- .simplicity(q, Q, j, lmin, lmax, lstep)
						c <- .coverage(dmin, dmax, lmin, lmax)						
						g <- .density(k, m, dmin, dmax, lmin, lmax)
						l <- .legibility(lmin, lmax, lstep)						

						score <- w[1]*s + w[2]*c + w[3]*g + w[4]*l

						if(score > best$score && (!only.loose || (lmin <= dmin && lmax >= dmax)))
						{
							best <- list(lmin=lmin,
								 lmax=lmax,
								 lstep=lstep,
						         score=score)
						}
					}
					z <- z+1
				}				
				k <- k+1
			}
		}
		j <- j + 1		
	}

	seq(from=best$lmin, to=best$lmax, by=best$lstep)
}



## Quantitative evaluation plots (Figures 2 and 3 in the paper)


#' Generate figures from An Extension of Wilkinson's Algorithm for Position Tick Labels on Axes
#'
#' Generates Figures 2 and 3 from our paper.
#' 
#' @param samples number of samples to use (in the paper we used 10000, but that takes awhile to run).
#' @return produces plots as a side effect
#' @references
#' Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
extended.figures <- function(samples = 100)
{
	oldpar <- par()
	par(ask=TRUE)
	
	a <- runif(samples, -100, 400)
	b <- runif(samples, -100, 400)
	low <- pmin(a,b)
	high <- pmax(a,b)
	ticks <- runif(samples, 2, 10)

	generate.labelings <- function(labeler, dmin, dmax, ticks, ...)
	{
		mapply(labeler, dmin, dmax, ticks, SIMPLIFY=FALSE, MoreArgs=list(...))
	}
	
	h1 <- generate.labelings(heckbert, low, high, ticks)
	w1 <- generate.labelings(wilkinson, low, high, ticks, mincoverage=0.8)
	f1 <- generate.labelings(extended, low, high, ticks, only.loose=TRUE)
	e1 <- generate.labelings(extended, low, high, ticks)
	
	figure2 <- function(r, names)
	{
		for(i in 1:length(r))
		{
			d <- r[[i]]
			
			#plot coverage
			cover <- sapply(d, function(x) {max(x)-min(x)})/(high-low)
			hist(cover, breaks=seq(from=-0.01,to=1000,by=0.02), xlab="", ylab=names[i], main=ifelse(i==1, "Density", ""), col="darkgray", lab=c(3,3,3), xlim=c(0.5,3.5), ylim=c(0,0.12*samples), axes=FALSE, border=FALSE)
			#hist(cover)
			axis(side=1, at=c(0,1,2,3,4), xlab="hello", line=-0.1, lwd=0.5)
			
			# plot density
			dens <- sapply(d, length) / ticks
			hist(dens, breaks=seq(from=-0.01,to=10,by=0.02), xlab="", ylab=names[i], main=ifelse(i==1, "Density", ""), col="darkgray", lab=c(3,3,3), xlim=c(0.5,3.5), ylim=c(0,0.06*samples), axes=FALSE, border=FALSE)
			axis(side=1, at=c(0,1,2,3,4), xlab="hello", line=-0.1, lwd=0.5)
		}
	}

	par(mfrow=c(4, 2), mar=c(0.5,1.85,1,0), oma=c(1,0,1,0), mgp=c(0,0.5,-0.3), font.main=1, font.lab=1, cex.lab=1, cex.main=1, tcl=-0.2)
	figure2(list(h1,w1, f1, e1), names=c("Heckbert", "Wilkinson", "Extended\n(loose)", "Extended\n(flexible)"))

	figure3 <- function(r, names)
	{
		for(i in 1:length(r))
		{
			d <- r[[i]]
			steps <- sapply(d, function(x) round(median(diff(x)), 2))
			steps <- steps / (10^floor(log10(steps)))
			tab <- table(steps)
			barplot(rev(tab), xlim=c(0,0.4*samples), horiz=TRUE, xlab=ifelse(i==1,"Frequency",""), xaxt='n', yaxt='s', las=1, main=names[i], border=NA, col="gray")
		}
	}
	
	par(mfrow=c(1,4), mar=c(0.5, 0.75, 2, 0.5), oma=c(0,2,1,1), mgp=c(0,0.75,-0.3), cex.lab=1, cex.main=1)
	figure3(list(h1,w1, f1, e1), names=c("Heckbert", "Wilkinson", "Extended\n(loose)", "Extended\n(flexible)"))
	par(oldpar)
}



#' Nelder's labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @param Q set of nice numbers
#' @return vector of axis label locations
#' @references
#' Nelder, J. A. (1976) AS 96. A Simple Algorithm for Scaling Graphs, Journal of the Royal Statistical Society. Series C., pp. 94-96.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
nelder <- function(dmin, dmax, m, Q = c(1,1.2,1.6,2,2.5,3,4,5,6,8,10))
{
	ntick <- floor(m)
	tol <- 5e-6
	bias <- 1e-4

	intervals <- m-1
	x <- abs(dmax)
	if(x == 0) x <- 1
	if(!((dmax-dmin)/x > tol))
	{
		## special case handling for very small ranges. Not implemented yet.
	}

	step <- (dmax-dmin)/intervals
	s <- step

	while(s <= 1)
		s <- s*10
	while(s > 10)
		s <- s/10

	x <- s-bias
	unit <- 1
	for(i in 1:length(Q))
	{
		if(x < Q[i])
		{
			unit <- i
			break
		}
	}
	step <- step * Q[unit] / s
	range <- step*intervals

	x <- 0.5 * (1+ (dmin+dmax-range) / step)
	j <- floor(x-bias)
	valmin <- step * j

	if(dmin > 0 && range >= dmax)
		valmin <- 0
	valmax <- valmin + range

	if(!(dmax > 0 || range < -dmin))
	{
		valmax <- 0
		valmin <- -range
	}

	seq(from=valmin, to=valmax, by=step)
}


#' R's pretty algorithm implemented in R
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @param n number of axis intervals (specify one of \code{m} or \code{n})
#' @param min.n nonnegative integer giving the \emph{minimal} number of intervals. If \code{min.n == 0}, \code{pretty(.)} may return a single value.
#' @param shrink.sml positive numeric by a which a default scale is shrunk in the case when \code{range(x)} is very small (usually 0).
#' @param high.u.bias non-negative numeric, typically \code{> 1}. The interval unit is determined as \code{\{1,2,5,10\}} times \code{b}, a power of 10. Larger \code{high.u.bias} values favor larger units.
#' @param u5.bias non-negative numeric multiplier favoring factor 5 over 2. Default and 'optimal': \code{u5.bias = .5 + 1.5*high.u.bias}.
#' @return vector of axis label locations
#' @references
#' Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) \emph{The New S Language}. Wadsworth & Brooks/Cole.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
rpretty <- function(dmin, dmax, m=6, n=floor(m)-1, min.n=n%/%3, shrink.sml = 0.75, high.u.bias=1.5, u5.bias=0.5 + 1.5*high.u.bias)
{
	ndiv <- n
	h <- high.u.bias
	h5 <- u5.bias

	dx <- dmax-dmin
	if(dx==0 && dmax==0)
	{
		cell <- 1
		i_small <- TRUE
		U <- 1
	}
	else
	{
		cell <- max(abs(dmin), abs(dmax))
		U <- 1 + ifelse(h5 >= 1.5*h+0.5, 1/(1+h), 1.5/(1+h5))
		i_small = dx < (cell * U * max(1, ndiv) * 1e-07 * 3)
	}

	if(i_small)
	{
		if(cell > 10)
		{
			cell <- 9+cell/10
		}
    	cell <- cell * shrink.sml
		if(min.n > 1) cell <- cell/min.n
	}	
	else
	{
		cell <- dx
		if(ndiv > 1) cell <- cell/ndiv
	}

	if(cell < 20 * 1e-07)
		cell <- 20 * 1e-07
	
	base <- 10^floor(log10(cell))

	unit <- base

	if((2*base)-cell < h*(cell-unit))
	{
		unit <- 2*base
		if((5*base)-cell < h5*(cell-unit))
		{
			unit <- 5*base
			if((10*base)-cell < h*(cell-unit))
				unit <- 10*base
		}
	}

	# track down lattice labelings...

	## Maybe used to correct for the epsilon here??
	ns <- floor(dmin/unit + 1e-07)
	nu <- ceiling(dmax/unit - 1e-07)

	## Extend the range out beyond the data. Does this ever happen??
	while(ns*unit > dmin+(1e-07*unit)) ns <- ns-1
	while(nu*unit < dmax-(1e-07*unit)) nu <- nu+1


	## If we don't have quite enough labels, extend the range out to make more (these labels are beyond the data :( )
	k <- floor(0.5 + nu-ns)
	if(k < min.n)
	{
		k <- min.n - k
		if(ns >=0)
		{
			nu <- nu + k/2
			ns <- ns - k/2 + k%%2
		}
		else
		{
			ns <- ns - k/2
			nu <- nu + k/2 + k%%2
		}
		ndiv <- min.n
	}
	else
	{
		ndiv <- k
	}

	graphmin <- ns*unit
	graphmax <- nu*unit

	seq(from=graphmin, to=graphmax, by=unit)
}

#' Matplotlib's labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @return vector of axis label locations
#' @references
#' \url{http://matplotlib.sourceforge.net/}
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
matplotlib <- function(dmin, dmax, m)
{
	steps <- c(1,2,5,10)
	nbins <- m
	trim <- TRUE

	vmin <- dmin
	vmax <- dmax
	params <- .matplotlib.scale.range(vmin, vmax, nbins)
	scale <- params[1]
	offset <- params[2]

	vmin <- vmin-offset
	vmax <- vmax-offset

	rawStep <- (vmax-vmin)/nbins
	scaledRawStep <- rawStep/scale

	bestMax <- vmax
	bestMin <- vmin

	scaledStep <- 1
	chosenFactor <- 1

	for (step in steps)
	{
		if (step >= scaledRawStep)
		{
			scaledStep <- step*scale
			chosenFactor <- step
			bestMin <- scaledStep * floor(vmin/scaledStep)
			bestMax <- bestMin + scaledStep*nbins
			if (bestMax >= vmax)
				break
		}
	}
	if (trim)
	{
		extraBins <- floor((bestMax-vmax)/scaledStep)
		nbins <- nbins-extraBins
	}
	graphMin <- bestMin+offset
	graphMax <- graphMin+nbins*scaledStep

	seq(from=graphMin, to=graphMax, by=scaledStep)
}

.matplotlib.scale.range <- function(min, max, bins)
{
	threshold <- 100
	dv <- abs(max-min)
	maxabsv<-max(abs(min), abs(max))
	if (maxabsv == 0 || dv/maxabsv<10^-12)
		return(c(1, 0))

	meanv <- 0.5*(min+max)

	if ((abs(meanv)/dv) < threshold)
		offset<- 0
	else if (meanv>0)
	{
		exp<-floor(log10(meanv))
		offset = 10.0^exp
	} else
	{
		exp <- floor(log10(-1*meanv))
		offset <- -10.0^exp
	}
	exp <- floor(log10(dv/bins))
	scale = 10.0^exp
	c(scale, offset)
}



#' gnuplot's labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @return vector of axis label locations
#' @references
#' \url{http://www.gnuplot.info/}
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
gnuplot <- function(dmin, dmax, m)
{
	ntick <- floor(m)
	power <- 10^floor(log10(dmax-dmin))
	norm_range <- (dmax-dmin)/power
	p <- (ntick-1) / norm_range

	if(p > 40)
		t <- 0.05
	else if(p > 20)
		t <- 0.1
	else if(p > 10)
		t <- 0.2
	else if(p > 4)
		t <- 0.5
	else if(p > 2)
		t <- 1
	else if(p > 0.5)
		t <- 2
	else
		t <- ceiling(norm_range)

	d <- t*power
	graphmin <- floor(dmin/d) * d
	graphmax <- ceiling(dmax/d) * d

	seq(from=graphmin, to=graphmax, by=d)
}



#' Sparks' labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @return vector of axis label locations
#' @references
#' Sparks, D. N. (1971) AS 44. Scatter Diagram Plotting, Journal of the Royal Statistical Society. Series C., pp. 327-331.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
sparks <- function(dmin, dmax, m)
{
	fm <- m-1
	ratio <- 0
	key <- 1
	kount <- 0
	r <- dmax-dmin
	b <- dmin
	
	while(ratio <= 0.8)
	{
		while(key <= 2)
		{
			while(r <= 1)
			{
				kount <- kount + 1
				r <- r*10
			}
			while(r > 10)
			{
				kount <- kount - 1
				r <- r/10
			}

			b <- b*(10^kount)
			if( b < 0 && b != trunc(b)) b <- b-1
			b <- trunc(b)/(10^kount)
			r <- (dmax-b)/fm
			kount <- 0
			key <- key+2
		}
	
		fstep <- trunc(r)
		if(fstep != r) fstep <- fstep+1
		if(r < 1.5) fstep <- fstep-0.5
		fstep <- fstep/(10^kount)
		ratio <- (dmax - dmin)*(fm*fstep)
		kount <- 1
		key <- 2
	}
	fmin <- b
	c <- fstep*trunc(b/fstep)
	if(c < 0 && c != b) c <- c-fstep
	if((c+fm*fstep) > dmax) fmin <- c
	
	seq(from=fmin, to=fstep*(m-1), by=fstep)
}


#' Thayer and Storer's labeling algorithm
#'
#' @param dmin minimum of the data range
#' @param dmax maximum of the data range
#' @param m number of axis labels
#' @return vector of axis label locations
#' @references
#' Thayer, R. P. and Storer, R. F. (1969) AS 21. Scale Selection for Computer Plots, Journal of the Royal Statistical Society. Series C., pp. 206-208.
#' @author Justin Talbot \email{jtalbot@@stanford.edu}
#' @export
thayer <- function(dmin, dmax, m)
{
	r <- dmax-dmin
	b <- dmin
	kount <- 0
	kod <- 0

	while(kod < 2)
	{
		while(r <= 1)
		{
			kount <- kount+1
			r <- r*10
		}
		while(r > 10)
		{
			kount <- kount-1
			r <- r/10
		}
		b <- b*(10^kount)
		if(b < 0)
			b <- b-1
		ib <- trunc(b)
		b <- ib
		b <- b/(10^kount)
		r <- dmax-b
		a <- r/(m-1)
		kount <- 0
		while(a <= 1)
		{
			kount <- kount+1
			a <- a*10
		}
		while(a > 10)
		{
			kount <- kount-1
			a <- a/10
		}
		ia <- trunc(a)
		if(ia == 6) ia <- 7
		if(ia == 8) ia <- 9

		aa <- 0
		if(a < 1.5) aa <- -0.5
		a <- aa + 1 + ia
		a <- a/(10^kount)
				
		test <- (m-1) * a
		test1 <- (dmax-dmin)/test
		if(test1 > 0.8)
			kod <- 2

		if(kod < 2)
		{
			kount <- 1
			r <- dmax-dmin
			b <- dmin
			kod <- kod + 1
		}
	}

	iab <- trunc(b/a)
	if(iab < 0) iab <- iab-1
	c <- a * iab
	d <- c + (m-1)*a
	if(d >= dmax)
		b <- c

	valmin <- b
	valmax <- b + a*(m-1)	

	seq(from=valmin, to=valmax, by=a)
}

