*! 1.0.0 NJC 29 April 2022
program nicelabels
	/// step() undocumented
	version 9

 	gettoken first 0 : 0, parse(" ,")

	capture confirm numeric variable `first'

	if _rc == 0 {
		// syntax varname(numeric), Local(str) [ nvals(int 5) tight step(str)]

		syntax [if] [in] , Local(str) [ nvals(int 5) tight step(str) ]
		local varlist `first'

		marksample touse
		quietly count if `touse'
		if r(N) == 0 exit 2000
	}
	else {
		// syntax #1 #2 , Local(str) [ nvals(int 5) tight step(str) ]

		confirm number `first'
		gettoken second 0 : 0, parse(" ,")
		syntax , Local(str) [ nvals(int 5) tight step(str) ]

		if _N < 2 {
			preserve
			quietly set obs 2
		}

		tempvar varlist touse
		gen double `varlist' = cond(_n == 1, `first', `second')
		gen byte `touse' = _n <= 2
	}

	local tight = "`tight'" == "tight"
	su `varlist' if `touse', meanonly
	mata: nicelabels(`r(min)', `r(max)', `nvals', `tight')

	di "step:{col 12}`interval'"
	di "labels:{col 12}`results'"
	c_local `local' "`results'"
	if "`step'" != "" c_local `step' "`interval'"
end

mata :

void nicelabels(real min, real max, real nvals, real tight) {
	if (min == max) {
		st_local("results", min)
		exit(0)
	}

	real range, d, newmin, newmax
	colvector nicevals
	range = nicenum(max - min, 0)
	d = nicenum(range / (nvals - 1), 1)
	newmin = tight == 0 ? d * floor(min / d) : d * ceil(min / d)
	newmax = tight == 0 ? d * ceil(max / d) : d * floor(max / d)
	nvals = 1 + (newmax - newmin) / d
	nicevals = newmin :+ (0 :: nvals - 1) :* d
	st_local("interval", strofreal(d))
	st_local("results", invtokens(strofreal(nicevals')))
}

real nicenum(real x, real round) {
	real expt, f, nf

	expt = floor(log10(x))
	f = x / (10^expt)

	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
	}

	return(nf * 10^expt)
}

end