*! 1.0.1 jmendelson 01nov2021
*  1.0.0 jmendelson 16oct2015
*  topline 3.0.0 (private version) jmendelson 16oct2013
*  topline 2.1.0 (private version) jmendelson 23jan2013
*  topline 2.0.1 (private version) jmendelson 5dec2012
*  topline 2.0.0 (private version) jmendelson 3aug2012
*  topline 1.0.0 (private version) jmendelson 7jul2012
/*
Changelog:
v1.0.1:
	Changed all instances of "svy linearized" to "svy"
		(this fixes a bug that would have occurred for variance estimates if users intended to use replicate methods [e.g., bootstrap, BRR, jackknife, SDR])
*/
program define tabsheet
	version 12.1
	syntax varlist (numeric) [if] [in], [SUBpops(varlist)] TYPE(string) [SIMPLE] [NOBREAK] [HEADER(string)] [SUPPress(string)] [OVERse] [LINEVAL(string)] [SORT]
	marksample touse, novarlist

	if("`subpops'"=="" & "`type'"!="h" & "`type'"!="header") {
		display _n "{error}Option subpops() required" as err
		error 198
	}

	if("`subpops'"!="") {
		foreach v of varlist `subpops' {
			tempvar tv
			qui: recode `v' (1=1) (0=1) (missing=1) (nonmissing=0) if(`touse'), gen(`tv')
			qui: capture assert `tv'==1 if(`touse')
			if(_rc!=0) {
				display _n "{error}Error! Subpop var `v' has nonmissing values other than 0 and 1." as err
				error 450
			}
		}
	}

	if("`header'"!="" & "`header'"!="varname" & "`header'"!="varlabel") {
		display _n "{error}Error! Invalid 'header' type. Valid options are header(varname) and header(varlabel)" as err
		error 198
	}

	qui: file query
	if(r(N)==0) {
		display _n "{error}Error!" _n "You need to open a file with the filehandle 'outfile' before running tabsheet." _n as err		//error displays if no files are open
		error 601
	}

	local suppstr "`suppress'"														//`suppstr' lists the suppressed values
	if("`suppstr'"!="") local suppstring "suppress((`suppstr'=.))"					//Creates a macro that can be dropped into recode statement

	if("`lineval'"=="") local linestring ""
	if("`lineval'"!="") local linestring "lineval(`lineval')"							//create a string that can be used to pass on the optional argument to subroutines

	if("`type'"=="header" | "`type'"=="h") local subpops "`varlist'"					//for type(header), use varlist as the list of subpops (so users don't have to enter it twice)
	if(("`type'"=="header" | "`type'"=="h") & "`header'"=="") local header "varname"	//by default, for type(header), use unlabeled varname

	if("`header'"!="") header `subpops', `simple' header(`header')						//If using a header flag, uses subpops; if type(header), then subpops will have been set to varlist
	if("`type'"=="prop" | "`type'"=="p") topfull `varlist' `if' `in', sub(`subpops') `simple' `suppstring' `overse'
	if("`type'"=="mean" | "`type'"=="m") topmean `varlist' `if' `in', sub(`subpops') `simple' `suppstring' `overse'
	if("`type'"=="line" | "`type'"=="l") tline`sort' `varlist' `if' `in', sub(`subpops') `simple' `suppstring' `overse' `linestring'

	if("`nobreak'"=="" & "`type'"!="prop" & "`type'"!="p") file write outfile _n		//by default, adds extra line at end (except for type(prop))
end

**Writes header line(s); two types: header(varname) or header(varlabel)
program define header
syntax varlist, HEADER(string) [SIMPLE]
file write outfile _tab
foreach var of varlist `varlist' {
	local `var'varlabel: variable label `var'
	if(`"``var'varlabel'"'=="" | "`header'"=="varname") local `var'varlabel "`var'"
	file write outfile `"``var'varlabel'"' _tab
	if("`simple'"!="simple") file write outfile _tab _tab
}
file write outfile _n _tab
foreach var of varlist `varlist' {
	file write outfile "Value/%" _tab
	if("`simple'"!="simple") file write outfile "SE" _tab "n" _tab
}
file write outfile _n
end

**Outputs proportions for every value in a variable
program define topfull
syntax varlist [if] [in] [, SUBpops(varlist) SIMPLE SUPPress(string) OVERse]
marksample touse, novarlist
foreach var of varlist `varlist' {
	local `var'varlabel: variable label `var'
	if(`"``var'varlabel'"'=="") local `var'varlabel "`var'"									//If var not labeled, uses var name
	file write outfile `"``var'varlabel'"' _n												//Display var label on its own line

	tempvar vp`var'
	qui: clonevar `vp`var'' = `var'
	qui: if("`suppress'"!="") recode `vp`var'' `suppress'									//recodes any suppressed values to missing

	qui: levelsof `vp`var'' if(`touse'), local(levels)
	local `var'labels: value label `var'
	local counter = 0

	foreach l of local levels {
		capture local vlabel: label ``var'labels' `l'
		if (_rc!=0) local vlabel "`l'"														//If there is no value label, then simply use the number
		file write outfile `"`vlabel'"' _tab
		tempvar r`counter'_`var'
		qui: recode `vp`var'' (`l'=1) (missing=.) (nonmissing=0) if(`touse'), gen(`r`counter'_`var'')
		topprop `r`counter'_`var'' `if' `in', sub(`subpops') `simple' `overse'
		local counter = `counter'+1
	}

	if("`simple'"=="simple") {
		file write outfile "n"
		foreach spvar of varlist `subpops' {
			qui: count if(`vp`var''<. & `spvar'==1 & `touse')										//var<. means var is non-missing (since there are mult values for missing vals)
			file write outfile _tab (r(N))
		}
		file write outfile _n
	}

	file write outfile _n
}
end

**Outputs a line of stats for a binary variable
program define topprop
syntax varlist [if] [in] [, SUBpops(varlist) SIMPLE OVERse]
marksample touse, novarlist
foreach var of varlist `varlist' {
	tempname tempmat_b tempmat_ev temp_var temp_se count0 count1 countsum tempnmat tempn_over
	foreach spvar of varlist `subpops' {
		qui: count if(`var'==0 & `spvar'==1 & `touse')
		scalar `count0' = r(N)
		qui: count if(`var'==1 & `spvar'==1 & `touse')
		scalar `count1' = r(N)
		scalar `countsum' = `count0' + `count1'

		if(`countsum'==0) {
			file write outfile "n/a" _tab
			if("`simple'"!="simple") file write outfile "n/a" _tab (`countsum') _tab
		}
		else if(`count0'==0 & `count1'!=0) {
			file write outfile ("100%") _tab
			if("`simple'"!="simple") file write outfile "n/a" _tab (`countsum') _tab
		}
		else if(`count0'!=0 & `count1'==0) {
			file write outfile ("0%") _tab
			if("`simple'"!="simple") file write outfile "n/a" _tab (`countsum') _tab
		}
		else if(`count0'!=0 & `count1'!=0) {
			if("`overse'"=="") {
				qui: svy: proportion `var' if(`touse'), subpop(`spvar')
				matrix `tempmat_b' = e(b)
				file write outfile (`tempmat_b'[1,2]*100) "%" _tab
				if("`simple'"!="simple") {
					matrix `tempmat_ev' = e(V)
					scalar `temp_var' = `tempmat_ev'[2,2]
					scalar `temp_se' = `temp_var'^.5
					file write outfile (`temp_se') _tab (e(N_sub)) _tab
				}
			}
			else if("`overse'"=="overse") {
				qui: svy: proportion `var' if(`touse'), over(`spvar')
				matrix `tempmat_b' = e(b)
				file write outfile (`tempmat_b'[1,e(N_over)*2]*100) "%" _tab				//will be in [1,2] or [1,4] depending on whether everyone is in same category or not
				if("`simple'"!="simple") {
					matrix `tempmat_ev' = e(V)
					scalar `temp_var' = `tempmat_ev'[e(N_over)*2,e(N_over)*2]				//will be in [2,2] or [4,4]
					scalar `temp_se' = `temp_var'^.5
					matrix `tempnmat' = e(_N)
					scalar `tempn_over' = `tempnmat'[1,e(N_over)*2]
					file write outfile (`temp_se') _tab (`tempn_over') _tab
				}
			}
		}
	}

	file write outfile _n
}
end

**Outputs a line of stats for a binary variable, and also writes the var name
program define tline
syntax varlist [if] [in] [, SUBpops(varlist) SIMPLE SUPPress(string) OVERse LINEVAL(string)]
marksample touse, novarlist

if("`lineval'"!="") file write outfile "% providing the following options: `lineval'" _n

foreach var of varlist `varlist' {
	local `var'varlabel: variable label `var'
	if(`"``var'varlabel'"'=="") local `var'varlabel "`var'"									//If var not labeled, uses var name
	file write outfile `"``var'varlabel'"' _tab
	tempvar bin_`var'
	qui: if("`lineval'"=="") recode `var' `suppress' (1=1) (missing=.) (nonmissing=0) if(`touse'), gen(`bin_`var'')
	qui: if("`lineval'"!="") recode `var' `suppress' (`lineval'=1) (missing=.) (nonmissing=0) if(`touse'), gen(`bin_`var'')
	topprop `bin_`var'' `if' `in', sub(`subpops') `simple' `overse'
	qui: inspect `var' if(`touse')
	if("`lineval'"=="" & r(N_unique)>2) dis "{error}Warning: `var' has more than two unique nonmissing values; please confirm that output is as intended."
}
end

**Determines the ordering for the proportion for the first subpopulation, and then runs the line command on the reordered list
program define tlinesort
syntax varlist [if] [in] , SUBpops(varlist) [SIMPLE SUPPress(string) OVERse LINEVAL(string)]
marksample touse, novarlist

tempname propitem count0 count1 countsum tempmat propstorage vartoadd

tokenize `subpops'
local firstsp `1'																		//We will be sorting only for cases in the first subpopulation

qui: describe `varlist', varlist
local vlist_expanded `r(varlist)'														//Expand any varlists that use wildcards
local sizeof_vlist: word count `vlist_expanded'

matrix `propstorage' = J(`sizeof_vlist',2,.)											//Initialize a matrix for storing %'s

tokenize `vlist_expanded'

forval i=1/`sizeof_vlist' {
	tempvar myvar

	qui: if("`lineval'"=="") recode ``i'' `suppress' (1=1) (missing=.) (nonmissing=0) if(`touse' & `firstsp'==1), gen(`myvar')
	qui: if("`lineval'"!="") recode ``i'' `suppress' (`lineval'=1) (missing=.) (nonmissing=0) if(`touse' & `firstsp'==1), gen(`myvar')

	scalar `propitem' = "n/a"						//This line is probably unnecessary and could conceivably cause an error if something else is incorrect, but doesn't do any harm and this potentially calls attention to any issues
	qui: count if `myvar'==0
	scalar `count0' = r(N)
	qui: count if `myvar'==1
	scalar `count1' = r(N)
	scalar `countsum' = `count0' + `count1'
	if(`countsum'==0) scalar `propitem' = 0			//Technically it might be better to add something to deal with items like this, but that complicates things and the user should know better; maybe address later
	if(`count0'==0 & `count1'!=0) scalar `propitem' = 1
	if(`count0'!=0 & `count1'==0) scalar `propitem' = 0
	if(`count0'>0 & `count1'>0) {
		qui: svy: proportion `myvar'
		matrix `tempmat' = e(b)
		scalar `propitem' = `tempmat'[1,2]
	}

	matrix `propstorage'[`i',1] = -`propitem'		//Put in the negative proportion since Mata sort is ascending
	matrix `propstorage'[`i',2] = `i'
}

mata: st_matrix("`propstorage'", sort(st_matrix("`propstorage'"), 1))		//Sort the matrix by %

local reorderedvlist ""
tokenize `vlist_expanded'
forval i=1/`sizeof_vlist' {
	local reorderedvlist "`reorderedvlist' ``=`propstorage'[`i',2]''"
}

if("`lineval'"=="") local linestring ""
if("`lineval'"!="") local linestring "lineval(`lineval')"

if("`suppress'"!="") local suppstring "supp(`suppress')"

tline `reorderedvlist' `if' `in', sub(`subpops') `simple' `suppstring' `overse' `linestring'			//Run tline on the reordered variable list
end

**Outputs a line of means
program define topmean
syntax varlist [if] [in] [, SUBpops(varlist) SIMPLE SUPPress(string) OVERse]
marksample touse, novarlist
foreach var of varlist `varlist' {
	tempname countObs tempmat_e tempmat_ev temp_var temp_se temp_b temp_nmat temp_n

	tempvar vp`var'
	qui: clonevar `vp`var'' = `var'
	qui: if("`suppress'"!="") recode `vp`var'' `suppress'									//recodes any suppressed values to missing

	local `var'varlabel: variable label `var'
	if(`"``var'varlabel'"'=="") local `var'varlabel "`var'"									//If var not labeled, uses var name
	file write outfile `"Mean of ``var'varlabel'"' _tab
	foreach spvar of varlist `subpops' {
		qui: count if(`vp`var''<. & `spvar'==1 & `touse')
		scalar `countObs' = r(N)
		if(`countObs'==0) {
			file write outfile "n/a" _tab
			if("`simple'"!="simple") file write outfile "n/a" _tab "0" _tab
		}
		else if(`countObs'>0) {
			if("`overse'"=="") {
				qui: svy: mean `vp`var'' if(`touse'), subpop(`spvar')
				matrix `tempmat_e' = e(b)

				file write outfile (`tempmat_e'[1,1]) _tab
				if("`simple'"!="simple") {
					matrix `tempmat_ev' = e(V)
					scalar `temp_var' = `tempmat_ev'[1,1]
					scalar `temp_se' = `temp_var'^.5
					if(`countObs'==1) scalar `temp_se' = "n/a"									//If n=1, there is no standard error
					file write outfile (`temp_se') _tab (e(N_sub)) _tab
				}
			}
			else if("`overse'"=="overse") {
				qui: svy: mean `vp`var'' if(`touse'), over(`spvar')
				matrix `tempmat_e' = e(b)
				scalar `temp_b' = `tempmat_e'[1,e(N_over)]

				file write outfile (`temp_b') _tab
				if("`simple'"!="simple") {
					matrix `tempmat_ev' = e(V)
					matrix `temp_nmat' = e(_N)
					scalar `temp_n' = `temp_nmat'[1,e(N_over)]
					scalar `temp_var' = `tempmat_ev'[e(N_over),e(N_over)]
					scalar `temp_se' = `temp_var'^.5
					if(`countObs'==1) scalar `temp_se' = "n/a"									//If n=1, there is no standard error
					file write outfile (`temp_se') _tab (`temp_n') _tab
				}
			}
		}
	}
	file write outfile _n
}
end