*******************************************************************************************
*** ----------------------------------------------------------------------------------- ***
*** pc_bootstrap_units: BOOTSTRAP ADDITIONAL UNITS FOR POWER CALCULATIONS BY SIMULATION ***
*** ----------------------------------------------------------------------------------- ***
*******************************************************************************************

*** version 1.0 19dec2016
*** part of ssc package "pcpanel"

program define pc_bootstrap_units
version `=clip(`c(version)', 9.0, 13.1)'

syntax varname [if] [in], Nunits(numlist max=1 integer >0) [VARlist(varlist) sort(varlist)]
{
// store master dataset in tempfile to restore if program breaks
tempfile m_dta_before_bs
quietly save `m_dta_before_bs', replace

// local for cross-sectional unit to be bootstrapped with replacement
local uNIt = subinstr("`1'",",","",1)

// identify observations to be included
quietly gen tEMp_if_in = 0
quietly replace tEMp_if_in = 1 `if' `in'

// sort data and store order of sort
sort tEMp_if_in `uNIt' `sort'
quietly gen tEMp_sORt = _n

// checks on bootstrap unit varaible
capture confirm numeric variable `uNIt'
	local rc = _rc
	if `rc' {
		display "{err}Error: Bootstrap unit id variable `uNIt' must be numeric"
		use `m_dta_before_bs', clear
		exit `rc'
	}
capture assert `uNIt'!=. if tEMp_if_in==1
	local rc = _rc
	if `rc' {
		display "{err}Error: `uNIt' cannot have missing values"
		use `m_dta_before_bs', clear
		exit `rc'
	}

// confirm that the dataset needs to be bootstrapped
quietly unique `uNIt' if tEMp_if_in==1
local Nexisting = r(sum)
capture assert `Nexisting' < `nunits'
	local rc = _rc
	if `rc' {
		display "{err}Error: Dataset already contains `Nexisting' unique `uNIt' units"
		use `m_dta_before_bs', clear
		exit `rc'
	}

// set parameters for bootstrap	loop
local Nloopstart = `Nexisting'+1
quietly sum `uNIt'
local uNIt_max = r(max)
quietly egen tEMp_taG_uNIt = tag(`uNIt') if tEMp_if_in==1
quietly gen ORIG`uNIt' = .


// bootstrap units
forvalues nLOOP = `Nloopstart'/`nunits' {

	* choose which unit to duplicate on each bootstrap
	quietly gen tEMp_rANdom = runiform()*tEMp_taG_uNIt
	sort tEMp_rANdom
	local uNIt_LOOP = `uNIt'[1]

	* identify rows to duplicate
	quietly sum tEMp_sORt if `uNIt'==`uNIt_LOOP' & tEMp_if_in==1
	local dUPstart = r(min)
	local dUPend = r(max)

	* increase observation count to accommodate new (duplicate) rows
	local oLDobs = _N
	local nEWobs = `dUPend'-`dUPstart'+1
	local oBs = `oLDobs'+`nEWobs'
	quietly set obs `oBs'

	* create new unit id for bootstrapped unit
	quietly replace `uNIt' = `uNIt_max'+`nLOOP'-`Nexisting' if `uNIt'==.

	* store original unit from which simulated unit was created
	quietly replace ORIG`uNIt' =  `uNIt_LOOP' if `uNIt'==`uNIt_max'+`nLOOP'-`Nexisting'

	* restore original sort, populate missing new rows with sort ids
	sort tEMp_sORt
	quietly replace tEMp_sORt = _n if tEMp_sORt==.
	capture assert tEMp_sORt==_n
		local rc = _rc
		if `rc' {
			display "{err}Error re-sorting data during bootstrap"
			use `m_dta_before_bs', clear
			exit `rc'
		}

	* populate new variables
	forvalues iLOOP = 1/`nEWobs' {
		if "`varlist'"=="" {
			foreach v of varlist * {
				if inlist("`v'","`uNIt'","tEMp_rANdom","tEMp_taG_uNIt","ORIG`uNIt'","tEMp_sORt")==0 {
					quietly replace `v' = `v'[`dUPstart'+`iLOOP'-1] if tEMp_sORt==`oLDobs'+`iLOOP'
				}
			}
		}
		else {
			foreach v of varlist `varlist' {
				if inlist("`v'","`uNIt'","tEMp_rANdom","tEMp_taG_uNIt","ORIG`uNIt'","tEMp_sORt")==0 {
					quietly replace `v' = `v'[`dUPstart'+`iLOOP'-1] if tEMp_sORt==`oLDobs'+`iLOOP'
				}
			}
		}
	}

	* reset within-loop random draw
	drop tEMp_rANdom

	* report intermediate output
	noisily display "   `nLOOP'"
}

* clean up
sort tEMp_sORt
quietly drop tEMp_sORt tEMp_taG_uNIt tEMp_if_in

* done
local Nnew = `nunits' - `Nexisting'
display _n
display "Bootstrapping complete, dataset now contains `nunits' unique `uNIt' units"
display "(`Nnew' simulated units bootstrapped from existing dataset with replacement, "
display "with original `uNIt' stored in the new variable ORIG`uNIt') "
}

end

*******************************************************************************************
*******************************************************************************************