*! mixlogitwtp 1.1.0 29Mar2016
*! author arh

*  1.1.0:	a bug that could affect the estimation results in the case of
*			extreme parameter values during the iterations has been fixed

program mixlogitwtp
	version 11.1
	if replay() {
		if (`"`e(cmd)'"' != "mixlogitwtp") error 301
		Replay `0'
	}
	else	Estimate `0'
end

program Estimate, eclass
	syntax varlist [if] [in] 		///
		[pweight iweight],			///
		GRoup(varname) 				///
		PRICE(varname) [			///
		RAND(varlist) 				///
		ID(varname) 				///
		LN(integer 0) 				///
		CORR						///
		NREP(integer 50)			///
		BURN(integer 15)			///
		FRom(string)				///
		Level(integer `c(level)')	///
		USERdraws					///
		TRace						///
		GRADient					///
		HESSian						///
		SHOWSTEP					///
		ITERate(passthru)			///
		TOLerance(passthru)			///
		LTOLerance(passthru)		///
		GTOLerance(passthru)		///
		NRTOLerance(passthru)		///
		CONSTraints(passthru)		///
		TECHnique(passthru)			///
		DIFficult					///
		VCE(passthru)				///
		COLL						///
	]

	local mlopts `trace' `gradient' `hessian' `showstep' `iterate' `tolerance' 	///
	`ltolerance' `gtolerance' `nrtolerance' `constraints' `technique' 			///
	`difficult' `vce'

	local randnop `rand'
	local rand `rand' `price'
	local ln = `ln' + 1

	if ("`search'" != "") {
		local search
		set seed `seed'
	}
	else local search search(off)

	** Check that group and id variables are numeric **
	capture confirm numeric var `group'
	if _rc != 0 {
		di in r "The group variable must be numeric"
		exit 498
	}
	if ("`id'" != "") {
		capture confirm numeric var `id'
		if _rc != 0 {
			di in r "The id variable must be numeric"
			exit 498
		}
	}

	** Create local wgt for use with clogit if weights are specified **
	if ("`weight'" != "") local wgt "[`weight'`exp']"

	** Mark the estimation sample **
	marksample touse
	markout `touse' `group' `rand' `id' `cluster'

	** Check that no variables have been specified to have both fixed and random coefficients **
	gettoken lhs fixed : varlist
	local k1 : word count `fixed'
	local k2 : word count `rand'
	local k12 = `k1'+`k2'

	if (`k12' == 0) {
		di in red "No explanatory variables have been specified"
		exit 498
	}
	forvalues i = 1(1)`k1' {
		forvalues j = 1(1)`k2' {
			local w1 : word `i' of `fixed'
			local w2 : word `j' of `rand'
			if ("`w1'" == "`w2'") {
				di in red "The variable `w1' is specified to have both fixed and random coefficients"
				exit 498
			}
		}
	}

	** Check that starting values are specified with the constraints option **
	if ("`constraints'" != "" & "`from'" == "") {
		di in red "When constraints are specified it is compulsory to supply starting values using the from option"
		exit 498
	}

	** Check that starting values are specified with the coll option **
	if ("`coll'" != "" & "`from'" == "") {
		di in red "When the coll option is specified it is compulsory to supply starting values using the from option"
		exit 498
	}

	** Check for multicollinearity **
	local rhs `fixed' `rand'
	if ("`coll'" == "") {
		qui _rmcoll `rhs'
		if ("`r(varlist)'" != "`rhs'" & "`constraints'" != "") {
			di in gr "Some variables are collinear - make sure this is intended, i.e. because you are"
			di in gr "estimating an error-components model with the necessary constraints imposed"
		}
		if ("`r(varlist)'" != "`rhs'" & "`constraints'" == "") {
			di in red "Some variables are collinear - check your model specification"
			exit 498
		}
	}

	** Estimate conditional logit model - if constraints or the coll option are specified this is simply to set estimation sample **
	if ("`constraints'" == "" & "`coll'" == "") {
		qui clogit `lhs' `rhs' if `touse' `wgt', group(`group')
		local nobs = e(N)
		local ll = e(ll)
		local k  = e(k)
		qui replace `touse' = e(sample)
	}
	else {
		qui clogit `lhs' `rhs' if `touse' `wgt', group(`group') iter(0)
		local nobs = e(N)
		qui replace `touse' = e(sample)
	}

	** Drop missing data **
	preserve
	qui keep if `touse'

	** Check that the independent variables vary within groups **
	sort `group'
	foreach var of varlist `rhs' {
		capture by `group': assert `var'==`var'[1]
		if (_rc == 0) {
			di in red "Variable `var' has no within-group variance"
			exit 459
		}
	}

	** Check that the dependent variable only takes values 0-1 **
	capture assert `lhs' == 0 | `lhs' == 1
	if (_rc != 0) {
		di in red "The dependent variable must be a 0-1 variable indicating which alternatives are chosen"
		exit 450
	}

	** Check that each group has only one chosen alternative **
	tempvar chonum
	sort `group'
	qui by `group': egen `chonum' = sum(`lhs')
	capture assert `chonum' == 1
	if (_rc != 0) {
		di in red "At least one group has more than one chosen alternative"
		exit 498
	}

	** Generate individual id **
	if ("`id'" != "") {
		tempvar nchoice pid
		sort `group'
		by `group': gen `nchoice' = cond(_n==_N,1,0)
		sort `id'
		by `id': egen `pid' = sum(`nchoice')
		qui duplicates report `id'
		mata: mixlwtp_np = st_numscalar("r(unique_value)")
		mata: mixlwtp_T = st_data(., st_local("pid"))
	}
	else {
		qui duplicates report `group'
		mata: mixlwtp_np = st_numscalar("r(unique_value)")
		mata: mixlwtp_T = J(st_nobs(),1,1)
	}

	** Generate choice occasion id **
	tempvar csid
	sort `group'
	by `group': egen `csid' = sum(1)

	** Sort data **
	sort `id' `group'

	** Set Mata matrices and scalars to be used in optimisation routine **
	local kfix: word count `fixed'
	local krnd: word count `rand'

	mata: mixlwtp_X = st_data(., tokens(st_local("rhs")))
	mata: mixlwtp_Y = st_data(., st_local("lhs"))
	mata: mixlwtp_CSID = st_data(., st_local("csid"))

	mata: mixlwtp_nrep = strtoreal(st_local("nrep"))
	mata: mixlwtp_kfix = strtoreal(st_local("kfix"))
	mata: mixlwtp_krnd = strtoreal(st_local("krnd"))
	mata: mixlwtp_krln = strtoreal(st_local("ln"))
	mata: mixlwtp_burn = strtoreal(st_local("burn"))

	if ("`userdraws'" != "") mata: mixlwtp_user = 1
	else mata: mixlwtp_user = 0

	** Restore data **
	restore

	** Create macro to define equations for optimisation routine **
	local mean (Mean: `rhs', noconst)
	if ("`corr'" == "") {
		mata: mixlwtp_corr = 0
		local sd (SD: `rand', noconst)
		local max `mean' `sd'
	}
	else {
		mata: mixlwtp_corr = 1
		local cho = `krnd'*(`krnd'+1)/2
		mata: mixlwtp_ncho = strtoreal(st_local("cho"))
		local max `mean'
		forvalues i = 1(1)`krnd' {
			forvalues j = `i'(1)`krnd' {
				local max `max' /l`j'`i'
			}
		}
	}

	/*
	** Create macro to define equations for optimisation routine **
	local meanwtp (Mean_WTP: `fixed' `randnop', noconst)
	local meanlnp (Mean_ln_price: `price', noconst)
	if ("`corr'" == "") {
		mata: mixlwtp_corr = 0
		local sdwtp (SD_WTP: `randnop', noconst)
		local sdlnp (SD_ln_price: `price', noconst)
		local max `meanwtp' `meanlnp' `sdwtp' `sdlnp'
	}
	else {
		mata: mixlwtp_corr = 1
		local cho = `krnd'*(`krnd'+1)/2
		mata: mixlwtp_ncho = strtoreal(st_local("cho"))
		local max `meanwtp' `meanlnp'
		forvalues i = 1(1)`krnd' {
			forvalues j = `i'(1)`krnd' {
				local max `max' /l`j'`i'
			}
		}
	}
	*/

	** Create matrix of starting values unless specified **
	if ("`from'" == "") {
		tempname b from
		matrix `b' = e(b)

		local fixnorm = `kfix'+`krnd'-`ln'
		forvalues  i = 1(1)`fixnorm' {
			matrix `from' = nullmat(`from'), `b'[1,`i'] / `b'[1,(`kfix'+`krnd')]
		}

		local lnmin1 = `ln' - 1
		forvalues i = 1(1)`lnmin1' {
			if (`b'[1,(`kfix'+`krnd'-`ln'+`i')] <= 0) {
				di in red "Variables specified to have log-normally distributed WTP coefficients should have positive"
				di in red "coefficients in the conditional logit model. Try multiplying the variable by -1."
				exit 498
			}
			matrix `from' = nullmat(`from'), ln(`b'[1,(`kfix'+`krnd'-`ln'+`i')] / `b'[1,(`kfix'+`krnd')])
		}

		if (`b'[1,(`kfix'+`krnd')] <= 0) {
			di in red "The price variable should be  multiplied by -1 so that it has a positive coefficient in the"
			di in red "conditional logit model."
			exit 498
		}
		matrix `from' = nullmat(`from'), ln(`b'[1,(`kfix'+`krnd')])

		if ("`corr'" == "") matrix `from' = `from', J(1,`krnd',0.1)
		else matrix `from' = `from', J(1,`cho',0.1)
		local copy , copy
	}


	** Run optimisation routine **
	if ("`id'" != "") {
		ml model gf0 mixlwtp_gf0()										///
			`max' if `touse' `wgt', group(`id') init(`from' `copy')		///
			`search' `mlopts' maximize missing nopreserve `coll'
	}
	else {
		ml model gf0 mixlwtp_gf0()										///
			`max' if `touse' `wgt', group(`group') init(`from' `copy')	///
			`search' `mlopts' maximize missing nopreserve `coll'
	}

	** To be returned as e() **
	ereturn local title "Mixed logit model in WTP space"
	ereturn local indepvars `rhs'
	ereturn local depvar `lhs'
	ereturn local group `group'
	ereturn scalar kfix = `kfix'

	ereturn scalar krnd = `krnd'
	ereturn scalar krln = `ln'
	ereturn scalar nrep = `nrep'
	ereturn scalar burn = `burn'
	if ("`corr'" != "") {
		ereturn scalar corr = 1
		ereturn scalar k_aux = `cho'
	}
	else ereturn scalar corr = 0
	if ("`id'" != "") ereturn local id `id'
	if ("`userdraws'" != "") ereturn scalar userdraws = 1
	else ereturn scalar userdraws = 0

	ereturn local cmd "mixlogitwtp"

	if ("`corr'" == "") Replay , level(`level')
	else Replay , level(`level') corr
end

program Replay
	syntax [, Level(integer `c(level)') CORR]
	ml display , level(`level')
	if ("`corr'" == "") {
		di in gr "The sign of the estimated standard deviations is irrelevant: interpret them as"
		di in gr "being positive"
	}
end

exit