*! version 1.0.4 21may2011 Daniel Klein
*	1.0.1 	reference may now be specified
*	1.0.2	assert mi set wide manually
*			u_mi_assert wide not fixed yet
*	1.0.3	u_mi_assert fixed and used
*			tempname for matrix checking dummy-coding
*	1.0.4	assert double precision

prog mi_cmd_mvncat
	version 11.1

	u_mi_assert_set wide
	if `_dta[_mi_M]' < 1 srerr noms

	syntax anything(id = "dset [(reference)]") [,noUPdate *]

	/*parse anything
	-----------------*/
	loc i 1
	gettoken dset`i' anything : anything ,p("\/")
	if inlist("`dset`i''", "\","/") srerr novars
	gettoken dset`i' refc`i' : dset`i' ,p("(") //check reference specified
	gettoken dump anything : anything ,p("\/")
	while !inlist(`"`dset`i''"', "", "\", "/") {
		gettoken dset`++i' anything : anything ,p("\/")
		/*check for explicit reference category*/
		gettoken dset`i' refc`i' : dset`i' ,p("(")
		gettoken dump anything : anything ,p("\/")
	}
	loc ndset = `i' - 1

	/*call subroutine fvcat in loop
	--------------------------------*/
	forval j = 1/`ndset' {
		loc user_ref = cond("`refc`j''" == "", "", "user_ref`refc`j''")
		srfvcat `dset`j'' ,`user_ref' `options'
	}

	/*mi update
	------------*/
	u_mi_certify_data, acceptable
	if "`update'" == "" u_mi_certify_data ,proper
end

prog srfvcat
	syntax varlist(numeric) [,REPort user_ref(name)]

	u_mi_no_sys_vars "`varlist'"
	u_mi_no_wide_vars "`varlist'"

	/*edit varlist
	---------------*/
	if "`user_ref'" != "" loc varlist : list varlist - user_ref
	loc varlist : list uniq varlist

	/*check dummies and determine reference category
	-------------------------------------------------*/
	tempname zo
	loc M `_dta[_mi_M]'
	loc ivlist `_dta[_mi_ivars]'
	loc nniv = ("`user_ref'" != "")
	foreach d of loc varlist {
		qui ta `d' ,matrow(`zo')
		if (`= rowsof(`zo')' != 2) | (`zo'[1,1] != 0) ///
			| (`zo'[2,1] != 1) srerr nodummy `d'
		if !`: list posof "`d'" in ivlist' {
			if `nniv' srerr notiv `varlist' `user_ref'
			loc nniv 1
			continue
		}
		forval m = 1/`M' {
			conf v _`m'_`d'
			if "`user_ref'" != "" {
				cap conf v _`m'_`user_ref'
				if !_rc loc refcat`m' _`m'_`user_ref'
			}
			else {
				cap ass _`m'_`d' != .
				if _rc {
					if "`refcat`m''" != "" srerr msoftmiss `varlist' ///
					,r1(`refcat`m'') r2(_`m'_`d')
					loc refcat`m' _`m'_`d'
					continue
				}
			}
			loc dlist`m' `dlist`m'' _`m'_`d'
		}
	}

	/*calculate values for reference category and assign final values
	------------------------------------------------------------------*/
	tempvar tmp_ref tmp_max tmp_chksum
	forval m = 1/`M' {
		loc vartype : t `: word 1 of `dlist`m'''
		loc doub = cond("`vartype'" == "double", "double", "")
		loc fp = cond("`vartype'" == "double", "", "float")
		qui g `double' `tmp_ref' = 1 - `: word 1 of `dlist`m''' ///
		if !inlist(`: word 1 of `dlist`m''', 0 ,1)
			/*create tmp_ref = . if no imputatios made
			!mi(`tmp_ref') can then be used to identify*/
		forval j = 2/`: word count `dlist`m''' {
			qui replace `tmp_ref' = `tmp_ref' - `: word `j' of `dlist`m'''
		}
		loc tmp_list `dlist`m'' `tmp_ref'
		qui egen `double' `tmp_max' = rowmax(`tmp_list') if !mi(`tmp_ref')
		qui g `tmp_chksum' = 0 if !mi(`tmp_ref')

		/*assign final values
		----------------------*/
		forval j = 1/`: word count `tmp_list'' {
			qui replace `: word `j' of `tmp_list'' ///
				= `fp'(`: word `j' of `tmp_list'') == `fp'(`tmp_max') ///
					if !mi(`tmp_ref')
			qui replace `tmp_chksum' = ///
				`tmp_chksum' + `: word `j' of `tmp_list''
				/*chksum has . where tmp_ref has .*/
		}

		/*check only one dummy set to one
		----------------------------------*/
		cap as `tmp_chksum' == 1 if !mi(`tmp_ref')
		if _rc srerr notaddone `m'
		if "`refcat`m''" != "" qui replace `refcat`m'' = `tmp_ref' ///
			if `refcat`m'' == .

		/*report
		---------*/
		if "`report'" != "" {
			di _n
			loc norc = cond("`refcat`m''" == "", "no ", "")
			di "{txt}assigning final values to ... "
			di "{res}`dlist`m''"
			di "{txt}`norc'reference category {res}`refcat`m''"
		}

		/*clear locals and drop tempvars
		---------------------------------*/
		loc dlist`m'
		loc refcat`m'
		drop `tmp_ref' `tmp_max' `tmp_chksum'
	}
end

prog srerr
	gettoken werr 0 : 0
	if "`werr'" == "noms" {
		di "{err}no imputed values"
		exit 459
	}
	if "`werr'" == "novars" {
		di "{err}syntax is {bf:dset1 [ \ dset2 ... \ dsetk]}"
		exit 198
	}
	if "`werr'" == "nodummy" {
		di "{err}`0': not a dummy; only values 0 and 1 are allowed"
		exit 459
	}
	if "`werr'" == "notiv" {
		syntax varlist
		di "{err}`varlist': more than one dummy not registered as imputed"
		exit 459
	}
	if "`werr'" == "msoftmiss" {
		syntax varlist ,r1(varlist) r2(varlist)
		di "{err}`varlist': cannot determine reference category;"
		di "`r1' and `r2' both contain soft missings"
		exit 459
	}
	if "`werr'" == "notaddone" {
		di "{err}unexpected error in dataset {it:m} = `2';"
		di "{err}final values do not add up to 1"
		exit 498
	}
end