*! version 1.0.0  30apr2008
program haplologit, eclass
	version 10.0
	if replay() {
		if "`e(cmd)'" != "haplologit" {
			error 301
		}
		if `e(rc)' != 0 {
			di as err "`e(cmd)' exited with error `e(rc)'; " ///
				  "replay is not allowed"
			error 301
		}
		local depvar `e(depvar)'
		Display `0'
		eret local depvar `"`depvar'"'
		exit
	}
	cap noi Estimate `0'
	local rc = _rc
	eret local cmdline `"haplologit `0'"'
	eret local cmd "haplologit"
	eret scalar rc = `rc'
	sret clear
	global MY_panel
	global MY_hap1
	global MY_hap2
	global MY_inhermodel
	global MY_riskhapmat
	global MY_rhapconsmat
	exit `rc'
end

program Estimate, eclass sortpreserve
	version 10.0
	syntax [varlist] [if] [in] ,			///
				SNPvars(varlist) [	/// Required
				noCONstant		/// Model opts
				INHERitance(string)	///
				RISKHAP(string asis) 	///
				RISKHAP1(string asis) 	///
				HFThreshold(string)	///
				CONSTraints(passthru) 	///
				COLlinear 		///
				Level(cilevel)		/// Reporting
				OR			///
				ALLDOTs 		///
				noCOEf noFREq noHEADer	///
				HAPPREFIX(string)	///
				EMITERate(passthru) 	/// EM opts
				EMTOLerance(passthru)	///
				EMSAMPle(string)	///
				eminit(string)		///
				EMLOG EMDOTs 		///
				noEMSHOW noEMTable	///
				DIFficult		/// ML opts
				ITERate(passthru)	///
				noLOG TRace		///
				GRADient HESSian	///
				showstep		///
				TOLerance(passthru)	///
				LTOLerance(passthru)	///
				GTOLerance(passthru)	///
				NRTOLerance(passthru)	///
				NONRTOLerance		///
				SHOWNRtolerance		///
				MLMethod(string) 	/// undocumented opts
				RARE			///
				PHASEINFO(passthru) 	///
				NRISKHAPlos(real 0) 	///
				TECHnique(passthru)	///
				SORT			///
				BASE10			///
				*			/// riskhap#() opts
					]
	if `"`happrefix'"' == "" {
		local happrefix "hap_"
	}
	if `"`rare'"' == "" {
		local rare rare
	}
	local maxopts `difficult' `iterate' `log' `trace' `gradient' `hessian'
	local maxopts `maxopts' `showstep' `tolerance' `ltolerance' `gtolerance'
	local maxopts `maxopts' `nrtolerance' `nonrtolerance' `shownrtolerance'
	local maxopts `maxopts' `technique'
	if `"`hfthreshold'"' != "" {
		cap confirm number `hfthreshold'
		if _rc {
			di as err "hfthreshold() must be between 0 and 1"
			exit 198
		}
		if `hfthreshold' <= 0 | `hfthreshold'>=1 {
			di as err "hfthreshold() must be between 0 and 1"
			exit 198
		}
		if `hfthreshold' < 1e-15 {
			di as err "hfthreshold(`hfthreshold') is too small"
			exit 498
		}
	}
	if "`emshow'" != "" {
		local emshow qui
	}
	if "`emtable'" != "" {
		local emtable nohaptable
	}
	if "`alldots'" != "" {
		if "`emlog'" != "" {
			di as err "alldots and emlog may not be combined"
			exit 198
		}
		local emdots emdots
	}
	marksample touse
	// main equation
	local ycons `constant'
	local constant
	if "`ycons'" == "" {
		local c = 1
	}
	else {
		local c = 0
		local note nonote
	}
	gettoken depvar xvars: varlist
	tempvar y
	qui gen byte `y' = (`depvar'!=0) if `touse'
	qui count if `depvar'==0
	if r(N) == 0 {
		di as txt "outcome does not vary; remember:"
		di as txt "                          0 = negative outcome,"
		di as txt "all other nonmissing values = positive outcome"
		exit 2000
	}
	if `"`xvars'"' == "" & "`ycons'" != "" {
		di as err "noconstant is not allowed without " ///
			  "independent variables"
		exit 198
	}
	_rmcoll `xvars' if `touse', `collinear' `ycons'
	local xvars `r(varlist)'
	local nxvars: word count `xvars'

	// check SNP vars to have values 0, 1, 2, or missing
	local nsnp: word count `snpvars'
	tokenize `snpvars'
	tempvar snpchk
	qui gen byte `snpchk' = .
	forvalues i = 1/`nsnp' {
		qui replace `snpchk'=(``i''==0)+(``i''==1)+(``i''==2)+(``i''==.)
		qui summ `snpchk', meanonly
		if r(min) == 0 {
			di as err `"snpvars(): variable ``i'' must "' ///
				   "contain values 0, 1, 2, and . only"
			exit 198
		}
	}
	drop `snpchk'
	// nriskhaplos()
	local sign = -1
	if `nriskhaplos' < 0 {
		local sign = 1
		local nriskhaplos = abs(`nriskhaplos')
	}
	if `sign' < 0 {
		local dihaptype most
	}
	else {
		local dihaptype less
	}

	local isriskhap `"`riskhap'`riskhap1'"'
	if `"`riskhap'"' != "" {
		if `"`riskhap1'"' != "" {
			di as err "riskhap() and riskhap1() may not be combined"
			exit 198
		}
		local options riskhap1(`riskhap') `options'
	}
	else if `"`riskhap1'"' != "" {
		local options riskhap1(`riskhap1') `options'
	}
	// parse riskhap#() options
	tempname K
	scalar `K' = 2^`nsnp'
	local nriskhap = 0
	// local options should contain only riskhap#() options
	if `"`options'"' != "" {
		if `nriskhaplos' > 0 {
			di as err "nriskhap() may not be specified " ///
				  "with risk haplotypes"
			exit 198
		}
		local 0 , `options'
		tokenize `"`options'"'
		local i = 1
		while `"``i''"' != "" {
			syntax [, RISKHAP`i'(string asis) * ]
			if `"`riskhap`i''"' != "" {
				local ++nriskhap
				gettoken riskhap`i' riskhap`i'vars: ///
						riskhap`i', parse(",") quotes
				// parse suboptions: interaction() noconstant
				local opts `options'
				local 0 `riskhap`i'vars'
				syntax [, INTERaction(varlist)	///
					  noCONstant 		///
					   *			]
				if `"`interaction'"'=="" & "`constant'"!="" {
				       di as err "riskhap`i'(): noconstant " ///
					  "is not allowed without interaction()"
					exit 198
				}
				markout `touse' `interaction'
				local riskhap`i'vars `interaction'
				local riskhap`i'cons `constant'
				local options `opts'
				cap confirm integer number `riskhap`i''
				qui if _rc { // riskhap#() contains bin. seq.
local 0 , riskhap`i'(`riskhap`i'') `options'
syntax [, RISKHAP`i'(string) * ]
local riskhap`i'str `riskhap`i''
mata: st_local("riskhap`i'",strofreal(frombase(2,st_local("riskhap`i'"))+1))
				}
				else {      // riskhap#() contains hap. index
cap assert `riskhap`i'' > 0
if _rc {
	di as err "riskhap`i'(): invalid haplotype index `riskhap`i''"
	exit 198
}
mata: st_local("riskhap`i'str",inbase(2,strtoreal(st_local("riskhap`i'"))-1))
				}
				// riskhap`i' stores ith ref. hap. index
				// riskhap`i'str stores ith riskhap as bin. seq.
				local len = strlen(`"`riskhap`i'str'"')
				if `len' > `nsnp' {
di as err "{p 0 12 4}riskhap`i'(): length of the risk haplotype "	///
   `""`riskhap`i'str'" may not exceed the number of variables in snpvars()"' ///
   " or haplotype index `riskhap`i'' may not exceed 2^N_snp = `=`K''{p_end}"
exit 198
				}
				if `"`riskhap`i'str'"' == "" {
di as err `"riskhap`i'() is incorrectly specified"'
exit 198
				}
				local riskhap`i'str ///
				  `: di _dup(`=`nsnp'-`len'') 0'`riskhap`i'str'
				if `riskhap`i'' == . & `"`isriskhap'"' != "" {
di as err `"{p 0 16 4}riskhap`i'(): risk haplotype "`riskhap`i'str'" "' ///
	  "is incorrectly specified{p_end}"
exit 198
				}
				local riskhaplist `riskhaplist' `riskhap`i''
				local duphap: list dups riskhaplist
				if `"`duphap'"' != "" {
di as err "riskhap`i'(): duplicate risk haplotype "	///
	  `""`riskhap`i'str'" (`riskhap`i'') is specified "'
exit 198
				}
			}
			local 0 , `options'
			local ++i
		} /* end of riskhap#() parsing */
		if `"`options'"' != "" {
			di as err `"option `options' is not allowed"'
			exit 198
		}
	}
	else { // the default is main effect of the most frequent haplotype
		if `nriskhaplos' == 0 {
			local nriskhaplos = 1
		}
		local nriskhap = 0
	}

	// parse inheritance()
	if `"`inheritance'"' == "" {
		local inheritance additive
	}
	else {
		local 0 , `inheritance'
		cap syntax [, Additive Dominant Recessive Codomimant]
		if _rc {
			di as err "inheritance(): `inheritance' is not allowed"
			exit 198
		}
		local inheritance `additive' `dominant' `recessive' `codominant'
		if `:word count `inheritance'' > 1 {
			di as err "inheritance(): only one model is allowed"
			exit 198
		}
		if `"`inheritance'"' == "codominant" {
			di as err "codominant model is not available"
			exit 198
		}

	}

	if `"`emsample'"' == "" {
		local emsample controls
		local emsampti control sample
		local ifopt ifopt(`y'==0)
	}
	else {
		local emsamplen = length(`"`emsample'"')
		if `emsamplen' < 2 {
			di as err "emsample() is incorrectly specified"
			exit 198
		}
		else {
			if `"`emsample'"'==substr("controls",1,`emsamplen') {
				local emsample controls
				local emsampti control sample
				local ifopt ifopt(`y'==0)
			}
			else if `"`emsample'"'==substr("cases",1,`emsamplen') {
				local emsample cases
				local emsampti case sample
				local ifopt ifopt(`y'==1)
			}
			else if `"`emsample'"'==substr("all",1,`emsamplen') {
				local emsample all
				local emsampti case-control sample
				local ifopt ifopt(`y'==0|`y'==1)
			}
			else {
				di as err "emsample() is incorrectly specified"
				exit 198
			}
		}
	}
	// estimate haplotype frequencies from the control sample
	// retain only haplotypes with sample freq. less than max(2/N, 0.001)
	qui summ if `touse', meanonly
	local N = r(N)
	if `"`hfthreshold'"' == "" {
		local cutoff = max(2/`N', 0.001)
		if `cutoff' >= 0.1 {
			local cutoff = 0.001
		}
		local hfthreshold = `cutoff'
	}
	else {
		local cutoff = `hfthreshold'
	}
	tempfile fhaplo
	tempname id indhap1 indhap2
	// expand snp-based genotype data to diplotype data (hap. pairs)
	_gntp2dptp `snpvars' if `touse', 				 ///
					usevar(`id' `indhap1' `indhap2') ///
					saving(`"`fhaplo'"')		 ///
					base10				 ///
					`ifopt'			 	 ///
					nohead				 ///
					`alldots' `phaseinfo'
	// obtain initial haplotype frequencies from emsample() (_ifcond)
	di
	di as txt "Obtaining initial haplotype frequency estimates"	///
		  " from the `emsampti':"
	local emopts `emtolerance' `emiterate' `base10' `emlog' `emdots'
	local emopts `emopts' `emtable' `sort' eminit(`eminit')
	`emshow' _haplo_em_phased `id' `indhap1' `indhap2' _ifcond	///
						using `"`fhaplo'"',   	///
						nsnp(`nsnp') 	  	///
						cutoff(`cutoff')  	///
						onlycutoff	  	///
						nohead		  	///
						`emopts'
	tempname em_ll em_N
	scalar `em_ll'  = e(ll)
	scalar `em_N'   = e(N_s)
	// redefine K to be the number of retained haplotypes
	tempname freqmat
	mat `freqmat' = e(freq)
	scalar `K' = colsof(`freqmat')
	if `K' == 1 {
		di as err "only one haplotype retained"
		exit 498
	}
	else if `K' < 1 {
		di as err "no haplotypes retained"
		exit 498
	}
	if `nriskhap' >= `K' {
		di "{p 0 6 4}{txt}Note: too many risk haplotypes are " ///
		   "specified; using {res}" `=`K'-1' "{txt} haplotype(s){p_end}"
		local nriskhap = `K'-1
	}
	di
	di as txt "Performing gradient-based optimization:"
	//di as txt "Note: using rare-disease approximation"
	tempfile fesamp
	// sid is used to save the estimation sample
	tempvar sid
	qui gen long `sid' = _n
	preserve
	qui keep if `touse'
	qui gen long `id' = _n if `touse'
	sort `id'
	tempvar merge
	// long form: consistent haplotype pairs are nested within subjects
	qui merge `id' using `"`fhaplo'"', uniqmaster _merge(`merge')
	sort `id'
	// macro ind contains retained haplotype indices
	mata: ///
st_local("ind",invtokens(substr(st_matrixcolstripe("e(freq)")[,2]',5,.)))
	local nlevels: word count `ind'
	// remove obs not compatible with retained haplotype pairs
	tempvar cnt subjdrop
	qui egen byte `cnt' = anycount(`indhap1' `indhap2'), values(`ind')
	// count how many subjects are removed
	qui by `id': gen long `subjdrop' = sum(`cnt'!=2)
	qui by `id': replace `subjdrop' = (`subjdrop'[_N]==_N & _n==_N)
	qui count if `subjdrop' & `touse'
	if r(N)!=0 {
		di as txt "{p 0 6 4}Note: removing " ///
		   as res `=string(r(N),"%9.0g")' ///
		   as txt " observation(s); constituent haplotype "	///
			  "frequencies are smaller than " ///
		   as res `"`=string(`hfthreshold',"%9.0g")'{p_end}"'
	}
	qui replace `touse' = 0 if `cnt'!=2
	drop `merge' `cnt' `subjdrop'
	qui keep if `touse'
	// save esample in a file
	qui outfile `sid' using `"`fesamp'"'
	// ifreqsort: matrix of hap. indices sorted from lowest to highest freq.
	tempname ifreqsort
	qui mata: ///
	st_matrix(st_local("ifreqsort"), sort((st_matrix("e(freq)")', st_matrix("e(index)")'),`sign')[.,2]')
	if `nriskhaplos' > 0 {
		if `nriskhaplos' >= `nlevels' {
			local nriskhaplos = `nlevels' - 1
			di as txt "{p 0 6 4}Note: nriskhap() must be less " ///
				  "than the number of retained haplotypes " ///
			   as res `nlevels' as txt "; using "		    ///
			   as res `nriskhaplos' as txt " `dihaptype' "	    ///
			   "frequent haplotypes from the `emsampti'{p_end}"
		}
		else if `nriskhaplos' == 1 {
			if `sign' < 0 {
di as txt "{p 0 6 4}Note: using the most frequent haplotype " ///
	  "from the `emsampti' as a risk haplotype{p_end}"
			}
			else {
di as txt "{p 0 6 4}Note: using the least frequent haplotype " ///
	  "from the `emsampti'{p_end}"
			}
		}
		else {
			di as txt "{p 0 6 4}Note: using "		///
			   as res `nriskhaplos' as txt " `dihaptype' "	///
			   "frequent haplotypes from the `emsampti'{p_end}"
		}
	}
	local concat X
	// map retained indices and risk haplotype indices
	// to a sequence of ordered integers
	// ind1, ind2 contain new indices
	tempvar ind1 ind2
	qui gen double `ind1' = `indhap1'
	qui gen double `ind2' = `indhap2'
	tokenize `ind'
	local nretained = 0
	local Nrhapvars = 0
	tempname riskhapmat rhapconsmat
	forvalues i=1/`nlevels' {
		if ``i'' != `i' {
			qui replace `ind1' = `i' if `ind1'==``i''
			qui replace `ind2' = `i' if `ind2'==``i''
		}
		if `nriskhaplos' > 0 { // if nriskhap() specified
			forvalues j=1/`nriskhaplos' {
if `=`ifreqsort'[1,`j']' == ``i'' {
	mat `riskhapmat' = (nullmat(`riskhapmat'), `i')
	local ++nretained
	// build haplotype ml and test equations
	mata: st_local("riskhap`nretained'str",inbase(2,strtoreal("``i''")-1))
	local len = `nsnp' - strlen(`"`riskhap`nretained'str'"')
	local riskhap`nretained'str `: di _dup(`len') 0'`riskhap`nretained'str'
	local eqriskhap `eqriskhap' (hap_`riskhap`nretained'str'`concat':)
	local testriskhap `testriskhap' ([hap_`riskhap`nretained'str'`concat']: _cons)
	mat `rhapconsmat' = (nullmat(`rhapconsmat'), 1)
	local ++Nrhapvars
}
			}
		}
		else { // if opts riskhap#() specified
			forvalues j=1/`nriskhap' {
if "`riskhap`j''" == "" {
	di as err "option riskhap`j'() is required with `nriskhap' " ///
		  "risk haplotype(s)"
	exit 198
}
if `riskhap`j'' == ``i'' {
	local riskhap`j' = `i'
	mat `riskhapmat' = (nullmat(`riskhapmat'), `riskhap`j'')
	local ++nretained
	// build haplotype ml and test equations
	local eqriskhap `eqriskhap' (hap_`riskhap`j'str'`concat': `riskhap`j'vars', ///
							`riskhap`j'cons')
	if "`riskhap`j'cons'" == "" {
		local testriskhap `testriskhap' ///
				([hap_`riskhap`j'str'`concat']: `riskhap`j'vars' _cons)
	}
	else {
		local testriskhap `testriskhap' ///
				([hap_`riskhap`j'str'`concat']: `riskhap`j'vars')
	}
	local riskhapxvars `riskhapxvars' `riskhap`j'vars'
	local Nrhapvars = `Nrhapvars' + `:word count `riskhap`j'vars''
	if "`riskhap`j'cons'" == "" {
		local ++Nrhapvars
		mat `rhapconsmat' = (nullmat(`rhapconsmat'), 1)
	}
	else {
		mat `rhapconsmat' = (nullmat(`rhapconsmat'), 0)
	}
				}
			}
		}
		// build equation names for frequency parameters and
		// nlcom expression
		if `i' != `nlevels' {
			local eqnu `eqnu' /nu`i'
			local nl_denom `nl_denom'`plus'exp([nu`i']_b[_cons])
			local plus +
		}
	}
	if `nretained' == 0 {
		di as txt  "{p 0 6 4}Note: " as res `nretained'	   ///
		   as txt  " out of " as res `nriskhap'			   ///
		   as txt  " specified risk haplotypes are retained"  ///
			   " in the computation; using the most frequent"  ///
		           " haplotype from the `emsampti'{p_end}"
		local riskhap1 = `:list posof "`=`ifreqsort'[1,1]'" in ind'
		mat `riskhapmat' = `riskhap1'
		mata: st_local("riskhap1str",inbase(2,`=`ifreqsort'[1,1]-1'))
		local len = `nsnp' - strlen(`"`riskhap1str'"')
		local riskhap1str `: di _dup(`len') 0'`riskhap1str'
		local nretained = 1
		local eqriskhap (hap_`riskhap1str'`concat': )
		local testriskhap ([hap_`riskhap1str'`concat']: _cons)
		mat `rhapconsmat' = 1
		local Nrhapvars = 1
	}
	else if `nretained'<`nriskhap' {
		di as txt  "{p 0 6 4}Note: " as res `nretained'	///
		   as txt  " out of " as res `nriskhap'			///
		   as txt  " specified risk haplotypes" 		///
			   " are used in the computation{p_end}"
	}

	if `"`rare'"' != "" { // algorithm for rare disease
		global MY_panel `id'
		global MY_hap1 `ind1'
		global MY_hap2 `ind2'
		global MY_inhermodel `inheritance'
		global MY_riskhapmat `riskhapmat'
		global MY_rhapconsmat `rhapconsmat'
		tempname mat initmat basefq hapfreq
		mat `hapfreq' = e(freq)
		// specify base haplotype frequency
		scalar `basefq' = `hapfreq'[1, `nlevels']
		if `basefq' < 1e-8 {
			di as err "base haplotype frequency is too small"
			exit 498
		}
		qui mata: ///
st_matrix(st_local("mat"),ln(st_matrix("e(freq)")/st_numscalar(st_local("basefq"))))
		sort `id' `ind1' `ind2'
		if `"`mlmethod'"' == "" {
			local mlmethod d1
		}
		local dtype = substr(`"`mlmethod'"',1,2)
		local noprofile = (`"`xvars'"'=="" & `"`riskhapxvars'"' == "")

		if `noprofile' {
// no covariates in the model
local note nonote
mat `initmat' = J(1,`Nrhapvars',0), `mat'[1,1..`=`nlevels'-1']
global MY_depvar `depvar'
ml model `mlmethod' _haplologit_d1_rare 		///
		`eqriskhap'				///
		`eqnu'					///
		if `touse'				///
		, init(`initmat', copy) maximize	///
		  search(off) `constraints' `collinear' ///
		  crittype(Retrospective log likelihood) ///
		  `maxopts'
		}
		else {
mat `initmat' = ///
	J(1,`=`nxvars'+`c'',0), J(1,`Nrhapvars',0), `mat'[1,1..`=`nlevels'-1']
ml model `mlmethod' _haplologit_`dtype'_rarex 		///
		(`depvar': `y' = `xvars', `ycons')	///
		`eqriskhap'				///
		`eqnu'					///
		if `touse'				///
		, init(`initmat', copy) maximize	///
		  search(off) `constraints' `collinear' ///
		  crittype(Retrosp. profile log likelihood) ///
		  `maxopts'
		}
		// combine haplotype equations with depvar equation
		// and order the estimation results
		tempname b V
		mata: _est_reorder()
		eret repost b = `b' V=`V', rename
		qui test [`depvar']
		local df_m = r(df)
		local chi2 = r(chi2)
		local chi2_p = r(p)
		eret scalar k_eq_nu = `nlevels'-1
		eret scalar k_eq = e(k_eq_nu) + 1
		eret local snpvars `"`snpvars'"'
		eret local inheritance `"`inheritance'"'
		eret local depvar `"`depvar'"'
		eret local genepop "Hardy-Weinberg equilibrium"
		eret local title "Haplotype-effects logistic regression"
		eret local emsample `emsample'
		eret local happrefix `happrefix'
		// clear predict
		eret local predict ""
		eret matrix em_freq = `freqmat'
		qui infile `sid' using `"`fesamp'"', clear
		qui by `sid', sort: keep if _n==1
		qui save `"`fesamp'"', replace
		restore
		tempvar merge
		qui merge `sid' using `"`fesamp'"', _merge(`merge') sort
		qui replace `touse' = 0 if `merge'!=3
		// compute number of phased, unphased, and missing genotypes
		tempvar miss one
		qui egen byte `miss' = rowmiss(`snpvars')
		qui egen byte `one' = anycount(`snpvars'), values(1)
		qui count if `miss'!=0 & `touse'
		local N_miss = r(N)
		qui count if `one'<=1 & `miss'==0 & `touse'
		local N_phased = r(N)
		qui count if `one'>1 & `miss'==0 & `touse'
		local N_unphased = r(N)
		qui count if `touse'
		local N_s = r(N)
		ereturn repost, esample(`touse')
		eret scalar N		= `N_s'
		eret scalar N_phased	= `N_phased'
		eret scalar N_unphased	= `N_unphased'
		eret scalar N_miss	= `N_miss'
		eret scalar ll		= `e(ll)'
		eret scalar converged	= `e(converged)'
		eret scalar df_m	= `df_m'
		eret scalar chi2 	= `chi2'
		eret scalar p 		= `chi2_p'

		// display results
		Display, ind(`ind') level(`level') `header' `coef' `freq' ///
			`or' `note'
		eret local depvar `"`depvar'"'
		// e-scalars (specific order)
		eret scalar N		= `N_s'
		eret scalar N_phased	= `N_phased'
		eret scalar N_unphased	= `N_unphased'
		eret scalar N_miss	= `N_miss'
		eret scalar ll		= `e(ll)'
		eret scalar converged	= `e(converged)'
		eret scalar df_m	= `df_m'
		eret scalar chi2 	= `chi2'
		eret scalar p 		= `chi2_p'
		eret scalar em_N = `em_N'
		eret scalar em_ll = `em_ll'
		eret scalar cutoff = `cutoff'
	}
	else {
		di as err "algorithm without rare disease assumption " ///
			  "is not available; specify option rare"
		exit
	}
end

program Display
	syntax [, ind(numlist) noHEADer noCOEf noFREq Level(cilevel) OR ///
		  noNote ]
	if "`header'" == "" {
		DiHeader
	}
	if "`coef'" == "" {
		local neq = e(k_eq) - e(k_eq_nu)
		if `neq' == 1 {
			local eq first
		}
		else {
			local eq neq(`neq')
		}
		di
		cap noi ml display, `eq' noheader level(`level') `or' nofoot
		if "`note'" == "" & "`or'" == "" {
di as txt "Note: _cons = b0 + ln(N1/N0) - ln{Pr(D=1)/Pr(D=0)}"
		}
	}
	if "`freq'" == "" {
		di
		ClearDepvar
		DiFreqTable, ind(`ind') level(`level')
	}
	ml footnote
end

program ClearDepvar, eclass
	eret local depvar
end

program DiHeader
	local crittype `e(crittype)'
	local piece2 `e(snpvars)'
	local piece1: piece 1 27 of `"`piece2'"', nobreak
	local piece2 = abbrev(subinstr(`"`piece2'"', `"`piece1'"', "", 1), 27)
	if missing(e(chi2)) {
		local dichi2 ///
		  "{help j_robustsingular##|_new:`e(chi2type)' chi2(`e(df_m)')}"
	}
	else {
		local dichi2 "{txt}`e(chi2type)' chi2({res}`e(df_m)'{txt})"
	}
	di
	di 	   `"{txt}`e(title)'"'
	di 	    "{txt}Mode of inheritance: {res}" `"`e(inheritance)'"' _c
	di _col(49) "{txt}Number of obs      = {res}" %9.0g e(N)
	di
	di	    "{txt}Genetic distribution: {res}" ///
			"`=subinstr("`e(genepop)'","rium",".",1)'" _c
	di _col(49) "{txt}Number phased      = {res}" %9.0g e(N_phased)
	di	    "{txt}Genotype: {res}" `"`piece1'"' _c
	di _col(49) "{txt}Number unphased    = {res}" %9.1g e(N_unphased)
	di	         "          {res}" `"`piece2'"' _c
	di _col(49) "{txt}Number missing     = {res}" %9.0g e(N_miss)
	di
	di _col(49) "`dichi2'" _col(68) "{txt}=" _col(70) as res %9.2f e(chi2)
        di          "{txt}`crittype' = {res}" %10.0g e(ll) _c
	di _col(49) "{txt}Prob > chi2        = {res}" ///
						%9.4f chiprob(e(df_m), e(chi2))
end

program DiFreqTable
	syntax [, ind(numlist) Level(cilevel) ]
	local happrefix `e(happrefix)'
	if `"`ind'"' == "" {
		mata: ///
st_local("ind",invtokens(substr(st_matrixcolstripe("e(em_freq)")[,2]',5,.)))
	}
	local nsnp: word count `e(snpvars)'
	mata: ///
st_local("indstr",invtokens(_inbase2fill(`nsnp',strtoreal(tokens(st_local("ind"))):-1)))
	// build nl expression
	local nlevels: word count `ind'
	local nlevels1 = `nlevels' - 1
	tokenize `indstr'
	forvalues i=1/`nlevels1' {
		local nldenom `nldenom'`plus'exp([nu`i']_b[_cons])
		local plus +
	}
	forvalues i=1/`nlevels1' {
		local nl_expr `nl_expr' ///
		      (`"`happrefix'``i''"':exp([nu`i']_b[_cons])/(1+`nldenom'))
	}
	local nl_expr `nl_expr' (`"`happrefix'``nlevels''"': 1/(1+`nldenom'))
	di as txt "Haplotype frequencies"
	nlcom `nl_expr', nohead level(`level')
end

version 10

mata:

/*
modifies haplotype equation names
	form			to
	hap_"str"X:_cons	status:-prefix-_"str"
	hap_"str"X:xvar		status:-prefix-_"str"*xvar

and puts estimation results in the following order
	xvars
	haplotype constants
	haplotype interactions
	constant
*/
void _est_reorder()
{
	string matrix colname_hap, colname_xb, colname_nu, colstripe
	real colvector sel, sel_dv, sel_hap, sel_cons, p, i
	real matrix b, V, w
	string scalar depvar
	real scalar nrows, nparms, indcons

	b 		= st_matrix("e(b)")
	V 		= st_matrix("e(V)")
	colstripe	= st_matrixcolstripe("e(b)")
	nrows 		= rows(colstripe)
	depvar		= st_local("depvar")

	sel_dv		= (substr(colstripe[.,1],1,strlen(depvar)):==depvar)
	sel_hap		= (substr(colstripe[.,1],1,4):=="hap_")
	sel_cons	= strmatch(colstripe[.,2], "_cons")
	sel		= (substr(colstripe[.,1],1,2):=="nu")
	// change stripe names
	colname_xb = select(colstripe, sel_dv)
	colname_hap = select(colstripe, sel_hap)
	colname_nu = select(colstripe, sel)
	colname_hap[.,2] = colname_hap[.,1]:+colname_hap[.,2]
	colname_hap[.,2] = subinstr(colname_hap[.,2],
				    st_local("concat")+"_cons", "", .)
	colname_hap[.,2] = subinstr(colname_hap[.,2],
				    st_local("concat"), "*", .)
	if (st_local("happrefix")!="hap_") {
		colname_hap[.,2] = subinstr(colname_hap[.,2],
				    	    "hap_", st_local("happrefix"), .)
	}
	colname_hap[.,1] = J(rows(colname_hap), 1, depvar)
	colstripe = (colname_xb\colname_hap\colname_nu)

	nparms = colsum(sel_dv)
	if (nparms != 0 ) { // change the order of estimation results
		if (nparms == 1) {
			if (st_local("xvars") == "") {
				indcons = 1
				p = J(0,0,.)
			}
			else {
				indcons = 0
				p = 1
			}
		}
		else {
			if (st_local("ycons") == "") {
				p = range(1, nparms-1, 1)
				indcons = nparms
			}
			else {
				p = range(1, nparms, 1)
				indcons = 0
			}
		}
		nparms = colsum(sel_hap)
		if (nparms != 0) {
// should always be true since always have haplo effects
			sel = sel_hap:*sel_cons
			if (colsum(sel) != 0) { // haplotype constants
				minindex(sel, -1, i, w)
				if (cols(p) == 0 ) p = i
				else p = p \ i
			}
			sel = sel_hap:*(1:-sel_cons)
			if (colsum(sel) != 0) { // haplotype interactions
				minindex(sel, -1, i, w)
				if (cols(p) == 0 ) p = i
				else p = p \ i
			}
		}
		if (indcons != 0 ) p = p \ indcons
		nparms = colsum(sel_nu)
		if (nparms != 0) { // should always be true
			p = p \ range(rows(p)+1, nrows, 1)
		}
		// reorder rows and columns
		colstripe = colstripe[p,.]
		b = b[.,p']
		V = V[.,p']
		V = V[p, .]
	}
	st_matrix(st_local("b"), b)
	st_matrix(st_local("V"), V)
	st_matrixcolstripe(st_local("b"), colstripe)
	st_matrixcolstripe(st_local("V"), colstripe)
	st_matrixrowstripe(st_local("V"), colstripe)
}

end