* version 0.0.1 :: 06/12/12 2:07 PM
* version 0.0.2 :: 06/12/12 8:45 PM :: fix bug with 1x1 margin
*!version 0.0.3 :: 06/13/12 8:00 AM :: combing gen() genstub() lines

/*

To-do:
1. statsto statout passthrough

*/

//  Program     : margsto
//  Author      : Joseph D. Wolfe & J. Scott Long
//  Description : Format and Export Matrices
//  Requirements: >=Stata 10 (untested with previous versions)

capture clear programs
program define margsto

syntax [anything] ,               ///
	[                             ///
                                  /// Row/Column specification
	ROWSelect(string asis)        /// name for rows()
	rorder(string asis)           /// select and reorder row
	corder(string asis)           /// select and reorder row
	STATistics(string asis)       /// only for matrix returns
	ATvar(string asis)            /// specify/rename margins' r(at)
	atoff                         /// Do not add at matrix
                                  ///
								  /// Viewing options
	nolist                        /// list matrix
	DECimals(numlist >-1 integer) /// digits when list matrix
	title(string)                 /// title added when listing
                                  ///
					              /// Matrix utilities
	clear                         /// reset matrix to null
	GENerate(string asis)         /// create variables from columns
								  ///
	LABel(string asis)            /// Use at values to label
                                  ///
    genstub(string asis)          /// Generate columns with stub
								  /// Advanced options
	NOStrip						  /// Don't remove omitted information
	]

//  #2
//  Create basics, macros, & temporary names

tempname INmat missmat tmpmat INmatrowNM INmatcolNM INmatrNUM INmatcNUM
tempname ATlabel a b

if "`anything'"   =="" local anything "MargSto"
if "`nolist'"     =="" local list  "list"
if "`statistics'" =="" local statistics `"Margin=b Pvalue=p"'
if "`decimals'"   =="" local decimals = 3
if "`clear'"      !="" local clear "clear"
if "`atoff'"      !="" local atoff = 1
if "`atoff'"      =="" local atoff = 0
if "`atvar'"=="" & "`label'"!="" & !`atoff' local atoff=1

*make sure it's following margins
local cmd = r(cmd)
if `"`cmd'"'!="margins" {
	di as error ///
	"margsto must be run immediately following a margins command"
	exit 198
}

//  #3
//  Remove Missing Values In r(at)

capture confirm matrix r(at)
local atmat = _rc

*if r(at) exists remove missing columns
if !`atmat' {
	mat   `INmat'     = r(at)
	mata: `INmat'     = st_matrix("`INmat'")

	*find out where missing columns are located
	mata: `missmat'   = colmissing(`INmat')

	*get matrix size in mata and stata
	mata: `INmatrNUM' = rows(`INmat')
	mata: `INmatcNUM' = cols(`INmat')
	local  INmatrNUM  = rowsof(`INmat')
	local  INmatcNUM  = colsof(`INmat')

	*create a matrix in stata to stripe mata matrix
	mata:  st_matrix("`missmat'", `missmat')
	mata: `missmat' = st_matrix("`INmat'")
	mat   `tmpmat'  = J(1,`INmatcNUM',1)

	forvalues j = 1/`INmatcNUM' {
		if `missmat'[1,`j'] == `INmatrNUM' mat `tmpmat'[1,`j'] = 0
	}

	if "`atvar'"=="" {
		*update matrices in mata
		mata: `tmpmat'     = st_matrix("`tmpmat'")
		mata: `INmatcolNM' = st_matrixcolstripe("r(at)")
		mata: `INmatrowNM' = st_matrixrowstripe("r(at)")
		mata: `INmat'      = select(`INmat', (`tmpmat'))
		mata: `INmatcolNM' = select(`INmatcolNM', (`tmpmat''))

		*send new matrices back to stata
		mata: st_matrix("`INmat'", `INmat')
		mata: st_matrixcolstripe("`INmat'", `INmatcolNM')
		mata: st_matrixrowstripe("`INmat'", `INmatrowNM')
	}
}

//  #4
//  Add Matrix Labels

if `"`label'"'!="" & !`atmat' {
	mat   `INmat'     = r(at)
	local matcolnms : colnames r(at)

	local labpos ""
	foreach lab of local label {
		local pos : list posof "`lab'" in matcolnms

		*give error if at label name isn't in r(at) column names
		if "`pos'" == "" {
			di as error ///
			"Column `lab' is not in r(at) matrix"
			exit 198
		}

		local labpos "`labpos' `pos'"
	}

	mata: `ATlabel' = J(`INmatrNUM', 2,"")

	forvalues i = 1/`INmatrNUM' {
		local labhold `"`label'"'
		local lpnum = 0
		foreach l of local labpos {
			local ++lpnum
			gettoken labnm labhold : labhold, parse(" ")
			local b = `INmat'[`i',`l']
			local b : di %9.3g `b'

			*error if . in label
			if regexm("`b'", "\.") {
				di as error ///
				`"Label `labnm' contains ".", which cannot be used to label matrix rows"'
				exit 198
			}

			local b = trim("`b'")
			if `lpnum'==1 local a `"`labnm'`b'"'
			if `lpnum' >1 local a `"`a' `labnm'`b'"'
		}

		local lablgth : length local a

		if `lablgth'<12 local lablgth=12

		mata: `ATlabel'[`i',2] = `"`a'"'
	}
}

//  #4
//  Create Matrix

if "`corder'"!="" | "`rorder'"!="" local skip = 1
else local skip = 0

if "`atvar'"=="" & !`skip' {
	statsto `anything', rbreturns(`statistics') `clear'
	if !`atmat' & !`atoff' {
		statsto `anything', mat(`INmat') coleq(At)
	}
}

if "`atvar'"!="" & !`skip' {
	statsto `anything', rbreturns(`statistics') `clear'
	if !`atmat' & !`atoff' {
		statsto `anything', at(`atvar') coleq(At)
	}
}

//  #5
//  Generate Variables

if `"`generate'"'!="" | `"`genstub'"'!="" {
	statout `anything', generate(`generate') genstub(`genstub')
}

//  #6
//  Update Row Labels

if `"`label'"'!="" & !`atmat' {
	mata: st_matrixrowstripe("`anything'", `ATlabel')
}

//  #7
//  List Matrix

statout `anything', `list' title(`title') dec(`decimals')      ///
					rorder(`rorder') corder(`corder') nocount  ///
					rwidth(`lablgth')

//  #8
//  Close

end
exit