*! Program to:
*! 1) Generate spatial weights using point coordinates or key variables
*! 2) Import spatial weights from ArcGIS, GeoDa and from dta and text files // Although this is not memory-efficient, it may be useful for some researchers.
*! 3) Export spatial weights to .txt, .dat, .gwt, and Mata files // Exported .txt file can be used with sspack and spautoreg and exported .dat file can be used in R and Matlab 
*!
*! What -spwmatrix- can actually do depends on memory availability since sparse matrix operations are not yet allowed in Mata.
*!
*! Author: P. Wilner Jeanty
*! Version 2.8: February 2013 -----> Information on the dimension of the generated spatial weights matrix added to the report 
*! Version 2.6: October 3, 2012 ---> Modified so that eigenvalue variable can be saved to a file when either xtw() or noisland is specified.
*! Version 2.5: September 4, 2012 -> Option noisland added to remove observations with no neighbors
*! Version 2.4: July 3, 2012 ------> Option external added to save the weights matrix to Mata memory and option noreport also added to suppress report when necessary
*! Version 2.3: April 28, 2012 ----> Option xtw() added to generate spatial weights for a balanced panel dataset 
*! Version 2.2: December 2011 -----> Option xport() added to export spatial weights to dat, txt, and gwt format with one option, replacing option export
*! Version 2.1: November 2011 -----> Option dthres(), dmins(), snn(), and gamma() added when socio-econ spatial weights requested to cap the number of neighbors
*! Version 2.0.0: October 2011 ----> Option swm() added: Weights matrix generated by ArcGIS or stored as a dta file or a text file can now be imported
*! Version 1.0.7: May 2011 --------> Change default value for beta from 100 to 1.
*! Version 1.0.6: January 2011 ----> Bugs when econ spatial weights requested fixed
*! Version 1.0.5: February 2010 ---> Mata code compiled to a separate Mata file and option connect added
*! Version 1.0.4: August 2009 -----> Social network and socio-economic weights added
*! Version 1.0.3: July 2009  ------> Three syntaxes allowed and option to export weights to gwt files added
*! Version 1.0.2: June 2009 -------> Econ weights and option eignvar() added
*! version 1.0.1: December 2008 ---> Option to export weights to .dat file added 
*! Version 1.0.0: August 21, 2008 -> Born date

program define spwmatrix
	version 12
	* First perform some housekeeping in case the program started running but did not finish for some reasons
	capture mata: mata drop spwmatrix* spwmatrix*()
	capture scalar drop spwmatrix_spa spwmatrix_forst spwmatrix_rowst spwmatrix_RC
	capture macro drop SP_*

	* Now begin serious stuff
	global spwmatrix_soc=0
	gettoken afaire 0:0
	if "`afaire'"=="import" spwmatrix_gal `0'
	else if "`afaire'"=="gecon" spwmatrix_n1 `0'
	else if "`afaire'"=="socio" spwmatrix_socio `0'
	else {
		di as err "`afaire' is not a valid subcommand"
		exit 199
	}
	local using "`s(using)'"
	local wname "`s(wname)'"
	local knn "`s(knn)'"
	local wtype "`s(wtype)'"
	local alpha "`s(alpha)'"
	local eignval "`s(eignval)'" 
	local dband "`s(dband)'" 
	local mataf "`s(mataf)'"
	local rowstand "`s(rowstand)'" 
	local eignvar "`s(eignvar)'" 
	local connect "`s(connect)'"
	local dta "`s(dta)'"
	local text "`s(text)'"
	local swm "`s(swm)'"
	local xtw "`s(xtw)'"
	local external "`s(external)'"
	local report "`s(report)'"
	local island "`s(island)'"
	if "`using'"=="" {
		DisplayReport, wname(`wname') knn(`knn') wtype(`wtype') alpha(`alpha') eignval(`eignval') dband(`dband') ///
			  `mataf' `rowstand' eignvar(`eignvar') `connect' xtw(`xtw') `external' `report'
	}
	else {
		DisplayReport using `using', wname(`wname') knn(`knn') wtype(`wtype') alpha(`alpha') eignval(`eignval') ///
		dband(`dband') `mataf' `rowstand' eignvar(`eignvar') `connect' `dta' `text' swm(`swm') xtw(`xtw') ///
		`external' `report'
	}	
	if scalar(spwmatrix_donow)==0 {
		if "`external'"!="" {
			capture mata: rmexternal("`wname'")
			mata: *crexternal("`wname'")=spwmatrixE_W
		}	
		if "`eignvar'"!="" {
			if "`xtw'"!="" | "`island'"!="" {
				capture erase "`c(pwd)'`c(dirsep)'`eignvar'.dta" 		
				preserve
				clear
				mata: spwmatrix_eignget()
				qui save "`c(pwd)'`c(dirsep)'`eignvar'" 
				restore
			} 
		}
	}	
	* Cleaning up after everything went smoothly well
	capture scalar drop spwmatrix_spa 
	capture scalar drop spwmatrix_forst 
	capture scalar drop spwmatrix_rowst  
	capture scalar drop spwmatrix_donow
 	capture sreturn clear
	capture erase "`c(pwd)'`c(dirsep)'spwmatrix_saveig.out" 
	capture mata: mata drop island xtw spwmatrix* spwmatrix_*()
	capture mata: mata set matastrict off
	macro drop SP_*
end
program define spwmatrix_gal
	version 12
	syntax using/, WName(str) [EIGNVal(name) Mataf eignvar(str) ROWstand replace favor(str) ///
					dta text swm(str) CONnect xport(str) xtw(numlist max=1 >0) ///
					EXTernal noREPort noISland] 
	if "`favor'"!="" & !inlist("`favor'", "space", "speed") {
		di as err "Option favor(`favor') not allowed"
		exit 198
	}
	if "`dta'" !="" & "`text'"!="" & "`swm'"!="" {
		di as err "Options {bf:dta}, {bf:text}, and {bf:swm} may not be combined"
		exit 198
	}	
	if "`eignvar'"!="" & "`xtw'"=="" Confnewvar `eignvar' `replace' 
	if "`xtw'"!="" CHKint `xtw'
	CheckFile, wname(`wname') nametype(`xport') `mataf' eignval(`eignval') `replace' 
	preserve
	drop _all
	if "`dta'`text'`swm'"=="" qui insheet using `using', delim(" ")
	else if "`dta'"!="" qui use `using', clear
	else if "`text'"!="" qui insheet using `using', clear
	else if "`swm'"!="" {
		qui insheet using `"`using'"', names clear
		confirm numeric var `swm' 		
		capture drop oid_ field1	
		sort `swm'
	}
	unab xvars: _all
	SpaceSpd, favor(`favor')
	capture erase spwmatrix_tempf
	mata: spwmatrix_CalcSPweightM("`xvars'")
	restore
	if "`xtw'`island'"=="" {
		if "`eignvar'"!="" & scalar(spwmatrix_rowst)==0 {
			if "`eignval'"!="" & "`mataf'"!="" mata: spwmatrix_getfile()
			else {
				mata: spwmatrix_getfile()
				capture erase spwmatrix_tempf
			}
		}
	}	
	if "$SP_fformat"=="txt" SP_fixit using "$outname", id(`swm') // where an id variable is required that variable will be used
	RetOption "`using'" "`wname'" "`knn'" "`wtype'" "`alpha'" "`eignval'" "`dband'" "`mataf'" "`rowstand'" ///
	 "`eignvar'" "`connect'" "`dta'" "`text'" "`swm'" "`xtw'" "`external'" "`report'" // Quotes here are crucial to determine position of arguments
end
program define spwmatrix_n1
		version 12
		syntax varlist(numeric min=2 max=2) [if] [in], WName(str) ///
		 	[WType(str) alpha(str) DBand(numlist min=2 max=2 sort) cart ///
		 	r(str) knn(str) ECONVar(varlist numeric min=1 max=1) beta(str) ///
			EIGNVal(name) eignvar(str) Mataf ROWstand replace favor(str) ///
			CONnect xport(str) xtw(numlist max=1 >0) EXTernal noREPort noISland] 
		marksample touse
		CheckFile, wname(`wname') nametype(`xport') `mataf' eignval(`eignval') `replace'
		if "`eignvar'"!="" & "`xtw'"=="" Confnewvar `eignvar' `replace'
		local xvars `varlist'
		if "`r'"!="" & "`cart'"!="" {
			di as err " Options {bf:cart} and {bf:r()} may not be combined"
			exit 198
		}
		if "`favor'"!="" & !inlist("`favor'", "space", "speed") {
			di as err " Option favor(`favor') not allowed"
			exit 198
		}
	* Checking the coordinates
		gettoken lat1 long1: xvars
		if "`cart'"=="" {
			quietly count if abs(`lat1')>90 
			if `r(N)'>0  { 
				di as err "Spherical latitudes must be in [-90,90]"
				exit 198
			}
			quiet count if abs(`long1')>180 
			if `r(N)'>0  { 
				di as err "Spherical longitudes must be in [-180,180]"
				exit 198
			}			
			if "`r'"=="" local rad= 6371.009 /* Calculcated as rad=(2*a+b)/3
 			with a=6,356.752 km (Polar radius) b=6,378.137 km (Equatorial radius) */
			else local rad=`r'		
		}
		else if `lat1'<0 | `long1'<0 {
			di
			di as err " Cartesian or projected coordinates must be positive"
			exit 198
		}
		if "`wtype'"!="" & !inlist(`"`wtype'"', "bin", "inv", "econ", "invecon", "socnet", "socecon") {
			di
			di as err " One of bin, inv, econ, invecon, socnet, and socecon is required with option {bf:wtype()}"
			exit 198		
		}
		if `"`wtype'"'=="bin" & `"`dband'"'=="" {
			di
			di as err "Option {bf:dband()} required when binary distance-based weights matrix requested"
			e 198
			// N.B. Although not recommended, distance cut-off is not required for inverse distance weights matrix
		}
		if "`knn'"!="" & ("`wtype'"!="" | "`dband'"!="") {
			di
			di as err " Option {bf:knn(#)} may not be combined with either {bf:wtype()} or {bf:dband()}"
			exit 198
		}
		if "`knn'`wtype'"=="" {
			di
			di as err " You must specify either {bf:knn(#)} or {bf:wtype()} option"
			exit 198
		}	
		if "`knn'"!="" CHKint `knn' 
		if "`xtw'"!="" CHKint `xtw' 
		if ("`alpha'"!="" & "`wtype'"!="inv") {
			di
			di as err "Option {bf:alpha()} can only be specified when inverse distance matrix requested"
			exit 198
		}
		if inlist("`wtype'", "econ", "invecon") & "`econvar'"=="" {
			di
			di as err "You must specify at least option {bf:econvar()} required when requesting economic or inverse economic distance spatial weights" 
			exit 198
		}
		if inlist("`wtype'", "econ", "invecon")==0 & ("`econvar'"!="" | "`beta'"!="") {
			di 
			di as err "Option {bf:econvar()} or {bf:beta()} may not be specified when option {bf:wtype(econ|invecon)} is not specified" 
			exit 198
		}
		if  "`wtype'"=="inv" {
			if "`alpha'"=="" local alpha=1  // alpha(1) is the default when wtype(inv) is specified
			mata: spwmatrix_Alfa=`alpha' 
		}
		if  "`wtype'"=="econ" | "`wtype'"=="invecon" {
			if "`dband'"!="" {
				di as err " Option {bf:dband()} may not be combined with {bf:wtype(econ|invecon)}"
				exit 198
			}
			markout `touse' `econvar'  
			confirm numeric var `econvar'
			if "`beta'"!="" CHKint `beta' 
			if "`beta'"=="" local beta=1  // beta=1 is the default when wtype(econ|invecon) is specified
			mata: spwmatrix_beta=`beta' 
		}
		if "`dband'"!="" {
			gettoken cut1 cut2: dband
			global cut1 `cut1'
			global cut2 `cut2'
			mata: spwmatrix_clow=`cut1'; spwmatrix_chigh=`cut2'
		}
		mata: spwmatrix_soc=$spwmatrix_soc
		SpaceSpd, favor(`favor')
		mata: spwmatrix_CalcSPweightM("`xvars'", "`touse'")	
		if "$SP_fformat"=="txt" SP_fixit using "$outname"
		
		RetOption "`using'" "`wname'" "`knn'" "`wtype'" "`alpha'" "`eignval'" "`dband'" ///
				  "`mataf'" "`rowstand'" "`eignvar'" "`connect'" "`dta'" "`text'" "`swm'" ///
				  "`xtw'"  "`external'" "`report'" "`island'"
end
program define spwmatrix_socio
		version 12
		syntax varlist(numeric min=1 max=1) [if] [in], WName(str) WType(str) ///
				[idvar(varname numeric) EIGNVal(name) eignvar(name) Mataf ///
				ROWstand  replace favor(str) CONnect DTHres(numlist max=1 >0) ///
				Gamma(numlist max=1 >0) snn(numlist max=1 >0) dmins(str) xport(str) ///
				xtw(numlist max=1 >0) EXTernal noREPort noISland]

		marksample touse
		if "`wtype'"!="" & !inlist(`"`wtype'"', "socnet", "socecon") {
			di
			di as err " One of socnet and socecon is required with option {bf:wtype()}"
			exit 198		
		}
		if "`wtype'"=="socnet" & "`idvar'"=="" {
			di
			di as err " Option {bf:idvar()} required when requesting social networks spatial weights"
			exit 198		
		}
		if "`wtype'"=="socecon" & "`idvar'"!="" {
			di
			di as err " Option {bf:idvar()} may not be specified when requesting socioeconomics spatial weights"
			exit 198		
		}
		if ("`dthres'"!="" | "`snn'"!="" | "`dmins'"!="" | "`gamma'"!="") & "`wtype'"!="socecon" {
			di "{err}Options {bf:dthres()}, {bf:dmins()}, {bf:gamma()}, and {bf:snn()} may be specified " _c
			di "only when socio-economic spatial weights are requested"
			exit 198
		}
		if "`snn'"!="" CHKint `snn'  // Need to note in the help file that snn() can be combined with wtype() while knn() cannot.
		local socops=("`dthres'"!="") + ("`snn'"!="") + ("`dmins'"!="") 
		if `socops'>1 {
			di as err "Only one of {bf:dthres()}, {bf:dmins()} and {bf:snn()} is allowed"
			exit 198 // The purpose of dmins is to help in specifying dthres()
		}
		if "`gamma'"!="" & "`dthres'"=="" {
			di as err "Option {bf:gamma()} must be combined with {bf:dthres()} only"
			exit 198 
		}
		local ksoc=0
		local dsoc=0
		if "`dthres'"!="" {
			local dsoc=`dthres'
			if "`gamma'"=="" local gamma=1  // gamma(1) is the default when wtype(socecon) is specified
			else {
				capture confirm integer number `gamma'
				if _rc {
					di as err "Value supplied for {bf:gamma()} must be an integer"
					exit 198
				}
			}
			mata: spwmatrix_gamma=`gamma' 			
		}
		if "`snn'"!="" local ksoc=`snn'
		mata: spwmatrix_d=`dsoc'; spwmatrix_snn=`ksoc'
		if "`favor'"!="" & !inlist("`favor'", "space", "speed") {
			di as err " Option favor(`favor') not allowed"
			exit 198
		}
		if "`idvar'"!="" markout `touse' `idvar' 			
		CheckFile, wname(`wname') nametype(`xport') `mataf' eignval(`eignval') `replace' 
		if "`eignvar'"!="" & "`xtw'"=="" Confnewvar `eignvar' `replace'
		global spwmatrix_soc=1
		mata: spwmatrix_soc=$spwmatrix_soc
		SpaceSpd, favor(`favor')
		mata: spwmatrix_CalcSPweightM("`varlist'", "`touse'")
		if "$SP_fformat"=="txt" SP_fixit using "$outname", id(`idvar')
		RetOption "`using'" "`wname'" "`knn'" "`wtype'" "`alpha'" "`eignval'" "`dband'" ///
				  "`mataf'" "`rowstand'" "`eignvar'" "`connect'" "`dta'" "`text'" "`swm'" ///
				  "`xtw'" "`external'" "`report'" "`island'"
end
program define DisplayReport
	version 12
	syntax [using/], wname(str) [knn(str) wtype(str) alpha(str) eignval(name) ///
		dband(numlist min=2 max=2 >=0 sort) mataf rowstand eignvar(name) ///
		connect dta text swm(str) xtw(str) external noREPort]
	local spwmatrix_N=scalar(spwmatrix_RC)	
	if scalar(spwmatrix_donow) exit 
	local impfile GAL
	if "`dta'" != "" local impfile DTA
	if "`text'"!="" local impfile TEXT
	if "`swm'"!="" local impfile SWM
	if `"`using'"'!="" local titl `impfile' file `using' imported successfully as a (`spwmatrix_N' x `spwmatrix_N') SPW matrix and the following action(s) taken:
	else {
		if "`knn'"!="" local matype Nearest neighbor (knn = `knn')
		else if "`wtype'"!="" {
 			if "`wtype'"=="bin" local matype Binary distance
			else if "`wtype'"=="inv" local matype Inverse distance (alpha = `alpha')
			else if "`wtype'"=="econ" local matype Economic distance
			else if "`wtype'"=="invecon" local matype Inverse economic distance
			else if "`wtype'"=="socnet" local matype Social network
			else if "`wtype'"=="socecon" local matype Socio-economic
		}
		local titl `matype' spatial weights matrix (`spwmatrix_N' x `spwmatrix_N') calculated successfully and the following action(s) taken:
	}
	mata: st_local("spisland", strofreal(island)); st_local("spxtw", strofreal(xtw))
	if "`report'"=="" {
		di
		di as txt `"{bf:`titl'}"' 
		local foreign1 ""
		local foreign2 ""
		if "`eignval'"!="" & scalar(spwmatrix_rowst)==0 {
			local foreign1 and its eigenvalues
			if "`mataf'"=="" local foreign2 as txt " and " in ye "`eignval'" 
			else if "`mataf'"!="" local foreign2 as txt " and " in ye "`c(pwd)'`c(dirsep)'`eignval'" 
			if scalar(spwmatrix_forst)!=0 & "`mataf'"=="" local foreign3 as txt " and " in ye "`c(pwd)'`c(dirsep)'`eignval'_n" 	
		}
		if scalar(spwmatrix_forst)==0 {
			if "`mataf'"=="" {
				if ("`using'"!="") local ROW="SWMDist No "
				else local ROW="SWMDist Yes "
				if "`rowstand'"!="" local ROW="`ROW'Yes"
				else local ROW="`ROW'No"
				matrix rownames `wname'=`ROW'
				if "`dband'"!="" {
					local int1=int($cut1)
					local int2=$cut1-`int1'
					local int2=string(`int2')
					local col "`int1' `int2'"
					local int=int($cut2)
					local int2=$cut2-`int1'
					local int2=string(`int2')
					local col "`col' `int1' `int2'"
					matrix colnames `wname'=`col'
				}
				di
				di as txt " - Spatial weights matrix `foreign1' created as Stata object(s): " in ye "`wname'" `foreign2' as txt "."
			}
			else {
				di 
				di " - Spatial weights matrix `foreign1' saved to Mata file(s): " in ye "`c(pwd)'`c(dirsep)'`wname'" `foreign2' as txt "."
			}
		}
		if scalar(spwmatrix_forst)!=0 {
			if "`mataf'"=="" {
				di
				di as txt " - Spatial weights matrix `foreign1' saved to Mata file(s): " in ye "`c(pwd)'`c(dirsep)'`wname'_n" `foreign3' as txt "," 
				di as txt "   since dimensions exceed {help matsize} limit of your Stata flavor."
			}
			if "`mataf'"!="" {
				di 
				di " - Spatial weights matrix `foreign1' saved to Mata file(s): " in ye "`c(pwd)'`c(dirsep)'`wname'" `foreign2' as txt "."
			}
		}
		if "`rowstand'"!="" & scalar(spwmatrix_rowst)==0 {
			di
			di as txt " - Spatial weights matrix has been row-standardized."
		}
		if "$SP_Fname"!="" {
			di
			di as txt " - Spatial weights matrix saved to .$SP_fformat file, " in ye "`c(pwd)'`c(dirsep)'$outname" as txt ", $SP_use"
		}
		if "`eignvar'"!="" & scalar(spwmatrix_rowst)==0  {
			di	
			if (`spxtw' + `spisland')>=1 {
				di as txt " - Eigenvalue variable saved the .dta file " in ye "`c(pwd)'`c(dirsep)'`eignvar'" as txt "."
			}	
			else di as txt " - Eigenvalues placed into the variable " in ye "`eignvar'" as txt "."
		}
		if "`external'"!="" {
			di
			di as txt " - Spatial weights matrix stored as a Mata object residing in Mata memory."
		}	
	}	
	if "`connect'"!="" {	
		mata: spwmatrix_Connect()
		di 
		di as txt "{bf:Connectivity Information for the Spatial Weights Matrix}"
		di as txt "  - Sparseness: " as res %5.3f scalar(spwmatrix_spa) as txt "%"		
		di as txt "  - Neighbors: "
		di as txt "  	 Min   : " as res "`min'"
		di as txt "  	 Mean  : " as res "`mean'"
		di as txt "  	 Median: " as res "`med'"
		di as txt "  	 Max   : " as res "`max'"
	}
end
program define RetOption, sclass
	version 12
	args using wname knn wtype alpha eignval dband mataf rowstand eignvar connect dta text swm xtw external report island
	
	if "`using'" != "" sreturn local using "`using'"
	if "`wname'" != "" sreturn local wname "`wname'" 	
	if "`knn'" != "" sreturn local knn "`knn'" 
	if "`wtype'" != "" sreturn local wtype "`wtype'" 
	if "`alpha'" !="" sreturn local alpha "`alpha'"
	if "`eignval'" != "" sreturn local eignval "`eignval'" 
	if "`dband'" != "" sreturn local dband "`dband'"
	if "`mataf'" != "" sreturn local mataf "`mataf'"
	if "`rowstand'" != "" sreturn local rowstand "`rowstand'" 
	if "`eignvar'" != "" sreturn local eignvar "`eignvar'" 
	if "`connect'" != "" sreturn local connect "`connect'"
	if "`dta'" != "" sreturn local dta "`dta'"
	if "`text'" != "" sreturn local text "`text'"
	if "`swm'" != "" sreturn local swm "`swm'"
	if "`xtw'" != "" sreturn local xtw "`xtw'"
	if "`external'" != "" sreturn local external "`external'"
	if "`report'" != "" sreturn local report "`report'"
	if "`island'"!="" sreturn local island "`island'"
end
program CheckFile
	version 12
	syntax, wname(str) [nametype(str) mataf eignval(name) replace] 
	if "`nametype'"!="" {	
		tokenize "`nametype'", parse(",")
		args fname sepopt ftype
		if "`sepopt'"!="," {
			di "{err}Comma required when {bf:xport()} is specified"
			exit
		}	
		local fformat `ftype'
		if !inlist("`fformat'", "dat", "txt", "gwt") {
			di as err "Option {bf:xport()} takes one of {bf:dat}, {bf:txt}, and {bf:gwt} as sub-option"
			exit 198
		}
		if "`fformat'"=="dat" {
			global outname `fname'.dat
			global SP_use "for use in {bf:Matlab} and {bf:R}."
		}	
		if "`fformat'"=="txt" {
			global outname `fname'.txt
			global SP_use "for use with other {bf:Stata} packages."
		}	
		if "`fformat'"=="gwt" {
			global outname `fname'.gwt
			global SP_use "for use in GeoDa or other software packages."
		}		
		Confnewfile $outname `replace'
		mata: spwmatrix_forout="`fformat'"
		global SP_fformat `fformat' 
		global SP_Fname `fname'
	}	
	if "`mataf'"!="" {
		Confnewfile `wname' `replace'
		if "`eignval'"!="" Confnewfile `eignval' `replace'		
	}
end
prog define Confnewfile
	version 12
	args filename replace
	local conf_file confirm new file
	global spwmatrix_repf=0
	cap `conf_file' `filename'
	if _rc {
		if "`replace'"!="" {
			erase `filename'
			global spwmatrix_repf=1	
		}	
		else {
			di              
			`conf_file' `filename'
		}
	}
	else {
		if "`replace'"!="" {
			di
			di "(Note: file `filename' not found)"
			di
		}	
	}	
end
prog define Confnewvar
	version 12
	args varname replace
	cap confirm new var `varname'
	if _rc & "`replace'"!="" drop `varname'
	else if _rc & "`replace'"=="" confirm new var `varname'
end
prog define SpaceSpd
	version 12
	syntax, [favor(str)]
	if "`favor'"=="" & c(matafavor)=="space" mata: mata set matafavor speed
	if "`favor'"=="speed" & c(matafavor)=="space" mata: mata set matafavor speed
	if "`favor'"=="space" & c(matafavor)=="speed" mata: mata set matafavor space
end
prog define SP_fixit
	version 12
	syntax using/, [id(varname numeric)]
	tempfile sav_id	
	preserve
 	qui {
		if "`id'"!="" gen idv=`id' // This assumes `id' takes on values ranging from 1 to _N
		else gen idv=_n 
		keep idv
		save `sav_id'
		insheet using `using', clear
		merge using `sav_id' 
		local n=_N+1
		set obs `n'
		gen nid=.
		replace nid=0 if _n==_N
		replace nid=idv if nid!=0 // Assuming `id' takes on values from 1 to _N
		sort nid
		order nid
		drop idv _merge
		local N=_N-1
		replace nid=`N' if nid==0
		outsheet using `using', delim(" ") nonames replace
	}	
	restore
end
prog define CHKint
	version 12
	args chkint 
	if "`chkint'"!="" { 
		capture confirm integer number `chkint'
           if _rc {
			di as err "Value supplied for option {bf:`chkint'()} must be an integer"
               exit 198
          }
	}
end