*! version 1.05, Ben Jann, 27apr2005
program define mrgraph
	version 8.2

//Syntax parsing
	gettoken graphtype 0 : 0
	local graphlist "bar dot hbar tab"
	if !`: list graphtype in graphlist' {
		di as error `"`graphtype' is not a valid graphtype"'
		exit 198
	}
	syntax varlist [if] [in] [fweight aweight] [ , /*
	                 */ ADDval ADDval2(string) Width(numlist >1 int max=1)/*
	 mrtab_options   */ Response(passthru) CONDition(passthru) Poly INclude INCLUDEMissing CASEwise /*
	                 */ COUNTall sort SORT2(numlist int max=1 >0) DEScending by(string) /*
	 _mrsvmat_options*/ Stat(passthru) RTotal CTotal noPercent noLabel /*
	 tabplot_options */ height(real 0.8) HORizontal BARWidth(real 0.5) /*
	 bar/dot_options */ bysep(string) OVERSubopts(string) /*
	 remainder       */ YSCale(passthru) XSCale(passthru) * ]

//Parse by option
	if `"`by'"'!="" {
		ParseBy `by'
	}

//Check options consistency
	if "`graphtype'"=="tab"&"`by'"=="" {
		di as error "graphtype tabplot not allowed unless by() is specified"
		exit 198
	}
	if `"`addval2'"'!="" {
		local addval addval
	}
	else local addval2 " "
	if "`label'"!=""&"`addval'"!="" {
		di as error "nolabel and addval not allowed both"
		exit 198
	}
	if "`rtotal'"!=""&"`by'"=="" {
		di as error "rtotal not allowed unless by() is specified"
		exit 198
	}

//Retrieve frequencies
	mrtab `varlist' `if' `in' [`weight'`exp'] , `response' `condition' `poly' /*
	 */ `include' `includemissing' `casewise' `countall' `by' nofreq
	preserve
	_mrsvmat, `stat' rtotal ctotal `percent' `label' clear returnstat

//Graph label
	if "`stat'"=="freq" local what "frequency"
	else if "`stat'"=="column" local what "column percent (base: cases)"
	else if "`stat'"=="row" local what "row percent"
	else if "`stat'"=="cell" local what "percent (base: cases)"
	else if "`stat'"=="rcolumn" local what "column percent (base: responses)"
	else if "`stat'"=="rcell" local what "percent (base: responses)"

//Sorting
	if "`sort'"!=""|"`sort2'"!="" {
		unab vlist: C* T
		local nov: word count `vlist'
		if "`sort2'"=="" local sort2 `nov'
		if `sort2'<=`nov' {
			sort `: word `sort2' of `vlist'' in 1/`=_N-1'
			if "`descending'"!="" {
				qui gen id=-_n
				sort id in 1/`=_N-1'
				drop id
			}
		}
	}
	qui gen id=_n

//Assemble over-variable
	capture confirm new variable L
	if _rc!=0&"`label'"=="" {
		if "`addval'"!="" {
			qui replace R=R+`"`addval2'"'+L if _n<_N
			qui replace R=L in l
		}
		else qui replace R=L
	}
	capture drop L

//Drop totals
	if "`rtotal'"=="" drop T
	else local T T
	if "`ctotal'"=="" qui drop in l

//Split labels
	if "`width'"!="" {
		forv i=1/`=_N' {
			local lab=R[`i']
			SplitLabel `"`lab'"' `width' nobreak
			qui replace R=`"`lab'"' in `i'
		}
	}

//Graph: univariate/inboard
	if "`by'"=="" | ( "`bysep'"=="inboard" & "`graphtype'"!="tab" ) {
		graph `graphtype' (asis) C* `T', over(R, sort(id) `oversubopts') ///
		 yti(`what') `yscale' `xscale' `options'
		exit
	}

//Prepare data for other graphs
//Rename totals variable (=> reshape)
	if "`T'"=="T" {
		unab vlist: C*
		local nov: word count `vlist'
		rename T C`++nov'
	}
//Get labels of by-groups
	unab vlist: C*
	local nov: word count `vlist'
	forv i=1/`nov' {
		local C: word `i' of `vlist'
		local vallab `"`vallab'`i' `"`: var l `C''"' "'
	}
//Reshape
	tokenize `by', parse("()")
	qui reshape long C, i(id) j(`3')
	tempname `3'
	lab def ``3'' `vallab'
	lab val `3' ``3''

//Graph: outboard
	if "`graphtype'"!="tab" {
		if "`bysep'"=="outboard" {
			graph `graphtype' (asis) C ,  over(R,sort(id) `oversubopts') ///
			 yti(`what') over(`3'`byopts') ///
			 `yscale' `xscale' `options'
		}

//Graph: separate
		else if "`bysep'"=="separate" {
			if "`sort'"!=""|("`sort'"==""&"`sort2'"=="") {
				local sort2 id
				local descending
			}
			graph `graphtype' (asis) C, over(R, sort(`sort2') `descending' `oversubopts') ///
			 yti(`what') by(`3'`byopts') ///
			 `yscale' `xscale' `options'
		}
		exit
	}

//Tabplot
//Levels and labels
	qui count if `3'==1
	local nor `r(N)'
	sort `3' id
	forv i=1/`nor' {
		local lab=R[`i']
		local ylabels `"`ylabels'`i++' `"`lab'"' "'
	}
//Direction
	if index("`stat'","column") local horizontal vertical
	else if "`stat'"=="row" local horizontal horizontal
	if "`horizontal'"!="horizontal" {
		local ygrid grid
		local xgrid nogrid
		local lhs id
		local rhs `3'
		if index(`"`yscale'"',"norev") {
			local sign "+"
		}
		else local sign "-"
	}
	else {
		local ygrid nogrid
		local xgrid grid
		local lhs `3'
		local rhs id
		if index(`"`xscale'"',"rev")&!index(`"`xscale'"',"norev") {
			local sign "-"
		}
		else local sign "+"
	}
//Compute margins
	sum C, meanonly
	qui gen M = `lhs' `sign' `height' * C / `r(max)'
	local max = trim("`: di %6.0g r(max)'")
//Graph
	twoway scatter id `3', ms(none)  yla(`ylabels', `ygrid' ang(h)) yscale(reverse) ///
	 || rbar M `lhs' `rhs', barw(`barwidth') xla(1(1)`nov', `xgrid' val) ///
	 subtitle(`what', place(w) size(medsmall)) legend(off) yti("") xti("") ///
	 note("maximum: `max'") `yscale' `xscale'  `options' `horizontal'
end

program ParseBy
	syntax varname [ , INboard OUTboard SEParate * ]
	local bysep `inboard'`outboard'`separate'
	if length("`bysep'")>8 {
		di as error "by_subopts inboard, outboard, separate are mutually exclusive"
		exit 198
	}
	else if "`bysep'"=="" local bysep inboard
	c_local bysep `bysep'
	c_local by "by(`varlist')"
	if `"`options'"'!="" c_local byopts `", `options'"'
end

program SplitLabel
	args lab width nobreak
	local j 1
	local temp : piece `j' `width' of `"`lab'"', `nobreak'
	local newlab `"`"`temp'"'"'
	local j 2
	local temp : piece `j' `width' of `"`lab'"', `nobreak'
	if `"`temp'"'=="" local newlab `"`newlab' """'
	while `"`temp'"'!="" {
		local newlab `"`newlab' `"`temp'"'"'
		local j=`j'+1
		local temp : piece `j' `width' of `"`lab'"', `nobreak'
	}
	c_local lab `"`newlab'"'
end