*! genqreg 1.0.0 14Nov2014
*! version 1.0.0
*! authors: David Powell, Travis Smith, Matthew Baker
*! MJB edited this file on 30Jul2015 to include perfect predictors
*!    for the proneness model.
*! version 1.0.1 February232016
*! TAS added some stuff for covariance matrices
*! MJB added in a scale term to scale the GMM objective for more control over MCMC

program genqreg
	version 11.2
	if replay() {
		if (`"`e(cmd)'"' != "genqreg") error 301
		Replay `0'
	}
	else Estimate `0'
end
program Estimate, eclass

	syntax varlist [fw aw/] [if] [in] , 	///
		[OPTIMIZE(string)					///
			DRAWs(int 1000)					///
			BURN(int 0)						///
			ARATE(real .234)                ///
			SAMPLER(string)					///
			DAMPPARM(real 1)				///
			FROM(string)					///
			FROMVariance(string)			///
			SAVING(string)					///
			REPLACE							///
			APPEND							///
			THIN(int 1)						///
			JUMBLE							///
			NOISY							///
			grid1(numlist)					///
			grid2(numlist)					///
			NMSIMplex(string)				///
			USEMAX							///
			SCALE(real 1)                   ///
		TECHnique(string) 					///
		PRONEness(varlist)    				///
		INSTRuments(varlist)         		///
		Quantile(real 0.5)]

	marksample touse
	gettoken lhs rhs : varlist
	markout `touse' `lhs' `rhs'

	if `quantile' >= 1 {
			local tau = `quantile'/100
		}
		else	local tau "`quantile'"
		if `tau' <= 0 | `tau' >= 1 {
			di in red "quantiles(`quantile') out of range"
			exit 198
		}

	// technique
	if "`technique'"=="logit" {
		local technique "logit"
		mata: f=&LogitObjective()
	}
	else if "`technique'"=="probit" {
		local technique "probit"
		mata: f=&ProbitObjective()
	}
	else {
		local technique "linear"
		mata: f=&pi()
	}
	// The last bit of code just passes an arbitrary function, because none is needed

	// col of ones
	tempvar ones
	qui gen `ones'=1 if `touse'

	// get weights, else use wt=1
	tempvar weightvar
	if `"`exp'"' != "" {
		qui gen double `weightvar' = `exp' if `touse'
	}
	else	{
		qui gen `weightvar' = `ones' if `touse'
	}

	// If proneness is blank then standard QR where x=1
	if ("`proneness'"!="") {
		mata: xi=st_data(.,"`proneness'","`touse'")
	}
	else {
		mata: xi=st_data(.,"`ones'","`touse'")
	}

	// Errors in choosing options
	if ("`optimize'" !="mcmc" & "`optimize'" !="grid") {
		local optimize "NM"
	}
	if ("`optimize'" !="mcmc") {
		if (`burn'!=0 | `draws'!=1000 | `arate'!=.234 | `dampparm'!=1 ///
		  | `thin'!=1 | "`sampler'"!=""  ///
		  | "`saving'"!="" | "`replace'"=="replace" | "`append'"=="append" ///
		  | "`jumble'"=="jumble" | "`noisy'"=="noisy") {
			disp as err "An mcmc option has been set but estimation method is not mcmc. Option is superfluous."
			disp as err "Please consult the help file for information on how to use mcmc options."
			disp as txt " "
		}
	}
	if ("`optimize'" !="grid") {
		if ("`grid1'"!="" | "`grid2'"!="") {
			disp as err "A grid search option has been set but estimation method is not grid search. Option is superfluous."
			disp as err "Please consult the help file for information on how to use grid search options."
			disp as txt " "
		}
	}

	// Checking instruments and rank condition
	if ("`instruments'" == "") {
		local instruments  "`rhs'"
	}

	if wordcount("`instruments'") < wordcount("`rhs'") {
		di as error "Error: # of instruments must be >= # of right-hand side variables."
		exit
	}

	tempname excluded_inst exog_vars endog_vars
	local excluded_inst : list instruments - rhs
	local exog_vars : list instruments - excluded_inst
	local endog_vars : list rhs - instruments

	// for variance calculation of QR and IVQR when proneness==""
	mata: st_view(excluded_inst=.,.,"`excluded_inst'","`touse'")
	mata: st_view(exog_vars=.,.,"`exog_vars'","`touse'")
	mata: st_view(endog_vars=.,.,"`endog_vars'","`touse'")

	/* Set starting values */
	if ("`from'" == "" &  "`fromvariance'"=="") {
		qui qreg `lhs' `rhs' if `touse' [aw= `weightvar'], q(`tau')
		mata: beta_start=st_matrix("e(b)")
		mata: beta_start=beta_start[1..cols(beta_start)-1]
		mata: beta_start_var=st_matrix("e(V)")
		mata: beta_start_var=beta_start_var[1..(cols(beta_start)),1..(cols(beta_start))]
		mata: beta_start_se=sqrt(diagonal(beta_start_var))'
	}
	else if ("`from'" == "" & "`fromvariance'"!="" ) {
		mata: beta_start_var=st_matrix("`fromvariance'")
		mata: beta_start_se=sqrt(diagonal(beta_start_var))'
		qui qreg `lhs' `rhs' if `touse' [aw= `weightvar'], q(`tau')
		mata: beta_start=st_matrix("e(b)")
		mata: beta_start=beta_start[1..cols(beta_start)-1]
	}
	else if ("`from'" != "" & "`fromvariance'"=="" ) {
		mata: beta_start=st_matrix("`from'")
		qui qreg `lhs' `rhs' if `touse' [aw= `weightvar'], q(`tau')
		mata: beta_start_var=st_matrix("e(V)")
		mata: beta_start_var=beta_start_var[1..(cols(beta_start)),1..(cols(beta_start))]
		mata: beta_start_se=sqrt(diagonal(beta_start_var))'
	}
	else {
		mata: beta_start=st_matrix("`from'")
		mata: beta_start_var=st_matrix("`fromvariance'")
		mata: beta_start_se=sqrt(diagonal(beta_start_var))'
	}


	/* Read in the options and desired quantile */
	mata: technique   =st_local("technique")
	mata: tau     =strtoreal(st_local("tau"))
	mata: scale   =strtoreal(st_local("scale"))             /* Added in so the MCMC estimates won't be so nuts if user so desires! */

	mata: info=st_data(.,"`ones'","`touse'")
	mata: fix=st_data(.,"`ones'","`touse'")
	mata: st_view(di=.,.,"`rhs'","`touse'")
	mata: st_view(zi=.,.,"`instruments'","`touse'")
	mata: st_view(yi=.,.,"`lhs'","`touse'")
	mata: st_view(wt=.,.,"`weightvar'","`touse'")

	//normalize weights to sum to N
	mata: wt = wt:/colsum(wt)*rows(info)

	mata: A=weightMatrix(beta_start,tau,yi,di,xi,technique,f,info,fix,zi,wt,"efficient")


	mata: M=moptimize_init()
	mata: moptimize_init_evaluator(M,&objectiveFunGmm())
	mata: moptimize_init_touse(M,"`touse'")
	mata: moptimize_init_evaluatortype(M,"d0")
	mata: moptimize_init_technique(M,"nm")
	if "`nmsimplex'"=="" {
		mata: moptimize_init_nmsimplexdeltas(M,J(1,1,10))
	}
	else {
		mata: simplex=st_matrix("`nmsimplex'")
		mata: moptimize_init_nmsimplexdeltas(M,simplex)
	}
	mata: moptimize_init_depvar(M,1,yi)
	mata: moptimize_init_eq_indepvars(M,1,di)
	mata: moptimize_init_eq_cons(M,1, "off")
	mata: moptimize_init_eq_coefs(M,1, beta_start)

	mata: G=grqInfoInit(yi,xi,di,A,technique,info,fix,zi,wt,tau,f,scale)
	mata: moptimize_init_userinfo(M,1,G)

/* MCMC optimization */
	if "`optimize'"=="mcmc" {
		di "Adaptive MCMC optimization"

		mata: junk=moptimize_evaluate(M)

		if (`burn'>=`draws') {
			di as error "Error: The # of burn in draws must be less than # draws"
			exit
		}

		if "`sampler'"!="mwg" {
			local sampler "global"
		}

		mata: noisy   =st_local("noisy")
		mata: sampler =st_local("sampler")
		mata: arate   =strtoreal(st_local("arate"))
		mata: alginfo ="standalone",st_local("sampler")
		mata: draws   =strtoreal(st_local("draws"))
		mata: dampparm=strtoreal(st_local("dampparm"))
		mata: burn    =strtoreal(st_local("burn"))
		mata: thin    =strtoreal(st_local("thin"))
		mata: jumble  =st_local("jumble")

		/* set up and execute the amcmc run */
		mata: alginfo=sampler,"gf0","moptimize"
		mata: Draws = amcmc(alginfo,&objectiveFunGmm(),beta_start, beta_start_var,draws,burn,dampparm,arate,ar=.,vals=.,lam=.,.,M,noisy)

		/* process all of the information that does not need further */
		/* manipulation */

		tempname arates arates_mean arates_min arates_max
		mata: st_matrix("`arates'",ar)
		mata: st_numscalar("`arates_mean'",mean(ar'))
		mata: st_numscalar("`arates_min'",min(ar))
		mata: st_numscalar("`arates_max'",max(ar))

		/* A function to process draws after they are done */
		mata: gqreg_process_draws(Draws,vals,jumble,thin,0,betaf=.,valsf=.)

		tempname dof
		mata: st_numscalar("`dof'",rows(betaf))
		local dof=`dof'

		tempname b V gamma valMean valMax valMin drawsKept

		* find betas corresponding to max objfunval
		if "`usemax'"!="" {
			mata: b=findRowMax(valsf,betaf)[1,.]
			mata: GAMMA=mm_quantile(yi:-di*b',wt,tau); GAMMA=GAMMA'

			//
			// Covariance
			//

			// GQR covariance
			if ("`proneness'"!="") {
				mata: deriv= beta_start_se
				mata: V=gqr_var(yi,di,xi,b,tau,zi,wt,deriv,technique,f,fix,info)

				// Output without constant
				tempname b V

				mata: st_matrix("`b'",b)
				mata: st_matrix("`V'",V)

				mat colnames `b' = `rhs'
				mat colnames `V' = `rhs'
				mat rownames `V' = `rhs'
			}
			// QR and IVQR covariance
			else {
				//  QR covariance
				if ("`excluded_inst'"=="") {
					mata: b=b, GAMMA
					mata: V=kcov_qr(b,tau,yi,di,wt)
				}

				// IVQR Just-id covariance
				else if wordcount("`excluded_inst'") == wordcount("`endog_vars'") {
					mata: b=b, GAMMA
					mata: V=kcov_iqr(b,tau,yi,endog_vars,exog_vars,excluded_inst,wt)
				}
				// Over-id covariance
				else {
					mata: b=b, GAMMA
					mata: V=kcov_iqr_oid(b,tau,yi,endog_vars,exog_vars,excluded_inst,wt)
				}

				// Output with constant
				tempname b V

				mata: st_matrix("`b'",b)
				mata: st_matrix("`V'",V)

				mat colnames `b' = `rhs' _cons
				mat colnames `V' = `rhs' _cons
				mat rownames `V' = `rhs' _cons
			}


		}
		*...or use mean of draws
		else {
			// Get the constant for each MCMC draw
			mata: GAMMA=mm_quantile(yi:-di*betaf',wt,tau); GAMMA=GAMMA'
			mata: b=mean((betaf, GAMMA))
			mata: V=variance((betaf, GAMMA))

			// Output with constant
			tempname b V

			mata: st_matrix("`b'",b)
			mata: st_matrix("`V'",V)

			mat colnames `b' = `rhs' _cons
			mat colnames `V' = `rhs' _cons
			mat rownames `V' = `rhs' _cons
		}

		mata: st_numscalar("`valMean'",mean(valsf))
		mata: st_numscalar("`valMax'",max(valsf))
		mata: st_numscalar("`valMin'",min(valsf))
		mata: st_numscalar("`drawsKept'",rows(valsf))


		tempname beta_start beta_start_var
		mata: st_matrix("`beta_start'",beta_start)
		mata: st_matrix("`beta_start_var'",beta_start_var)

		tempname nobs
		quietly tab `touse' if `touse'
		local nobs=`r(N)'

		ereturn clear
		ereturn post `b' `V', esample(`touse') obs(`nobs') dof(`dof')

		ereturn local title "mcmc-estimated Generalized Quantile Regression"
		ereturn local cmd "genqreg"
		ereturn local indepvars `rhs'
		ereturn local depvar `lhs'
		ereturn local technique `technique'
		ereturn matrix b_init=`beta_start'
		ereturn matrix V_init=`beta_start_var'



	/* Return if MCMC */
		ereturn local sampler `sampler'
		ereturn local saving `saving'
		ereturn local jumble `jumble'

		ereturn scalar draws=`draws'
		ereturn scalar burn=`burn'
		ereturn scalar thin=`thin'
		ereturn scalar damper=`dampparm'

		ereturn scalar opt_arate=`arate'
		ereturn scalar arates_mean=`arates_mean'
		ereturn scalar f_mean=`valMean'
		ereturn scalar f_max =`valMax'
		ereturn scalar f_min =`valMin'
		ereturn scalar scale = `scale'

		ereturn scalar draws_retained = `drawsKept'
		ereturn matrix arates=`arates'

			if "`saving'"!="" {
			preserve
			clear

			local exnames "`rhs'"
			getmata (`exnames')=betaf
			getmata (fun_val)=valsf

			gen t=_n
			if "`append'"=="append" {
				append using "`saving'"

			}
			else {
				qui save "`saving'", `replace'
			}
			restore
		}

		di _newline
		di as txt "`e(title)'"
		di as txt "     Observations:         " as res %10.0f `e(N)'
		di as txt "     Mean acceptance rate: " as res %10.3f `e(arates_mean)'
		di as txt "     Total draws:          " as res %10.0f `e(draws)'
		di as txt "     Burn-in draws:        " as res %10.0f `e(burn)'
		di as txt "     Draws retained:       " as res %10.0f `e(draws_retained)'
		if `e(thin)'!=1		{
		di as txt	_col(10) "*One of every " %1.0f `e(thin)' " draws kept"
					}
		if "`e(jumble)'"=="jumble" {
		di as txt			   _col(10) "*Draws Jumbled"
					}

		ereturn display
		if ("`excluded_inst'"=="" & "`proneness'"=="") {
			disp as text "No excluded instruments and no proneness variables."
			disp as text "--> Estimation is equivalent to standard quantile regression."
		}
		if ("`excluded_inst'"!="") {
			disp as text "Excluded instruments: " "`excluded_inst'"
		}
		if ("`proneness'"!="") {
			disp as text "Proneness variables: " "`proneness'"
		}

		di as txt " Value of objective function:   "
		di as txt "             Mean:         " as res %12.2f `e(f_mean)'
		di as txt "             Min:          " as res %12.2f `e(f_min)'
		di as txt "             Max:          " as res %12.2f `e(f_max)'
		if "`e(saving)'"!="" {
		di as txt "   Draws saved in: " "`e(saving)'"
							}
		di _newline
		di as txt "    *Results are presented to conform with Stata convention, but "
		di as txt "     are summary statistics of draws, not coefficient estimates. "
		if `scale' != 1 {
			di as txt ""
			di as txt "    *Objective function rescaled by a factor of: " %12.2f `scale'
		}
	}

/* grid search */
	else if "`optimize'"=="grid" {
		di "Grid-search optimization"

		// if rhs > 2 then exit
		if wordcount("`rhs'") > 2 {
			di as error "Error: The # of RHS variables is > 2. Grid search not feasible."
			exit
		}

		// if 2 RHS, search over 2x2 grid
		if wordcount("`rhs'") == 2 {

			if "`grid1'"!="" {
				// passes the numlist as a vector to mata
				local grid1: subinstr local grid1 " " ", ", all
				mata: grid1=(`grid1')'
			}
			// default grid values: 95% CI in each direction over 81 points
			else {
				mata: min1=beta_start[1,1]-1.96*beta_start_se[1,1]
				mata: max1=beta_start[1,1]+1.96*beta_start_se[1,1]
				mata: intvl1=(min1-max1)/8
				mata: grid1=range(min1,max1,intvl1)
				mata: st_numscalar("min1",min1)
				mata: st_numscalar("max1",max1)
				mata: st_numscalar("intvl1",intvl1)
				local grid1 "`=min1'(`=intvl1')`=max1'"
				numlist "`grid1'"
			}

			if "`grid2'"!="" {
				local grid2: subinstr local grid2 " " ", ", all
				mata: grid2=(`grid2')'
			}
			else {
				mata: min2=beta_start[1,2]-1.96*beta_start_se[1,2]
				mata: max2=beta_start[1,2]+1.96*beta_start_se[1,2]
				mata: intvl2=(min2-max2)/8
				mata: grid2=range(min2,max2,intvl2)
				mata: st_numscalar("min2",min2)
				mata: st_numscalar("max2",max2)
				mata: st_numscalar("intvl2",intvl2)
				local grid2 "`=min2'(`=intvl2')`=max2'"
				numlist "`grid2'"
			}

			// set up grid
			mata: expand_grid1=J(rows(grid2),1,grid1)
			mata: expand_grid2=vec(J(rows(grid1),1,grid2'))
			mata: grid12=(expand_grid1, expand_grid2)
			mata: st_numscalar("grid_points",rows(grid12))
			di as txt "Number of grid points: " `=grid_points'
			// column for obj func value
			mata: grid12=grid12,J(rows(grid12),1,.)
			mata: grid_index=0

			local iter=0
			qui foreach num2 of numlist `grid2' {
				qui foreach num of numlist `grid1' {
					if "`noisy'"!="" {
						local iter=`iter'+1
						noisily di as txt "." _c
						if mod(`iter',50)==0 {
							noisily di as txt "`iter'"
						}
					}
					mata: grid_index=grid_index+1
					mata: b_grid=grid12[grid_index,1..2]
					mata: g=momentEvaluator(grid12[grid_index,1..2],tau,yi,di,xi,technique,f,info,fix,zi,wt)
					mata: g=colsum(g)/sqrt(rows(info))
					mata: grid12[grid_index,3]=-.5*g*A*g'
				}
			}
			// find the maximum
			// findRowMax(X1,X2) where X1 is a colVec to search for max
			//							X2 is the corresponding matrix of betas
			mata: grid_max=findRowMax(grid12[.,3],grid12)
			// take the first instance of beta if there are ties
			//mata: b=grid_max[1,1..2]
			// midpoint of grid, find unique values first
			mata: b1=(min(uniqrows(grid_max[.,1]))+max(uniqrows(grid_max[.,1])))/2
			mata: b2=(min(uniqrows(grid_max[.,2]))+max(uniqrows(grid_max[.,2])))/2
			mata: b=b1,b2
			mata: fval=grid_max[1,3]
		}

		if wordcount("`rhs'") == 1 {
			if "`grid1'"!="" {
				// passes the numlist as a vector to mata
				local grid1: subinstr local grid1 " " ", ", all
				mata: grid1=(`grid1')'
			}
			// default grid values: 95% CI over 81 points
			else {
				mata: min1=beta_start[1,1]-1.96*beta_start_se[1,1]
				mata: max1=beta_start[1,1]+1.96*beta_start_se[1,1]
				mata: intvl1=(min1-max1)/80
				mata: grid1=range(min1,max1,intvl1)
				mata: st_numscalar("min1",min1)
				mata: st_numscalar("max1",max1)
				mata: st_numscalar("intvl1",intvl1)
				local grid1 "`=min1'(`=intvl1')`=max1'"
				numlist "`grid1'"
			}

			mata: st_numscalar("grid_points",rows(grid1))
			di as txt "Number of grid points: " `=grid_points'
			// column for obj func value
			mata: grid1=grid1,J(rows(grid1),1,.)
			mata: grid_index=0

			local iter=0
			qui foreach num of numlist `grid1' {
				if "`noisy'"!="" {
					local iter=`iter'+1
					noisily di as txt "." _c
					if mod(`iter',50)==0 {
						noisily di as txt "`iter'"
					}
				}
				mata: grid_index=grid_index+1
				mata: b_grid=grid1[grid_index,1]
				mata: g=momentEvaluator(grid1[grid_index,1],tau,yi,di,xi,technique,f,info,fix,zi,wt)
				mata: g=colsum(g)/sqrt(rows(info))
				mata: grid1[grid_index,2]=-.5*g*A*g'
			}
			// find the maximum
			mata: grid_max=findRowMax(grid1[.,2],grid1)
			// take the first instance of beta if there are ties
			// mata: b=grid_max[1,1]
			// midpoint
			mata: b=(min(uniqrows(grid_max[.,1]))+max(uniqrows(grid_max[.,1])))/2
			mata: fval=grid_max[1,2]
		}

		tempname grid_betas
		mata: st_matrix("`grid_betas'",grid_max)
		mat colnames `grid_betas' = `rhs' obj_fun_value

		// Get the constant
		mata: GAMMA=mm_quantile(yi:-di*b',wt,tau); GAMMA=GAMMA'

		//
		// Covariance
		//

		// GQR covariance
		if ("`proneness'"!="") {
			mata: deriv= beta_start_se
			mata: V=gqr_var(yi,di,xi,b,tau,zi,wt,deriv,technique,f,fix,info)

			// Output without constant
			tempname b V

			mata: st_matrix("`b'",b)
			mata: st_matrix("`V'",V)

			mat colnames `b' = `rhs'
			mat colnames `V' = `rhs'
			mat rownames `V' = `rhs'
		}
		// QR and IVQR covariance
		else {
			//  QR covariance
			if ("`excluded_inst'"=="") {
				mata: b=b, GAMMA
				mata: V=kcov_qr(b,tau,yi,di,wt)
			}

			// IVQR Just-id covariance
			else if wordcount("`excluded_inst'") == wordcount("`endog_vars'") {
				mata: b=b, GAMMA
				mata: V=kcov_iqr(b,tau,yi,endog_vars,exog_vars,excluded_inst,wt)
			}
			// Over-id covariance
			else {
				mata: b=b, GAMMA
				mata: V=kcov_iqr_oid(b,tau,yi,endog_vars,exog_vars,excluded_inst,wt)
			}

			// Output with constant
			tempname b V

			mata: st_matrix("`b'",b)
			mata: st_matrix("`V'",V)

			mat colnames `b' = `rhs' _cons
			mat colnames `V' = `rhs' _cons
			mat rownames `V' = `rhs' _cons
		}

		tempname nobs
		quietly tab `touse' if `touse'
		local nobs=`r(N)'

		ereturn clear
		ereturn post `b' `V', esample(`touse') obs(`nobs')

/*				ereturn post `b' , esample(`touse') obs(`nobs')
		disp ""
		disp as err "Grid search: bootstrap to obtain standard errors"	*/

		ereturn local title "Generalized Quantile Regression (GQR)"
		ereturn local cmd "genqreg"
		ereturn local indepvars `rhs'
		ereturn local depvar `lhs'
		ereturn local technique `technique'
		ereturn matrix solutions `grid_betas'

		display as txt " "
		display as txt "`e(title)'"
		display as txt "     Observations:         " as res %10.0f `e(N)'
		display as txt " "

		ereturn display

		if ("`excluded_inst'"=="" & "`proneness'"=="") {
			disp as text "No excluded instruments and no proneness variables."
			disp as text "--> Estimation is equivalent to standard quantile regression."
		}
		if ("`excluded_inst'"!="") {
			disp as text "Excluded intstruments: " "`excluded_inst'"
		}
		if ("`proneness'"!="") {
			disp as text "Proneness variables: " "`proneness'"
		}

		if rowsof(e(solutions)) > 1 {
			di as txt "    Note: Alternative solutions exist. See e(solutions)."
		}
	}

/* Nelder-Mead */
	else {
		di "Nelder-Mead optimization"
		mata: moptimize(M)
		mata: b=moptimize_result_coefs(M)

		// Get the constant
		mata: GAMMA=mm_quantile(yi:-di*b',wt,tau); GAMMA=GAMMA'

		// standard QR cov-var
		if ("`excluded_inst'"=="" & "`proneness'"=="") {
			mata: b=b, GAMMA
			mata: V=kcov_qr(b,tau,yi,di,wt)

			tempname b V

			mata: st_matrix("`b'",b)
			mata: st_matrix("`V'",V)

			mat colnames `b' = `rhs' _cons
			mat colnames `V' = `rhs' _cons
			mat rownames `V' = `rhs' _cons
		}
		// standard IVQR cov-var
		else if ("`proneness'"=="") {
			if wordcount("`excluded_inst'") == wordcount("`endog_vars'") {
				mata: b=b, GAMMA
				mata: V=kcov_iqr(b,tau,yi,endog_vars,exog_vars,excluded_inst,wt)
				tempname b V

				mata: st_matrix("`b'",b)
				mata: st_matrix("`V'",V)

				mat colnames `b' = `rhs' _cons
				mat colnames `V' = `rhs' _cons
				mat rownames `V' = `rhs' _cons
			}
			else {
				mata: b=b, GAMMA
				mata: V=kcov_iqr_oid(b,tau,yi,endog_vars,exog_vars,excluded_inst,wt)
				tempname b V

				mata: st_matrix("`b'",b)
				mata: st_matrix("`V'",V)

				mat colnames `b' = `rhs' _cons
				mat colnames `V' = `rhs' _cons
				mat rownames `V' = `rhs' _cons
			}
		}
		// GQR covariance
		else {
			mata: deriv=beta_start_se
			mata: V=gqr_var(yi,di,xi,b,tau,zi,wt,deriv,technique,f,fix,info)
			tempname b V

			mata: st_matrix("`b'",b)
			mata: st_matrix("`V'",V)

			mat colnames `b' = `rhs'
			mat colnames `V' = `rhs'
			mat rownames `V' = `rhs'
		}


		tempname nobs
		quietly tab `touse' if `touse'
		local nobs=`r(N)'

		ereturn clear
		ereturn post `b' `V', esample(`touse') obs(`nobs')

		ereturn local title "Generalized Quantile Regression (GQR)"
		ereturn local cmd "genqreg"
		ereturn local indepvars `rhs'
		ereturn local depvar `lhs'
		ereturn local technique `technique'

		display as txt " "
		display as txt "`e(title)'"
		display as txt "     Observations:        " as res %10.0f `e(N)'
		display as txt " "
		ereturn display
		if ("`excluded_inst'"=="" & "`proneness'"=="") {
			disp as text "No excluded instruments and no proneness variables."
			disp as text "--> Estimation is equivalent to standard quantile regression."
		}
		if ("`excluded_inst'"!="") {
			disp as text "Excluded intstruments: " "`excluded_inst'"
		}
		if ("`proneness'"!="") {
			disp as text "Proneness variables: " "`proneness'"
		}

	}
end


program Replay
	syntax
	ereturn display
end

mata:
struct gqrInfo {
	real matrix yi,xi,di,A,info,fix,zi,wt
	real scalar tau, scale
	string scalar technique
	pointer (real scalar function) scalar fun
}

struct gqrInfo grqInfoInit(yi,xi,di,A,technique,info,fix,zi,wt,tau,fun,scale)
{
	struct gqrInfo scalar G
	G.yi=yi
	G.xi=xi
	G.di=di
	G.A=A
	G.technique=technique
	G.info=info
	G.fix=fix
	G.zi=zi
	G.wt=wt
	G.tau=tau
	G.fun=fun
	G.scale=scale
	return(G)
}

void objectiveFunGmm(M,todo,b,obj,g,H)
{
	real scalar tau
	real matrix yi,xi,di,info,fix,zi,wt,A
	string scalar technique
	pointer(real scalar function) scalar fun
	struct gqrInfo scalar G

	real matrix gi

	yi=moptimize_util_depvar(M,1)

	G=moptimize_util_userinfo(M,1)

	gi=momentEvaluator(b,G.tau,yi,G.di,G.xi,G.technique,G.fun,G.info,G.fix,G.zi,G.wt)

	gi=colsum(gi)/sqrt(rows(G.info))

	obj =-.5*G.scale*(1/(G.tau-G.tau^2))*gi*G.A*gi'
}

real matrix momentEvaluator(real matrix b,
							real scalar tau,
							real matrix yi,
							real matrix di,
							real matrix xi,
							string scalar technique,
							pointer(real scalar function) scalar f,
							real matrix info,
							real matrix fix,
							real matrix zi,
							real matrix wt)
{
	real colvector gamma, tauXi, yip, gammap, wtp
	real matrix gi, zi1, dip, zip, zim, delHat, below
	real scalar i

	// computes gamma for both cross-sectional GQR and QRPD depending on technique
	gamma=estimateGamma(b,yi,di,wt,tau,technique,fix)

	// GQR
	if (technique=="logit" | technique=="probit" | technique=="linear") {
		delHat=estimateProneness(yi,gamma,di,b,xi,technique,f,wt,flagMat=.)
		if (technique=="logit") {
			tauXi =exp((xi,J(rows(xi),1,1))*delHat'):/(1:+exp((xi,J(rows(xi),1,1))*delHat'))
		}
		else if (technique=="probit") {
			tauXi =normal((xi,J(rows(xi),1,1))*delHat')
		}
		else {
			tauXi=(xi,J(rows(xi),1,1))*delHat
		}
		zeroPreds=mm_which(rowsum(flagMat:==-1):>0)
		onePreds=mm_which(rowsum(flagMat:==1):>0)
		if (rows(zeroPreds)>0) tauXi[zeroPreds]=J(rows(zeroPreds),1,0)
		if (rows(onePreds)>0) tauXi[onePreds]=J(rows(onePreds),1,1)		/* Check function set at 0,1 with perfect preds */
		gi=wt:*zi:*((yi:<(gamma:+di*b'))-tauXi)
	}
	// IV-QR
	else {
		gi=wt:*zi:*((yi:<(gamma:+di*b'))-tau)
	}

	return(gi)
}

// estimate the constant
real matrix estimateGamma(	real matrix b,
							real matrix yi,
							real matrix di,
							real matrix wt,
							real scalar tau,
							string scalar technique,
							real matrix fix)
{
	real matrix Gamma, u, fe_id, u_fe, wt_fe
	real scalar num_fe, gamma_fe, fe
	real matrix dum

	Gamma=mm_quantile(yi-di*b',wt,tau)*J(rows(yi),1,1)
	return(Gamma)
}

real matrix estimateProneness(	real matrix yi,
								real matrix gamma,
								real matrix di,
								real matrix b,
								real matrix xi,
								string scalar technique,
						  		pointer(real scalar function) scalar f,
						  		real matrix wt,
								real matrix flagMat)
{
	transmorphic Model
	real matrix yDum,delHat,xx,xy
	yDum=(yi:<=(gamma+di*b'))
	if (technique=="logit" | technique=="probit") {
		flagMat=checkSupport(yDum,xi)
		nonPerfPredInd=(rowsum(abs(flagMat)):==0)
		yDum=select(yDum,nonPerfPredInd)
		xiToUse=select(xi,nonPerfPredInd)
		Model=moptimize_init()
		moptimize_init_tracelevel(Model,"none")
		moptimize_init_evaluatortype(Model,"lf")
		moptimize_init_depvar(Model,1,yDum)
		moptimize_init_eq_indepvars(Model,1,xiToUse)
		moptimize_init_evaluator(Model,f)
		moptimize_init_weight(Model, wt)
		moptimize(Model)
		delHat=moptimize_result_coefs(Model)
		return(delHat)
	}
	else {
		xx = cross(xi,1,wt,xi,1)
		xy = cross(xi,1,wt,yDum,0)
		delHat=invsym(xx)*xy
	}
	return(delHat)
}

void ProbitObjective(M, b, f)
{
	real matrix y,bX
	y=moptimize_util_depvar(M,1)
	bX=moptimize_util_xb(M,b,1)
	displayflush()
	f=y:*ln(normal(bX)):+(1:-y):*ln(normal(-bX))
}
void LogitObjective(M,b,f)
{
	real matrix y,bX
	y=moptimize_util_depvar(M,1)
	bX=moptimize_util_xb(M,b,1)
	f=y:*(bX:-ln(1:+exp(bX))):-(1:-y):*ln(1:+exp(bX))
}

real matrix weightMatrix(	real rowvector b,
							real scalar tau,
							real matrix yi,
							real matrix di,
							real matrix xi,
							string scalar technique,
							pointer(real scalar function) scalar f,
							real matrix info,
							real matrix fix,
							real matrix zi,
							real matrix wt,
							string scalar type)
{
	real scalar i
	real matrix gi,A, wt0,zbar

	gi=momentEvaluator(b,tau,yi,di,xi,technique,f,info,fix,zi,wt)
	A=invsym((1/rows(yi))*cross(gi,gi))

	return(A)
}

void gqreg_process_draws(real matrix Draws,
							 real matrix vals,
							 string scalar jumble,
							 real scalar thin,
							 real scalar burn,
							 transmorphic betaf,
							 transmorphic valsf)
{
	real scalar i
	real matrix ordVec

	Draws = Draws[burn+1::rows(Draws),]

	if (thin!=1) {
		keep=J(0,1,.)
		for (i=thin;i<=rows(Draws);i=i+thin) keep=keep \ i
	}
	else keep=(1::rows(Draws))

	if (jumble=="jumble") _jumble(keep)
	betaf=Draws[keep,]
	valsf=vals[keep,]
}

// GQR analytic standard errors.
real matrix gqr_var(yi,di,xi,beta,tau,zi,wt,deriv,technique,f,fix,info)
{
	real scalar bdw,n
	real matrix dib,gamma,uu,iqr,meanuu,sqruu,pick,stderr,meansqr
	real matrix dep1, dep2, diff, G

	n=rows(yi)

	dib=di*beta'

	gamma=mm_quantile(yi-dib,wt,tau)*J(rows(yi),1,1)

	// figure out bandwidth
	uu = yi:-dib:-gamma
	iqr=mm_iqrange(uu, 1)
	meanuu=colsum(uu)/rows(uu)
	sqruu=(uu:-meanuu):^2
	meansqr=(colsum(sqruu)/(rows(uu)-1))^.5
	pick=(meansqr,iqr/1.349)
	stderr=rowmin(pick)
	bdw=1.0589*stderr*(rows(yi))^(-.2)

	//Get G
	dep1=(uu :< bdw)
	dep2=(uu :< -bdw)

	if (technique=="logit" | technique=="probit") {
		Mod1=moptimize_init()
		moptimize_init_tracelevel(Mod1,"none")
		moptimize_init_evaluatortype(Mod1,"lf")
		moptimize_init_depvar(Mod1,1,dep1)
		moptimize_init_eq_indepvars(Mod1,1,xi)
		moptimize_init_evaluator(Mod1,f)
		moptimize_init_weight(Mod1, wt)
		moptimize(Mod1)
		delHat1=moptimize_result_coefs(Mod1)

		Mod2=moptimize_init()
		moptimize_init_tracelevel(Mod2,"none")
		moptimize_init_evaluatortype(Mod2,"lf")
		moptimize_init_depvar(Mod2,1,dep2)
		moptimize_init_eq_indepvars(Mod2,1,xi)
		moptimize_init_evaluator(Mod2,f)
		moptimize_init_weight(Mod2, wt)
		moptimize(Mod2)
		delHat2=moptimize_result_coefs(Mod2)

	}
	else {
		xx = cross(xi,1,wt,xi,1)
		xy = cross(xi,1,wt,dep1,0)
		delHat1=invsym(xx)*xy

		xx = cross(xi,1,wt,xi,1)
		xy = cross(xi,1,wt,dep2,0)
		delHat2=invsym(xx)*xy
	}

		if (technique=="logit") {
			pr1 =exp((xi,J(rows(xi),1,1))*delHat1'):/(1:+exp((xi,J(rows(xi),1,1))*delHat1'))
			pr2 =exp((xi,J(rows(xi),1,1))*delHat2'):/(1:+exp((xi,J(rows(xi),1,1))*delHat2'))
		}
		else if (technique=="probit") {
			pr1 =normal((xi,J(rows(xi),1,1))*delHat1')
			pr2 =normal((xi,J(rows(xi),1,1))*delHat2')
		}
		else {
			pr1 =(xi,J(rows(xi),1,1))*delHat1
			pr2 =(xi,J(rows(xi),1,1))*delHat2
		}
	diff=(dep1:-dep2):-(pr1:-pr2)

	// relationship btw constant and coeff on X
	constderiv=J(rows(di),cols(di),0)
	for (l=1; l<=cols(di); l++) {
		beta_plus=beta
		beta_plus[l]=beta[l]:+deriv[l]
		gamma_psi_plus=estimateGamma(beta_plus, yi,di,wt,tau,technique,fix)
		beta_minus=beta
		beta_minus[l]=beta[l]:-deriv[l]
		gamma_psi_minus=estimateGamma(beta_minus,yi,di,wt,tau,technique,fix)
		constderiv[.,l]=(gamma_psi_plus:-gamma_psi_minus):/(2:*deriv[l])
	}

	diff=wt:*diff
	G=cross(zi,diff,(di:+ constderiv))/(2*bdw*n)

	//SIGMA
	gi=momentEvaluator(beta,tau,yi,di,xi,technique,f,info,fix,zi,wt)
	Sigma=((1/n)*cross(gi,gi))

	variance=invsym(G'*invsym(Sigma)*G)/rows(info)
	//variance=invsym(G'*A*G)*(G'*A*Sigma*A*G)*invsym(G'*A*G)

	return(variance)
}

// heteroskedastic consistent variance-covariance matrix for standard quantile regression.
// adapted from Chernozhukov and Hansen's vcqr.ox code
real matrix kcov_qr(beta,tau,y,x,wt)
{
	real matrix  vc, n, S, e, J, k, h

	n=rows(y)
	x=(x,J(rows(x),1,1))
	k=cols(beta)
	vc=J(k,k,0)
	S=(1/n)*cross(x,wt,x)
	e=y-x*beta'

	h = 1.364*((2*sqrt(pi()))^(-1/5))*sqrt(variance(e,wt))*(n^(-1/5));
	J = (1/(n*h))*cross((normalden(e/h):*x),wt,x)
	J = cholinv(J)

    vc = (1/n)*(tau-tau^2)*J'*S*J;

	return(vc)
}

// heteroskedastic consistent variance-covariance matrix for standard IV-quantile regression.
// adapted from Chernozhukov and Hansen's vciqr.ox code
real matrix kcov_iqr(beta,tau,y,d,xi,z,wt)
{
	real matrix  vc, n, S, e, J, k, h

	n=rows(y)
	x=(xi,J(rows(xi),1,1))
	k=cols((d,x))
	vc=J(k,k,0)
	S = (1/n)*cross((z,x),wt,(z,x))
	e=y-(d,x)*beta'

	h = 1.364*((2*sqrt(pi()))^(-1/5))*sqrt(variance(e,wt))*(n^(-1/5));
	J = (1/(n*h))*cross((normalden(e/h):*(d,x)),wt,(z,x))
	J = invsym(J)

    vc = (1/n)*(tau-tau^2)*J'*S*J;

	return(vc)
}

// heteroskedastic consistent variance-covariance matrix for standard IV-quantile regression.
// Over-identified case.
// adapted from Chernozhukov and Hansen's vciqr_oid.m code

real matrix kcov_iqr_oid(beta,tau,y,d,xi,z,wt)
{
	real matrix  vc, n, x, S, e, h, Ja, H,JT,Jg,Jb,K,L,M

	n=rows(y)
	x=(xi,J(rows(xi),1,1))
	k=cols((d,x))
	vc=J(k,k,0)
	S = (tau-tau^2)*(1/n)*cross((z,x),wt,(z,x))
	e=y-(d,x)*beta'

	h = 1.364*((2*sqrt(pi()))^(-1/5))*sqrt(variance(e,wt))*(n^(-1/5));
	Ja = (1/(n*h))*cross((normalden(e/h):*d),wt,(z,x))
	H = (1/(n*h))*cross((normalden(e/h):*(z,x)),wt,(z,x))
	JT = invsym(H)
	Jg = JT[1..cols(z),.]
	Jb = JT[(cols(z)+1)..rows(JT),.]
	K = invsym(Ja*Jg'*invsym(Jg*S*Jg')*Jg*Ja')*(Ja*Jg'*invsym(Jg*S*Jg')*Jg);
	L  = Jb - Jb*Ja'*K;

	M = (K \ L);

    vc = (1/n)*M*S*M';

	return(vc)
}

// Functions added by Matt on 7/31/2015 to check whether there are any perfect predictors and act accordingly.
// This is then reported to the user.
real matrix checkSupport(y,X) {
	flag=J(rows(X),cols(X),0)
	for (i=1;i<=cols(X);i++) {
		vals=uniqrows(X[,i])
		check=rows(vals)
		if (check==2) {
			for (j=1;j<=check;j++) {
				yCheck=select(y,X[,i]:==vals[j])
				if (all(yCheck:==0)) {
					pos=mm_which(X[,i]:==vals[j])
					flag[pos,i]=J(rows(pos),1,-1)
				}
				else if (all(yCheck:==1)) {
					pos=mm_which(X[,i]:==vals[j])
					flag[pos,i]=J(rows(pos),1,1)
				}
			}
		}
	}
	return(flag)

}
/* Maybe return a message at some point? */
void supportMessage(real matrix X,string matrix namesX)
{
		predY1=colsum(X:==1)
		predY0=colsum(X:==-1)
		for (i=1;i<=cols(X);i++) {
				if (predY1[i]>0) {
					printf("{txt}Variable {cmd}%s {txt}predicts p(y>gamma+d'b)=1 for %f values perfectly in last iteration.\n",
						namesX[i],predY1[i])
					printf("Proneness set to p(y>gamma+d'b)=1.\n")
					displayflush()
				}
				if (predY0[i]>0) {
					printf("{txt}Variable {cmd}%s {txt}predicts p(y<=gamma+d'b)=0 for %f values perfectly in last iteration.\n",
						namesX[i],predY0[i])
					printf("  Proneness set to p(y>gamma+d'b)=0.\n")
				}
		}
}

/* function added by Travis to find row max */
real matrix findRowMax(	real colvector X1,
						real matrix X2)
{
	real colvector row_index
	real matrix trash

	row_index=.; trash=.; maxindex(X1,1,row_index,trash)
	RowMax=X2[row_index,.]

	return(RowMax)
}
end