*! 1.1.1 NJC 27 April 2016
* 1.1.0 NJC 13 April 2016
* 1.0.4 NJC 1 Nov 2007
* 1.0.3 NJC 30 Oct 2007
* 1.0.2 NJC 25 Oct 2007
* 1.0.1 NJC 19 Oct 2007
* 1.0.0 NJC 17 Oct 2007
program spineplot, rclass
	version 8.2
	// -levels- or -levelsof- must be present
	if _caller() >= 9 local of "of"

	forval i = 1/20 {
		local baropts `baropts' bar`i'(str asis)
	}
	syntax varlist(min=2 max=2 numeric) [fweight aweight/] [if] [in] ///
	[, MISSing PERCent `baropts' text(str asis) barall(str asis) * ]

	quietly {
		if "`missing'" != "" marksample touse, novarlist zeroweight
		else marksample touse, zeroweight
		count if `touse'
		if r(N) == 0 error 2000

		if `"`text'"' != "" {
			gettoken textvar text : text, parse(",")
			capture confirm variable `textvar'
			if _rc {
				di as err "`textvar' not a variable"
				exit 198
			}
			gettoken comma text : text, parse(",")
		}

		tokenize "`varlist'"
		args y x
		preserve
		tempvar f xf xF xmid tag yF ymid xshow

		keep if `touse'
		if "`exp'" == "" local exp = 1
		bysort `x' `y': gen double `f' = sum(`exp')
		by `x' `y': keep if _n == _N
		fillin `x' `y'
		replace `f' = 0 if `f' == .
		drop _fillin
		compress `f'

		if "`percent'" != "" {
			local factor 100
			local la 0 25 50 75 100
			local what "percent"
		}
		else {
			local factor 1
			local la 0 .25 .5 .75 1
			local what "fraction"
		}

		bysort `x' (`y'): gen `xf' = sum(`f')
		by `x' : replace `xf' = `xf'[_N]

		by `x' : gen byte `tag' = _n == 1
		gen `xF' = sum(`xf' * `tag')
		local total = `xF'[_N]
		replace `xF' = (`factor' * `xF') / `total'
		replace `xf' = (`factor' * `xf') / `total'
		by `x': gen `xmid' = `xF'[_N] - 0.5 * `xf'[_N]

		by `x' : gen `yF' = sum(`f')
		by `x' : gen `ymid' = `yF' - 0.5 * `f'
		by `x' : replace `ymid' = `factor' * `ymid'/ `yF'[_N]
		by `x' : replace `yF' = `factor' * `yF'/ `yF'[_N]

		local first = _N + 1
		expand 2 if `x' == `x'[_N]
		replace `yF' = . in `first'/l
		sort `y' `x' `yF'
		by `y' : gen `xshow' = cond(_n == 1, 0, `xF'[_n - 1])

		local xtitle : var label `x'
		if `"`xtitle'"' == "" local xtitle `x'
		local ytitle : var label `y'
		if `"`ytitle'"' == "" local ytitle `y'

		// returned label specification uses 4 d.p. only
		// to simplify macro contents
		levels`of' `x', local(levels)
		foreach l of local levels {
			su `xmid' if `x' == `l', meanonly
			local l : label (`x') `l'
			local min : di %5.4f r(min)
			local toreturn `" `toreturn' `min' "`l'" "'
			local XLA `" `XLA' `r(min)' "`l'" "'
		}

		return local catlabels `toreturn'
		drop `f' `xf' `xF' `tag'

		levels`of' `y', local(levels)
		local ny : word count `levels'
		tokenize `levels'
		forval i = `ny'(-1)1 {
			local pipe = cond(`i' > 1, "||", "")
			local I = `ny' - `i' + 1

			local call `call' ///
			bar `yF' `xshow' if `y' == ``i'' ///
			, bartype(spanning) blcolor(bg) blw(medium) ///
			`barall' `bar`I'' `pipe'

			local which : label (`y') ``i''
			local lgnd `lgnd' `I' `"`which'"'
		}

		if `"`textvar'"' != "" {
			local texttoplot mla(`textvar') mlabpos(0)
		}
	}

	twoway `call'                                    ///
	xaxis(1 2)                                       ///
	xsc(r(0, `factor') axis(1))                      ///
	xsc(r(0, `factor') axis(2))                      ///
	xla(`XLA', axis(2) noticks)                      ///
	xla(`xla', axis(1))                              ///
	yaxis(1 2)                                       ///
	ysc(r(0, `factor'))                              ///
	yla(`la', ang(h) nogrid axis(2))                 ///
	yla(none, ang(h) nogrid axis(1))                 ///
	xtitle(`"`xtitle'"', axis(2))                    ///
	xtitle(`"`what' by `xtitle'"', axis(1))          ///
        ytitle(`"`what' by `ytitle'"', axis(2))          ///
        ytitle("", axis(1))                              ///
	legend(order(`lgnd') col(1) pos(3))              ///
	`options'                                        ///
	|| scatter `ymid' `xmid' if !missing(`textvar'), ///
	ms(none)                                         ///
	`texttoplot' `text'
end