****Randomizr Stata Port*************
****Module 4:************************
****Block+Cluster Random Assignment**
*************************************
****John Ternovski*******************
****Alex Coppock*********************
****Yale University******************
*************************************
****12sep2017************************
*****version 1.1*********************
***john.ternovski@yale.edu***********
program define block_and_cluster_ra, rclass sortpreserve
	version 15
	syntax [namelist(max=1 name=assignment)] [if] [in], cluster_var(varname) block_var(varname) [prob(numlist max=1 >=0 <=1)] [prob_each(numlist >=0 <=1)] [block_m(numlist >=0)] [block_m_each(string)] [block_prob(numlist >=0 <=1)] [block_prob_each(string)] [num_arms(numlist max=1 >0)] [condition_names(string)] [m(numlist max=1 >=0 int)] [skip_check_inputs] [replace]

//error-checking
if missing(`"`skip_check_inputs'"') {
	//take all available commands and see if more than two are specified
	local commandlist="prob prob_each block_m block_m_each block_prob block_prob_each num_arms m"
	local commandnum=0
	foreach n in `commandlist' {
		local commandnum=`commandnum'+ !missing(`"``n''"')
	}
	if `commandnum'>1 {
		disp as error "ERROR: You must specify only ONE of the following options: prob, prob_each, block_m, block_m_each, block_prob_each, block_prob, num_arms, m"
		exit 1
	}

	//make sure clusters are nested in blocks
	tempvar clustnum
	egen `clustnum'=group(`cluster_var')
	qui tab `clustnum'
	forval i=1/`r(r)' {
		qui tab `block_var' if `clustnum'==`i'
		if `r(r)'>1 {
			disp as error "ERROR: Clusters must be nested within blocks."
			exit 47
		}
	}
}

//get number of clusters
tempvar touse
egen `touse'=tag(`cluster_var' `block_var') `if' `in'

//setting defaults
//set default condition names
if missing(`"`assignment'"') {
	local assignment "assignment"
}

//replace assignment variable and label if replace is specified
if `"`replace'"'!="" {
	cap drop `assignment'
	if _N==0 {
		qui set obs `N'
	}
	cap label drop `assignment'

}

tempvar assignmenttemp
block_ra `assignmenttemp' if `touse'==1 `andif' `in', `replace' block_var(`block_var') prob_each(`prob_each') prob(`prob') block_m(`block_m') block_m_each(`block_m_each') block_prob(`block_prob') block_prob_each(`block_prob_each') num_arms(`num_arms') condition_names(`condition_names') m(`m') `skip_check_inputs'
bysort `cluster_var': egen `assignment'=max(`assignmenttemp')

//update condition name labels if applicable
//programmer's note--unless we want to make label name global/permanent, can't copy local label this many levels up
if !missing(`"`condition_names'"') {
	tempname stringparse
	local `stringparse'=subinstr(`"`condition_names'"'," ","",.)
	cap confirm num ``stringparse''
	if _rc {
		qui levelsof `assignment', local(start)
		local start=substr("`start'",1,1)
		tokenize `"`condition_names'"'
		label define `assignment' `start' `"`1'"'
		macro shift
		local startplusone=`start'+1
		qui tab `assignment'
		forval i=`startplusone'/`r(r)' {
			label define `assignment' `i' `"`1'"', add
			macro shift
		}
		label val `assignment' `assignment'
	}
}

return scalar complete=1


end
*