*! NJC 2.1.2 7 November 2005
*! NJC 2.1.1 12 December 2004
*! NJC 2.1.0 6 February 2002
*! NJC/CFB 2.0.0 3 October 2001
* CFB 1.0.0 1 October 2001
program def statsmat, rclass
        version 7.0
        syntax varlist(min=1) [if] [in] [aweight fweight] /*
	*/ [ , Matrix(str) Stat(str) noHeader BY(varname) XPose /*
	*/ MISSing LISTWISE MAGIC(numlist max=1) * ]

	* version 7: missings not allowed, so apply magic
	* otherwise: no magic
	if "`magic'" == "" {
		if "`c(version)'" == "" | _caller() <= 7 {
			local magic "1e+300"
			local nomagic "*"
		}
		else {
			local applymagic "*"
		}
	}

	* what data to use
	if "`listwise'" == "" {
		marksample touse
	}
	else {
		tempvar touse
		mark `touse' `if' `in'
	}

	if "`by'" != "" & "`missing'" == "" { markout `touse' `by', strok }
	qui count if `touse'
	if r(N) == 0 { error 2000 }

	if "`listwise'" == "" {
		return local N = r(N)
	}

	* syntax checking: { varlist | varname, by(byvar) }
	local nvars : word count `varlist'

	if "`by'" != "" {
		if `nvars' > 1 {
			di in r "by() option only allowed with single varname"
			exit 198
		}
	}

	* which statistics?
        if "`stat'" == "" { local stat "min q max mean sd" }
	local monly  "meanonly"

        foreach st of local stat {
                if "`st'" == "n" | "`st'" == "N" | "`st'" == "count" {
                        local s "N"
                }
                else if "`st'" == "sum" | "`st'" == "sum_w" {
                        local s "`st'"
                }
                else if "`st'" == "mean" {
                        local s "mean"
		}
                else if "`st'" == "min" | "`st'" == "max" {
                        local s "`st'"
                }
		else if "`st'" == "range" {
			local s "range"
		}
                else if "`st'" == "var" | "`st'" == "Var" {
			local monly
                        local s "Var"
                }
                else if "`st'" == "SD" | "`st'" == "sd" {
			local monly
                        local s "sd"
                }
		else if "`st'" == "p" {
			local monly
			local detail "detail"
			local s "p1 p5 p10 p25 p50 p75 p90 p95 p99"
			local st "`s'"
		}
		else if "`st'" == "q" {
			local monly
			local detail "detail"
			local s "p25 p50 p75"
			local st "`s'"
		}
                else if "`st'" == "p1" | "`st'" == "p5" {
       			local monly
			local detail "detail"
                	local s "`st'"
                }
                else if "`st'" == "p10" | "`st'" == "p25" {
			local monly
			local detail "detail"
                        local s "`st'"
                }
                else if "`st'" == "p50" /*
		 */ | "`st'" == "med" | "`st'" == "median" {
		 	local monly
			local detail "detail"
                        local s "p50"
                }
                else if "`st'" == "p75" | "`st'" == "p90" {
			local monly
			local detail "detail"
                        local s "`st'"
                }
                else if "`st'" == "p95" | "`st'" == "p99" {
			local monly
			local detail "detail"
                        local s "`st'"
                }
                else if "`st'" == "skewness" | "`st'" == "skew" {
			local monly
			local detail "detail"
                        local s "skewness"
                }
                else if "`st'" == "kurtosis" | "`st'" == "kurt" {
			local monly
			local detail "detail"
                        local s "kurtosis"
                }
                else if "`st'" == "se" | "`st'" == "SE" | "`st'" == "semean" {
			local monly
			local detail "detail"
                        local s "se"
                }
		else if "`st'" == "iqr" | "`st'" == "IQR" {
			local monly
			local detail "detail"
			local s "iqr"
		}
		else if "`st'" == "zero" | "`st'" == "0" {
			local s "0"
		}
		else if "`st'" == "extra" | "`st'" == "." {
			if "`c(version)'" == "" | _caller() <= 7 {
				noi di as txt "stat(extra) not allowed in Stata 7"
				local s
				local st
			}
			else {
				local s "extra"
				local st "extra"
			}
		}
                else {
                        di in r "stat() option invalid"
                        exit 198
                }

                local cnames "`cnames' `st'"
		local stats "`stats' `s'"
        }

	* if by(), separate variables
	qui if "`by'" != "" {
		tempname sep
		separate `varlist' if `touse', by(`by') gen(`sep') `missing'
		local origvar "`varlist'"
		unab varlist : `sep'*
		local nvars : word count `varlist'
	}

	* set up matrix
        if "`matrix'" == "" {
                local header "noheader"
                tempname matrix
        }
	local nstats : word count `stats'
	mat `matrix' = J(`nvars',`nstats',0)

	* calculation loop and fill in matrix
	tempname result
	local i = 1
        foreach v of local varlist {
                qui su `v' if `touse' [`weight' `exp'], `detail' `monly'
		local j = 1
                foreach s of local stats {
			if "`s'" == "0" {
				scalar `result' = 0
			}
			else if "`s'" == "extra" {
				scalar `result' = .
			}
                        else if "`s'" == "se" {
                                scalar `result' = r(sd) / sqrt(r(N))
                        }
			else if "`s'" == "iqr" {
				scalar `result' = r(p75) - r(p25)
			}
			else if "`s'" == "range" {
				scalar `result' = r(max) - r(min)
			}
                        else scalar `result' = r(`s')

`applymagic'	mat `matrix'[`i',`j'] = cond(`result' < ., `result', `magic')
`nomagic'       mat `matrix'[`i',`j'] = `result'

			local j = `j' + 1
                }
		local i = `i' + 1
        }

	* matrix row and column names
	if "`by'" == "" {
		mat rownames `matrix' = `varlist'
	}
	else {
		tempvar order
		gen long `order' = _n
		local vallbl : value label `by'
		local fmt: format `by'
		capture confirm string var `by'
		local isstr = _rc == 0

		foreach v of local varlist {
			su `order' if `v' < ., meanonly
			* numeric value or string value
			local val = `by'[`r(min)']

			* value label
			if "`vallbl'" != "" {
				local val : label `vallbl' `val'
				* first word only
				local val : word 1 of `val'
			}
			* apply format, but no leading or trailing spaces
			else {
				if `isstr' { local val : di `fmt' "`val'" }
				else local val : di `fmt' `val'
				local val = trim("`val'")
			}

			* missings or spaces or . in strings
			if "`val'" == "." | "`val'" == "" {
				local val "missing"
			}
			else if trim("`val'") == "" {
				local val "space(s)"
			}
			else local val : subinstr local val "." ",", all

			local rownames "`rownames' `val'"
		}
		mat rownames `matrix' = `rownames'
	}

        mat colnames `matrix' = `cnames'

	* output matrix
	if "`xpose'" == "xpose" { mat `matrix' = (`matrix')' }
        mat li `matrix', `options' `header'

end