* Creates undirected dyad contagion spatial effect variable from a dyadic dataset
* 6.0 Eric Neumayer and Thomas Plümper 22may2013

* Change to version 5.0: replaced user-written mmerge.ado commands with Stata's official merge command
* Change to version 4.1: fixed bug that affected row-standardization and spatial effect with additive link functions

program spundir, sortpreserve

version 11.2

syntax varlist(min=1 max=1)[if][in], link(string) Weightvar(name) i(name) j(name) ///
		[time(name) REVerse_W EXCLusive ///
		noMerge noRowst SEname(name) FILEname(name) LABELname(name)]

if "`link'"=="" {
	di as err "User must specify a link function."
	exit 198
}
if "`link'"!="ik" & "`link'"!="ki" & "`link'"!="jm" & "`link'"!="mj" & "`link'"!="ik+jm" & "`link'"!="ki+mj" & "`link'"!="ik*jm" & "`link'"!="ki*mj" {
	di as err "Link(`link') not allowed. See help file for allowed link options."
	exit 198
}

marksample touse, novarlist strok
tokenize `varlist'
local spatialvar "`1'"
capture confirm numeric "`spatialvar'"
capture confirm numeric "`weightvar'"
quietly su `weightvar'
local W_min=r(min)
if `W_min'<0 {
	di as err _newline "The weighting variable must not contain negative values."
	exit 601
}
local sourcetype : type `i'
local targettype : type `j'
capture confirm numeric "`time'"

di _newline "User has specified that:"
di _newline "   {bf}`spatialvar'{sf} is the variable to be spatially lagged."
if "`link'"=="ik" {
	di _newline "   {bf}`weightvar'{sf} will link unit {it}i{sf} to other units {it}k{sf}."
}
if "`link'"=="jm" {
	di _newline "   {bf}`weightvar'{sf} will link unit {it}j{sf} to other units {it}m{sf}."
}
if "`link'"=="ki" {
	di _newline "   {bf}`weightvar'{sf} will link other units {it}k{sf} to unit {it}i{sf}."
}
if "`link'"=="mj" {
	di _newline "   {bf}`weightvar'{sf} will link other units {it}m{sf} to unit {it}j{sf}."
}
if "`link'"=="ik+jm" {
	di _newline "   {bf}`weightvar'{sf} will represent the {bf}sum{sf} of two links:
	di _newline "      link of unit {it}i{sf} to other units {it}k{sf} and link of unit {it}j{sf} to other units {it}m{sf}."
}
if "`link'"=="ki+mj" {
	di _newline "   {bf}`weightvar'{sf} will represent the {bf}sum{sf} of two links:
	di _newline "      link of other units {it}k{sf} to unit {it}i{sf} and link of other units {it}m{sf} to unit {it}j{sf}."
}
if "`link'"=="ik*jm" {
	di _newline "   {bf}`weightvar'{sf} will represent the {bf}product{sf} of two links:
	di _newline "      link of unit {it}i{sf} to other units {it}k{sf} and link of unit {it}j{sf} to other units {it}m{sf}."
}
if "`link'"=="ki*mj" {
	di _newline "   {bf}`weightvar'{sf} will represent the {bf}product{sf} of two links:
	di _newline "      link of other units {it}k{sf} to unit {it}i{sf} and link of other units {it}m{sf} to unit {it}j{sf}."
}

di _newline "   {bf}`i'{sf} is the identifying variable of unit {it}i{sf}."
di _newline "   {bf}`j'{sf} is the identifying variable of unit {it}j{sf}."
if "`time'"=="" {
	di _newline "   the spatial effect variable is {bf}time-invariant{sf}."
}
else {
	di _newline "   {bf}`time'{sf} is the numeric time variable."
}
if "`exclusive'"=="exclusive" {
	di _newline "   Dyads containing either unit {it}i{sf} or {it}j{sf} are {bf}excluded{sf} from having a spatial effect on dyad {it}ij{sf}."
}
if "`rowst'"=="norowst" {
	di _newline "   the spatial effect is {bf}not{sf} to be {bf}row-standardized{sf}."
}
else {
	di _newline "   the spatial effect is to be {bf}row-standardized{sf}."
	local rowst "rowst"
}
di _newline "If these assumptions are incorrect, the generated spatial effect variable will be incorrect."

if "`merge'"=="nomerge" {
	di _newline "User must {bf}merge{sf} the created spatial effect variable into the estimation dataset {bf}by hand{sf}."
	if "`filename'"=="" {
		di _newline "   Warning: file {bf}SE_file_undir_`rowst'{sf} will be replaced in current working directory, if already existant."
	}
	else {
		di _newline "   Warning: file {bf}`filename'{sf} will be replaced in current working directory, if already existant."
	}
}
else {
	di _newline "The created spatial effect variable will be {bf}automatically merged{sf} into the original dataset."
	if "`sename'"=="" {
		di _newline "   Warning: variable {bf}SE_var_undir_`rowst'{sf} will be dropped from dataset before merging, if already existant."
		capture drop SE_var_undir_`rowst'
	}
	else {
		di _newline "   Warning: variable {bf}`sename'{sf} will be dropped from dataset before merging, if already existant."
		capture drop `sename'
	}
	if "`filename'"!="" {
		di _newline "   Warning: file {bf}`filename'{sf} will be replaced in current working directory, if already existant."
	}
}

set more on
di _newline(2) "{bf}*** Check the above. ***{sf}  --  Press q to break, press any other button to continue."
more

preserve
quietly keep if `touse'

quietly {
	tempfile datasample holder addeddyads
	tempvar sourceidinf
	ge `sourceidinf'=.
	tempvar targetidinf
	ge `targetidinf'=.
	quietly compress
	save "`datasample'", replace
	capture drop _merge
	capture drop _fillin
	fillin `i' `j'
	collapse (sum) _fillin, by(`i')
	tempvar nonsymmetric
	su _fillin
	gen `nonsymmetric'=0 if r(max)==0
	if `nonsymmetric'==0 {
		use "`datasample'", replace
		keep `spatialvar' `weightvar' `sourceidinf' `targetidinf' `time' `i' `j'
		save "`holder'", replace
		tempvar varholder
		ren `i' `varholder'
		ren `j' `i'
		ren `varholder' `j'
		keep `time' `i' `j'
		save "`addeddyads'", replace
	}
	else {
		use "`datasample'", replace
		keep `spatialvar' `weightvar' `sourceidinf' `targetidinf' `time' `i' `j'
		save "`holder'", replace
		tempvar varholder
		ren `i' `varholder'
		ren `j' `i'
		ren `varholder' `j'
		save "`addeddyads'", replace
	}
	use "`holder'", replace
	append using "`addeddyads'"
	capture drop _merge
	capture drop _fillin
	fillin `i' `j' `time'
	collapse `spatialvar' `weightvar' `sourceidinf' `targetidinf', by(`i' `j' `time')
	save "`holder'", replace
}


if "`sourcetype'"=="int" | "`sourcetype'"=="long" | "`sourcetype'"=="float" | "`sourcetype'"=="double"  | "`sourcetype'"=="byte" {
	tempvar sourceid
	quietly egen `sourceid'=group(`i')
}
else {
	tempvar sourceid
	quietly encode `i', ge(`sourceid')
}
if "`targettype'"=="int" | "`targettype'"=="long" | "`targettype'"=="float" | "`targettype'"=="double"  | "`targettype'"=="byte" {
	tempvar targetid
	quietly egen `targetid'=group(`j')
}
else {
	tempvar targetid
	quietly encode `j', ge(`targetid')
}
quietly su `sourceid'
local source_max=r(max)
quietly su `targetid'
local target_max=r(max)
local target_min=r(min)
if `source_max'<2 & `target_max'<2 {
		di as err "Generation of spatial effect variable not possible with fewer than 2 units of i and 2 units of j."
		exit 198
}
else {
	di _newline "Generating spatial effect components for dyads involving `source_max' units of i in total - please wait."
}

quietly {
	tempfile spatialvarempty Wempty
	tempvar sourceidinf
	ge `sourceidinf'=.
	tempvar targetidinf
	ge `targetidinf'=.
	quietly compress
	save "`datasample'", replace
	keep `spatialvar' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid' `time'
	save "`holder'", replace
	use "`holder'", replace
	keep if `sourceidinf'==0
	keep `spatialvar' `sourceidinf' `targetidinf' `sourceid' `targetid' `time'
	save "`spatialvarempty'", replace
	use "`holder'", replace
	keep if `sourceidinf'==0
	keep `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid' `time'
	save "`Wempty'", replace
}

forvalues m = 1(1)`source_max' {
	if `m'/10==int(`m'/10) {
		di "."
	}
	else {
		di "." _continue
	}
	quietly {
		use "`spatialvarempty'", replace
		tempfile spatialvarundirdyad_`m'
		save "`spatialvarundirdyad_`m''", replace
		use "`Wempty'", replace
		tempfile Wsource_`m'
		save "`Wsource_`m''", replace
		tempfile Wtarget_`m'
		save "`Wtarget_`m''", replace
		use "`holder'", replace
		replace `sourceidinf'=`m'
		save "`holder'", replace
	}
	forvalues n = 1(1)`target_max' {
		quietly {
			use "`holder'", replace
			replace `targetidinf'=`n'
			drop if `sourceid'>`targetid'
			if "`exclusive'"=="" {
				drop if  (`targetidinf'==`targetid' & `sourceidinf'==`sourceid')
				drop if  (`targetidinf'==`sourceid' & `sourceidinf'==`targetid')
			}
			if "`exclusive'"=="exclusive" {
				drop if  (`targetidinf'==`targetid' | `sourceidinf'==`sourceid')
				drop if  (`targetidinf'==`sourceid' | `sourceidinf'==`targetid')
			}
			keep `time' `spatialvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
			append using "`spatialvarundirdyad_`m''",
			save "`spatialvarundirdyad_`m''", replace
			if "`exclusive'"=="" & "`link'"=="ik" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'==`sourceid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
			}
			if "`exclusive'"=="" & "`link'"=="ki" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'==`targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
			}
			if "`exclusive'"=="" & "`link'"=="jm" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'==`sourceid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="" & "`link'"=="mj" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'==`targetid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="" & ("`link'"=="ik+jm" | "`link'"=="ik*jm") {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'==`sourceid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'==`sourceid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="" & ("`link'"=="ki+mj" | "`link'"=="ki*mj") {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'==`targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'==`targetid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="exclusive" & "`link'"=="ik" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'!=`targetid'
				keep if  `sourceidinf'==`sourceid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
			}
			if "`exclusive'"=="exclusive" & "`link'"=="ki" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'==`targetid'
				keep if  `sourceidinf'!=`sourceid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
			}
			if "`exclusive'"=="exclusive" & "`link'"=="jm" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'!=`targetid'
				keep if  `targetidinf'==`sourceid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="exclusive" & "`link'"=="mj" {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'==`targetid'
				keep if  `targetidinf'!=`sourceid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="exclusive" & ("`link'"=="ik+jm" | "`link'"=="ik*jm") {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'!=`targetid'
				keep if  `sourceidinf'==`sourceid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'!=`targetid'
				keep if  `targetidinf'==`sourceid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
			if "`exclusive'"=="exclusive" & ("`link'"=="ki+mj" | "`link'"=="ki*mj") {
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `sourceidinf'==`targetid'
				keep if  `sourceidinf'!=`sourceid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wsource_`m''"
				save "`Wsource_`m''", replace
				use "`holder'", replace
				replace `targetidinf'=`n'
				keep if  `targetidinf'==`targetid'
				keep if  `targetidinf'!=`sourceid'
				tempvar varholder
				ge `varholder'=`sourceid'
				drop `sourceid'
				ren `targetid' `sourceid'
				ren `varholder' `targetid'
				keep `time' `weightvar' `sourceidinf' `targetidinf' `sourceid' `targetid'
				append using "`Wtarget_`m''"
				save "`Wtarget_`m''", replace
			}
		}
	}
}


di _newline(2) "Combining spatial effect components for dyads involving `source_max' units of i in total - please wait."
forvalues m = 1(1)`source_max'{
	if `m'/10==int(`m'/10) {
		di "."
	}
	else {
		di "." _continue
	}
	quietly {
		use "`spatialvarundirdyad_`m''", replace
		if "`link'"=="ik" | "`link'"=="ki" {
			merge m:m `targetidinf' `sourceidinf' `sourceid' `time' using `Wsource_`m'', nogenerate
		}
		if "`link'"=="jm" | "`link'"=="mj" {
			merge m:m `targetidinf' `sourceidinf' `targetid' `time' using `Wtarget_`m'', nogenerate
		}
		if "`link'"=="ik+jm" | "`link'"=="ik*jm" | "`link'"=="ki+mj" | "`link'"=="ki*mj" {
			merge m:m `targetidinf' `sourceidinf' `sourceid' `time' using `Wsource_`m'', nogenerate
			tempvar Wsource
			ren `weightvar' `Wsource'
			merge m:m `targetidinf' `sourceidinf' `targetid' `time' using `Wtarget_`m'', nogenerate
			tempvar Wtarget
			ren `weightvar' `Wtarget'
			if "`link'"=="ik+jm" | "`link'"=="ki+mj" {
				replace `Wsource'=0 if `Wsource'==.
				replace `Wtarget'=0 if `Wtarget'==.
				gen `weightvar'=`Wsource'+`Wtarget'
			}
			if "`link'"=="ik*jm" | "`link'"=="ki*mj" {
				gen `weightvar'=`Wsource'*`Wtarget'
			}
		}
		drop if `sourceidinf'>`targetidinf'
		tempvar undirdyadcont
		replace `weightvar'=. if `spatialvar'==.
		ge `undirdyadcont'=`spatialvar'*`weightvar'
		tempvar allmissing
		gen `allmissing'=`undirdyadcont'
		collapse (sum) `undirdyadcont' `weightvar' (mean) `allmissing', by(`sourceidinf' `targetidinf' `time')
		replace `undirdyadcont'=. if `allmissing'==.
		if "`rowst'"=="norowst" {
			ge SE_var_undir_`rowst'= `undirdyadcont'
			replace SE_var_undir_`rowst'=. if `weightvar'==0
		}
		else {
			ge SE_var_undir_`rowst'= `undirdyadcont'/`weightvar'
		}
		keep `sourceidinf' `targetidinf' SE_var_undir_`rowst' `time'
		tempfile SE_file_undir_`rowst'_`m'
		save "`SE_file_undir_`rowst'_`m''", replace
	}
}
local appendnumber = `source_max'-1
di _newline(2) "Appending spatial effects for `appendnumber' times in total - please wait."
quietly use "`SE_file_undir_`rowst'_1'", replace
forvalues m = 2(1)`source_max'{
	quietly append using "`SE_file_undir_`rowst'_`m''"
	if `m'/10==int(`m'/10) {
		di "."
	}
	else {
		di "." _continue
	}
}
ren `sourceidinf' `sourceid'
ren `targetidinf' `targetid'
if "`sename'"=="" {
	local sename "SE_var_undir_`rowst'"
}
else {
	ren SE_var_undir_`rowst' `sename'
}
if "`filename'"=="" {
	local filename "SE_file_undir_`rowst'"
}
quietly save "`holder'", replace


quietly {
	use "`datasample'", replace
	merge m:m `sourceid' `targetid' `time' using `holder', nogenerate
	keep `i' `j' `time' `sename'
	collapse `sename', by(`i' `j' `time')
	if "`sourcetype'"=="int" | "`sourcetype'"=="long" | "`sourcetype'"=="float" | "`sourcetype'"=="double"  | "`sourcetype'"=="byte" {
		drop if `i'==.
	}
	else {
		drop if `i'==""
	}
	save `filename', replace
	restore
	preserve
	merge m:m `i' `j' `time' using `filename', keep(match) nogenerate
	keep `i' `j' `time' `sename'
	if "`labelname'"=="" {
		label var `sename' "Undirected dyad contagion spatial effect variable"
	}
	else {
		label var `sename' "`labelname'"
	}
	save `filename', replace
}

di _newline(2) "File {bf}`filename'{sf}"
di _newline(1) "  with variables {bf}`i'{sf}, {bf}`j'{sf}, {bf}`time'{sf}"
di _newline(1) "  and SE variable {bf}`sename'{sf} saved in current working directory"
if "`merge'"=="nomerge" {
	restore
}
else {
	restore
	capture drop _merge
	quietly merge m:m `i' `j' `time' using `filename', keep(match master)
	di _newline(2) "Spatial lag variable {bf}`sename'{sf} merged into the original dataset."
}

end