*! version 1.0.0 13Jul2020
//-----------------------------------------------------------------------------
//
//	mivcausal - Testing the hypothesis about the signs of the 2SLS weights
//	Checking data and input
//
//-----------------------------------------------------------------------------

capt program drop mivchecks

program mivchecks, rclass

	version 10.0
	syntax anything [if] [in] [, SEEd(integer 1) 							///
								 PRECision(integer 3) 						///
								 MTDraws(integer 10000) 					///
								 RSW(passthru)]

	// Step 1 - Check whether the parameters `precision' and `mtdraws'
	// are positive (they have to be integers so that this function is called)
	* Generic error message
	scalar gen_error = "Error: Please input a positive integer for "

	* Check `precision'
	if `precision' <= 0 {
		di as error gen_error "'precision'."
		exit 498
	}

	* Check `mtdraws'
	if `mtdraws' <= 0 {
		di as error gen_error "'mtdraws'."
		exit 498
	}

	// Step 2 - Check whether RSW should be called and the number of reps for
	// the RSW test
	* RSW error messages
	local rswerr "Error: The option for the RSW test is rsw(reps = x) "		///
				 "where x is a positive integer representing the number "	///
				 "of replications."

	* If RSW is missing, check if it is because user did not specify anything
	* inside the parentheses.
	if missing("`rsw'") {
		* If yes, assign the default number of reps as 1000.
		if strpos("`0'", "rsw") > 0 {
			local rswreps 1000
		}
		* Otherwise, assign the default number of reps as 0.
		else {
			local rswreps 0
		}
	}
	* Otherwise, check whether the RSW syntax is correct and extract the number
	* of reps.
	else {
		* Extract the RSW syntax
		gettoken rsw1 rsw2 : rsw, parse("(") match(paren)
		if "`rsw1'" != "rsw" {
			local rswreps 0
		}
		else {
			* Extract the cluster inside the parentheses
			gettoken rsw3 rsw4 : rsw2, match(paren)

			* Assign the default number of replications if nothing is inside
			* the parentheses
			if "`rsw2'" == "" {
				local rswreps 1000
			}

			* Check if the syntax is correct. If correct, it should be empty
			* here.
			if "`rsw4'" != "" {
				di as error "`rswerr'"
				exit 498
			}

			* Split the parts before and after the equal sign
			gettoken rsw5 rsw6 : rsw3, match(paren)
			if lower("`rsw5'") != "reps" {
				di as error "`rswerr'"
				exit 498
			}

			* Extract the number of reps
			gettoken rsw7 rsw8 : rsw6, match(paren)
			local rswsign : list retokenize rsw7
			local rswreps : list retokenize rsw8

			* Check if the user put an equal sign
			if lower("`rswsign'") != "=" {
				di as error "`rswerr'"
				exit 498
			}

			* Check the number of reps (Allow 0 here, which means the test will
			* be skipped)
			if `rswreps' < 0 {
				di as error gen_error "the number of replications for the " ///
									  "RSW test."
				exit 498
			}
		}
	}

	* Return whether the RSW test is run and the number of replications
	return local rswreps = `rswreps'

	// Step 3 - Check whether there is one treatment and two instruments
	// This is checked by there must be at least three variables
	* Count the number of variables
	local nvar : word count `anything'
	if `nvar' < 3 {
		di as error "Error: There has to be at least three variables for " 	///
					"the 'mivcausal' module (one binary treatment and "   	///
					"two binary instruments)."
		exit 498
	}

	// Step 4 - Retrieve the treatment, instruments and covariates
	* Treatment
	local dvar: word 1 of `anything'

	* Instruments
	local zvars ""
	forval i = 2/3 {
		local ztemp: word `i' of `anything'
		local zvars `zvars' `ztemp'
	}

	* Covariates
	local vardrop `dvar' `zvars'
	local xvars: list anything - vardrop

	// Step 5 - Check whether the treatment and instruments are binary
	* Define generic messages
	scalar msg1 = "Error: The variable '"
	scalar msg2 = "' has to be binary."

	* Check the treatment and instruments
	foreach vartemp of varlist `zvars' `dvar' {
		qui tab `vartemp'
		if r(r) != 2 {
			di as error msg1 "`vartemp'" msg2
			exit 498
		}
	}

	// Step 6 - Convert to treatment and instruments to 0 and 1 if they are
	// not already binary with 0 and 1 being the only possible values
	* Dummy variable to store the list of variables that are not 0 or 1
	local varerr `'
	mat varerrval = J(3, 2, .)

	* Initialize two scalars
	scalar nvarerr = 0
	scalar k = 1

	* Empty local variables to contain the updated variables
	local dvar_new `'
	local zvars_new `'

	* Inspect the treatment and instruments
	foreach vartemp of varlist `dvar' `zvars' {
		* Check the variable
		cap assert `vartemp' == 0 | `vartemp' == 1

		* Generate a variable that represents the variable if it is non 0 or 1
		if _rc != 0 {
			* Update the counter for variables being updated
			scalar nvarerr = nvarerr + 1

			* Obtain the max and min of the variable
			qui sum `vartemp'
			scalar vartemp_1 = r(max)
			scalar vartemp_0 = r(min)

			* Define the initial name for the variable
			local binarytemp `vartemp'_bin
			cap confirm variable `binarytemp', exact

			* Add some random integers to the variable name until the variable
			* name does not exist
			while _rc == 0 {
				local binarytemp `binarytemp'`=runiformint(1,100)'
				cap confirm variable `binarytemp', exact
			}

			* Generate the binary variable
			qui gen `binarytemp' = 1 if `vartemp' == vartemp_1
			qui replace `binarytemp' = 0 if `vartemp' == vartemp_0

			* Add the variable to the list
			mat varerrval[nvarerr, 1] = vartemp_1
			mat varerrval[nvarerr, 2] = vartemp_0

			* Assign the list of variables that are updated
			local varerr `varerr' `vartemp'

			* Assign the new list of variables with the updated variable
			if k == 1 {
				local dvar_new `dvar_new' `binarytemp'
			}
			else {
				local zvars_new `zvars_new' `binarytemp'
			}
		}
		else {
			* Assign the original variable in the updated list if the variable
			* is already either 0 or 1
			if k == 1 {
				local dvar_new `dvar_new' `dvar'
			}
			else {
				local zpos = k - 1
				local ztemp: word `zpos' of `zvars'
				local zvars_new `zvars_new' `ztemp'
			}
		}

		* Update counter
		scalar k = k + 1
	}

	// Step 7 - Return the list of variables that are updated
	return local varerr `varerr'
	return local dvar_new `dvar_new'
	return local zvars_new `zvars_new'

end